Системные функции UNIX-подобных систем в PHP

Php

Автор: Eugen

1 нояб. 2011 г., 10:08:47  3814


Доброго времени суток!
Сегодня я расскажу про системные функции UNIX-подобных систем в PHP.
Для начала, под UNIX-подобными системами подразумеваются различные Linux'ы, BSD и т.п. Всё описанное здесь НЕ БУДЕТ работать в ОС Windows. Хотя, находились и извращенцы, которые PHP компилировали в cygwin, но это уже отдельный вопрос :-)

Итак, начнём.
С помощью данных функций можно управлять процессами - ветвить, посылать им различные сигналы. Причём, посылать сигналы процессу можно и из другого процесса, зная только его PID. Дла проверок нам понадобится:
- Сервер под управлением UNIX-подобной с истемы с установленным PHP и модулями pcntl и posix
- SSH доступ к серверу и SSH клиент (или по старинке - сервер под ногами, монитор и клавиатура на столе)
Однако, SSH или физический доступ к серверу нужен только для наглядности примеров.
Сразу оговорюсь, что крайне не желательно использовать функции для управления процессами в скриптах, запускаемых через вебсервер. В некоторых случаях, эти функции и не доступны, в некоторых случаях, они будут работать не так, как это ожидается, а в некоторых случаях, возможен даже крах вебсервера. Если же нужно запускать скрипт через вебсервер - используйте функцию shell_exec() для запуска скрипта.
Для начала, приведу полный список сигналов. Для чего - далее поймёте.

Название  Действие по умолчанию      Описание                                                 Тип
SIGABRT   Завершение с дампом памяти Сигнал посылаемый функцией abort()                       Управление
SIGALRM   Завершение                 Сигнал истечения времени заданного alarm()               Уведомление
SIGBUS    Завершение с дампом памяти Неправильное обращение в физическую память               Исключение
SIGCHLD   Игнорируется               Дочерний процесс завершен или остановлен                 Уведомление
SIGCONT   Продолжить выполнение      Продолжить выполнение ранее остановленного процесса      Управление
SIGFPE    Завершение с дампом памяти Ошибочная арифметическая операция                        Исключение
SIGHUP    Завершение                 Закрытие терминала                                       Уведомление
SIGILL    Завершение с дампом памяти Недопустимая инструкция процессора                       Исключение
SIGINT    Завершение                 Сигнал прерывания (Ctrl-C) с терминала                   Управление
SIGKILL   Завершение                 Безусловное завершение                                   Управление
SIGPIPE   Завершение                 Запись в разорванное соедиение (пайп, сокет)             Уведомление
SIGQUIT   Завершение с дампом памяти Сигнал «Quit» с терминала                                Управление
SIGSEGV   Завершение с дампом памяти Нарушение при обращении в память                         Исключение
SIGSTOP   Остановка процесса         Остановка выполнения процесса                            Управление
SIGTERM   Завершение                 Сигнал завершения (сигнал по умолчанию для утилиты kill) Управление
SIGTSTP   Остановка процесса         Сигнал остановки с терминала (Ctrl-Z)                    Управление
SIGTTIN   Остановка процесса         Попытка чтения с терминала фоновым процессом             Управление
SIGTTOU   Остановка процесса         Попытка записи на терминал фоновым процессом             Управление
SIGUSR1   Завершение                 Пользовательский сигнал № 1                              Пользовательский
SIGUSR2   Завершение                 Пользовательский сигнал № 2                              Пользовательский
SIGPOLL   Завершение                 Событие отслеживаемое poll()                             Уведомление
SIGPROF   Завершение                 Истечение таймера профилирования                         Отладка
SIGSYS    Завершение с дампом памяти Неправильный системный вызов                             Исключение
SIGTRAP   Завершение с дампом памяти Ловушка трассировки или брейкпоинт                       Отладка
SIGURG    Игнорируется               На сокете получены срочные данные                        Уведомление
SIGVTALRM Завершение                 Истечение «виртуального таймера»                         Уведомление
SIGXCPU   Завершение с дампом памяти Процесс превысил лимит процессорного времени             Исключение
SIGXFSZ   Завершение с дампом памяти Процесс превысил допустимый размер файла                 Исключение



В PHP определены константы для каждого из сигналов.
Описание функций я начну с простых POSIX функций, не служащих для управления процессами:

int posix_get_last_error() - Функция возвращает номер ошибки, который вернула предыдущая posix-функция. Параметры не требуются.
str posix_strerror(int errno) - Функция возвращает текстовое описание ошибки по её номеру.
Примеров к этим двум функциям не привожу.
Оговорюсь, что приведенные примеры не являются пособием по использованию функций, а только лишь наглядно показывают их работу. Следует не забывать, что любой системный вызов может завершиться ошибкой, к которой ваш скрипт должен быть готов (хотя, в нормальных условиях, эти ошибки не должны возникать).

arr posix_uname() - Функция возвращает ассоциативный массив с информацией о системе.
Пример:

<?php
print_r(posix_uname());
?>
Результат выполнения:
Array
(
    [sysname] => FreeBSD
    [nodename] => tem.dp.ua
    [release] => 6.3-RELEASE
    [version] => FreeBSD 6.3-RELEASE #4: Thu Mar 12 22:29:03 EET 2009     [email protected]:/src/sys/i386/compile/TEM
    [machine] => i386
)


sysname - название системы
nodename - имя системы
release - сборка системы
version - версия системы
machine - архитектура системы
domainname - DNS имя машины
Так же, возможно появление параметра domainname (доменное имя машины), однако, это доступно только в GNU системах или при использовании GNU libc.

str posix_getcwd() - Функция возвращает текущий рабочий каталог. Аналогом функции является функция getcwd(), доступная во всех ОС, в отличии от данной.

Пример:
<?php
echo 'Сurrent working directory is '.posix_getcwd();
?>
Результат выполнения:
Сurrent working directory is /root



int posix_getuid() - Функция возвращает UID пользователя, который запустил процесс. Значение может на совпадать с т.н. "эффективным" UID. Аналог функции - getmyuid().
Пример:

<?php
echo 'My UID: '.posix_getuid();
?>
Результаты:
My UID: 0 - от имени root
My UID: 84 - от имени eugen



int posix_geteuid() - Функция возвращает UID пользователя, от имени которого и с правами процесс реально работает. Чаще всего, EUID совпадает с UID.
Пример не привожу, т.к. он аналогичен предыдущему.

int posix_getgid() - Функция возвращает GID группы пользователя, запустившего процесс. Значение может не совпадать с т.н. "эффективным" GID. Аналог функции - getmygid().
Пример не привожу.

int posix_getegid() - Функция возвращает GID группы, с правами которой реально работает процесс. Чаще всего, значение EGID будет совпадать с GID.
Пример не привожу.

bool posix_setgid(int gid) - Функция устанавливает GID для текущего процесса. Однако, следует заметить, что использовать эту функцию, как правило, можно только с правами суперпользователя. В случае, если скрипт работает с правами суперпользователя, функция так же, изменяет и EGID.
Пример:

<?php
echo "GID/EGID: ".posix_getgid()."/".posix_getegid()."\n";
posix_setgid(80);
echo "GID/EGID: ".posix_getgid()."/".posix_getegid()."\n";
?>
Результат:
GID/EGID: 0/0
GID/EGID: 80/80



bool posix_setegid(int gid) - Функция устанавливает EGID для текущего процесса. Однако, следует заметить, что использовать эту функцию можно только с правами суперпользователя. В случае, если скрипт работает с правами суперпользователя, функция НЕ изменяет и GID процесса. Чтобы изменить GID и EGID, необходимо сначала выполнить posix_setgid, а потом posix_setegid.
Пример:

<?php
echo "GID/EGID: ".posix_getgid()."/".posix_getegid()."\n";
posix_setegid(80);
echo "GID/EGID: ".posix_getgid()."/".posix_getegid()."\n";
?>
Результат:
GID/EGID: 0/0
GID/EGID: 0/80



bool posix_setuid(int uid) - Функция устанавливает UID для текущего процесса. Для выполнения, как правило, требует привилегий суперпользователя. Аналогично posix_setgid, изменяет и EUID, в случае работы с правами суперпользователя.
Пример:

<?php
echo "UID/EUID: ".posix_getuid()."/".posix_geteuid()."\n";
posix_setuid(80);
echo "UID/EUID: ".posix_getuid()."/".posix_geteuid()."\n";
?>
Результат:
UID/EUID: 0/0
UID/EUID: 80/80



bool posix_seteuid(int uid) - Функция устанавливает EUID для текущего процесса. Для выполнения, как правило, требует привилегий суперпользователя. Аналогично posix_setegid, НЕ изменяет EUID, в случае работы с правами суперпользователя.
Пример:

<?php
echo "UID/EUID: ".posix_getuid()."/".posix_geteuid()."\n";
posix_seteuid(80);
echo "UID/EUID: ".posix_getuid()."/".posix_geteuid()."\n";
?>
Результат:
UID/EUID: 0/0
UID/EUID: 0/80


Примечание:
Если необходимо изменить группу и владельца процесса, функции нужно запускать в таком порядке: posix_setgid, posix_setegid, posix_setuid, posix_seteuid. В противном случае, теряются права суперпользователя и некоторые функции могут не сработать.

str posix_getlogin() - Функция возвращает имя пользователя, который запустил процесс, независимо от последующих вызовов posix_setuid и posix_seteuid.
Пример:

<?php
echo "Login: ".posix_getlogin()."\n";
echo "UID/EUID: ".posix_getuid()."/".posix_geteuid()."\n";
posix_setuid(80);
posix_seteuid(80);
echo "UID/EUID: ".posix_getuid()."/".posix_geteuid()."\n";
echo "Login: ".posix_getlogin()."\n";
?>
Результат:
Login: root
UID/EUID: 0/0
UID/EUID: 80/80
Login: root



arr posix_getgrgid(int gid) - Функция возвращает массив с информацией о группе.
Пример:

<?php
print_r(posix_getgrgid(0));
?>
Результат:
Array
(
    [name] => wheel
    [passwd] => *
    [members] => Array
        (
            [0] => root
        )

    [gid] => 0
)



arr posix_getgrnam(str group) - Функция возвращает массив с информацией о группе. Только, в отличии от предыдущей функции, нужно знать имя группы, вместо GID.
Пример:

<?php
print_r(posix_getgrnam('wheel'));
?>


Результат выполнения будет таким же, как и у предыдущей функции.

arr posix_getpwuid(int uid) - Функция возвращает массив с информацией о пользователе.
Пример:

<?php
print_r(posix_getpwuid(84));
?>
Результат:
Array
(
    [name] => eugen
    [passwd] => $1$************************
    [uid] => 84
    [gid] => 80
    [gecos] => Just a Eugen
    [dir] => /home/eugen
    [shell] => /bin/sh
)


Примечание:
name - имя пользователя
passwd - хэш пароля пользователя при запуске с правами суперпользователя, либо звёздочка (или другой знак) при запуске с правами обычного пользователя
uid - UID пользователя
gid - GID пользователя
gecos - текстовое описание пользователя
dir - домашний каталог пользователя
shell - Shell-интерпретатор пользователя

arr posix_getpwnam() - Функция возвращает массив с информацией о пользователе. Но, в отличии от предыдущей функции, нужно указать имя пользователя, вместо UID.
Пример:

<?php
print_r(posix_getpwnam('eugen'));
?>


Результат выполнения будет таким же, как и у предыдущей функции.

arr posix_getrlimit() - Функция возвращает различные системные лимиты (мягкие и жесткие).

core        Максимальный размер core-файлов. 0 - файлы не создаются. Если файл будет больше указанного размера, он обрезается до указанного.
totalmem    Максимальный объём памяти, выделяемый под процесс.
virtualmem    Максимальный размер виртуальной памяти (SWAP), выделяемой под процесс.
data        Максимальный размер сегмента данных процесса.
stack        Максимальный размер стека процесса.
rss        Максимальное ограничение (в страницах) для rss (числа виртуальных страниц в памяти).
maxproc        Максимальное количество процессов, которые может запустить владелец данного процесса.
memlock        Максимальное количество байт, которые могут быть заблокированы в памяти.
cpu        Ограничение процессорного времени.
filesize    Максимальный размер файла для текущей файловой системы.
openfiles    Максимальное количество открытых файловых дескрипторов.


Пример:

<?php
print_r(posix_getrlimit());
?>
Результат:
Array
(
    [soft core] => unlimited
    [hard core] => unlimited
    [soft data] => 536870912
    [hard data] => 536870912
    [soft stack] => 67108864
    [hard stack] => 67108864
    [soft virtualmem] => unlimited
    [hard virtualmem] => unlimited
    [soft totalmem] => unlimited
    [hard totalmem] => unlimited
    [soft rss] => unlimited
    [hard rss] => unlimited
    [soft maxproc] => 5547
    [hard maxproc] => 5547
    [soft memlock] => unlimited
    [hard memlock] => unlimited
    [soft cpu] => unlimited
    [hard cpu] => unlimited
    [soft filesize] => unlimited
    [hard filesize] => unlimited
    [soft openfiles] => 11095
    [hard openfiles] => 11095
)



Далее опишу некоторые полезные функции для управления процессами. Данные примеры советую запускать из командной строки...

int posix_getpid() - Функция определяет PID (идентификатор) текущего процесса. Аналог функции - getmypid().
Позже, можно будет посылать сигналы процессу, зная его PID.
Пример:

<?php
echo 'My PID is '.posix_getpid();
?>
Результат:
My PID is 28088



int posix_getppid() - Функция возвращает PID родительского процесса. Для чего это нужно, поймёте позже...
Пример не привожу, т.к. он аналогичен предыдущему.

bool posix_kill(int pid, int sig) - Функция посылает сигнал процессу. Константы сигналов можно найти в таблице в начале статьи.
Пример:

<?php
$pid = posix_getpid();
echo "My PID is ".$pid."\n";
echo "Sending SIGTERM to myself...\n";
posix_kill($pid,SIGTERM);
echo "Bla-bla-bla.\n";
?>
Результат:
My PID is 28144
Sending SIGTERM to myself...


Как видим, скрипт завершился и последний вызов echo не сработал. Выполнение скрипта прервалось. Естественно, зная PID, можно обмениваться сигналами с любыми принадлежащими вам процессами (либо со всеми процессами, если скрипт запускается с правами суперпользователя)

С модулем POSIX, надеюсь, более-менее познакомились. Далее, я расскажу о функциях модуля pcntl и покажу несколько хороших примеров.
Поведение функций PCNTL изменилось, начиная с PHP 5.3.0. На мой взгляд, старое поведение этих функций гораздо удобнее, но в статье я опишу оба варианта использования функций.

bool pcntl_signal(int signo, callback handler [, bool restart_syscalls=true]) - Функция связывает заданный сигнал с функцией, которая будет запускаться при поступлении заданного сигнала. В качестве единственного аргумента, в функцию будет передаваться идентификатор сигнала. Таким образом, можно делать одну общую функцию-обработчик для всех сигналов. Параметр restart_syscalls определяет, должен ли использоваться перезапуск системных вызовов; он нам не понадобится. Для работы, необходимо задать ticks: declare(ticks = 1); либо, для PHP>=5.3.0, вызывать функцию pcntl_signal_dispatch(). Причём, до вызова pcntl_signal_dispatch() поступившие сигналы не обработаются. Поэтому, можно вызывать эту функцию в цикле, а можно использовать ticks для запуска:

declare(ticks = 1);
register_tick_function('pcntl_signal_dispatch');


Собственно, в качестве примера, приведу универсальный вариант:

<?php
function signal_handler($sig) {
    // Как и в любой функции, здесь можно использовать глобальные переменные.
    switch($sig) {
        case SIGTERM:
        echo "SIGTERM received. Exitting...\n";
        // Здесь можно выполнять различные действия перед завершением скрипта. Например, записать соотв. строку в лог, и т.п.
        exit(1); // Завершаемся с кодом, большим 0
        break;
    case SIGINT:
        echo "SIGINT received. Exitting...\n";
            // Здесь можно выполнять различные действия перед завершением скрипта (в случае прерывания по Ctrl+C).
        exit(2); // Завершение с другим кодом
        break;
    }
}
declare(ticks = 1);
if(function_exists('pcntl_signal_dispatch')) // Проверяем наличие функции pcntl_signal_dispatch
    register_tick_function('pcntl_signal_dispatch'); // И регистрируем её как tick-функцию для запуска при выполнении каждой команды
pcntl_signal(SIGTERM,'signal_handler'); // Устанавливаем обработчик для сигнала SIGTERM.
pcntl_signal(SIGINT,'signal_handler'); // Устанавливаем обработчик для сигнала SIGINT.
while(true) // Вводим скрипт в бесконечный цикл
    usleep(10);
?>


Запускаем скрипт в командной строке и прерываем его по Ctrl+C и видим примерно такое:

tem# php 1.php
^CSIGINT received. Exitting...
tem#


Видим, что скрипт поймал сигнал SIGINT. Точно так же он себя поведёт при SIGTERM.
Забыл сказать, что сигнал безусловного завершения SIGKILL отловить невозможно. На то оно и безусловное завершение.

Функции pcntl_getpriority и pcntl_setpriority служат для определения и изменения приоритета процессов, соответственно. В данной статье я не буду описывать эти функции, т.к., как правило, эти функции не используются.

int pcntl_alarm(int time) - Функция посылает текущему процессу сигнал SIGALRM через заданное количество секунд. При необходимости, можно вызывать функцию повторно внутри обработчика (для циклического выполнения каких-либо действий раз в N секунд).
Пример:

<?php
function signal_handler($sig) {
    switch($sig) {
        case SIGTERM:
        echo "SIGTERM received. Exitting...\n";
        exit(1);
        break;
    case SIGINT:
        echo "SIGINT received. Exitting...\n";
        exit(2);
        break;
    case SIGALRM:
        echo "ALARM! Time: ".date('H:i:s')."\n";
        pcntl_alarm(1);
        break;
    }
}
declare(ticks = 1);
if(function_exists('pcntl_signal_dispatch'))
    register_tick_function('pcntl_signal_dispatch');
pcntl_signal(SIGTERM,'signal_handler');
pcntl_signal(SIGINT,'signal_handler');
pcntl_signal(SIGALRM,'signal_handler');
pcntl_alarm(1);
while(true)
    usleep(10);
?>


После запуска скрипта, видим, что скрипт каждую секунду выводит сообщение о срабатывании "будильника". Чтобы прервать скрипт достаточно нажать Ctrl+C (послать скрипту SIGINT):

tem# php 1.php
ALARM! Time: 11:47:09
ALARM! Time: 11:47:10
ALARM! Time: 11:47:11
ALARM! Time: 11:47:12
ALARM! Time: 11:47:13
ALARM! Time: 11:47:14
^CSIGINT received. Exitting...
tem#


Следует отметить, что если вызвать pcntl_alarm в то время, пока предыдущий ждёт своего запуска, предыдущий alarm отменится, а функция вернёт количество секунд, оставшееся предыдущему alarm'у до запуска.

void pcntl_exec(str path [, arr args [, arr envs]]) - Функция запускает другую команду внутри памяти данного процесса. На самом деле, я не хотел описывать эту функцию в данной статье, т.к. она используется крайне редко.
Но, раз уже описываю функцию, то приведу пример:

<?php
$args = array('-v');
$envs['MYVAR'] = 'test';
pcntl_exec('/usr/bin/env', $args, $envs);
?>


Результат выполнения будет таким:
MYVAR=test
Примечание:
Массив с аргументами - простой нумерованный массив. Массив с параметрами среды - ассоциативный, ключи - названия параметров, значения - их значения.

int pcntl_fork() - Отсюда всё только начинается... Функция ветвит текущий процесс, полностью копируя память в новый. После ветвления, два процесса работают независимо друг от друга, но могут обмениваться сигналами. Процесс, от которого происходит ветвление, называется родительским (parent), а ответвлённый процесс - дочерним (child). Существует ряд функций (которые будут описаны позже) для контроля дочернего процесса родительским. Однако, для контроля родительского процесса внутри дочернего можно использовать только лишь одну функцию - posix_getppid, описанную выше. Функция получает PID родительского процесса, после чего, можно обмениваться с ним сигналами. PID дочернего процесса внутри родительского уже известен, т.к. его возвращает данная функция. Использовать ветвление можно в разных целях - от запуска нескольких совершенно разных контроллируемых скриптов до реализации многопоточности в PHP (это ответ на многие споры по поводу многопоточности в PHP). При вызове, функция возвращает 0 в дочерний процесс и PID дочернего процесса в родительский. В случае неудачи, возвращает -1 и ветвление не происходит.
Примеров использования можно приводить много, но постараюсь уместить всё в одном:

<?php
echo "Main PID: ".posix_getpid()."\n";
$pid = pcntl_fork(); // Fork you! :-D
// Совсем без юмора нельзя :-)
if($pid == -1)
    exit("Unable to fork\n"); // Хотя реально мне ниразу не попадалась эта ошибка.
    // В теории, это возможно, если
    // - превышен лимит на максимальное количество процессов
    // - при нехватки памяти
    // - при перегрузке процессора
if(!$pid) {
    // Мы в дочернем процессе.
    // $pid равен нулю, можете сами в этом убедиться.
    // Процессы работают одновременно и независимо друг от друга.
    // Все выше объявленные переменные и функции родительского процесса будут доступны и здесь.
    sleep(5);
    echo "My (child) PID: ".posix_getpid()."\n";
    echo "Parent PID: ".posix_getppid()."\n";
    exit(0); // Обязательно нужно завершать дочерний процесс выходом с кодом 0 (или >0 при ошибках).
    // В противном случае, может произойти зацикливание либо "висящий" дочерний процесс.
}
echo "Child PID: ".$pid."\n"; // Это выведется раньше, чем 2 строки выше.
sleep(10); // Подождём немного...
// А здесь вовсе не обязательно использовать exit.
?>


При выполнении получим примерно такой результат:

tem# php 1.php
Main PID: 30537
Child PID: 30538
My (child) PID: 30538
Parent PID: 30537
tem#


При этом никто не запрещает ветвление в цикле для реализации многопоточности или многозадачности. Однако, советую изучить и ниже описанные функции.
Родительский процесс может ветвиться несколько раз, так же, как и его дочерние процессы (т.к. они являются полноценными процессами).

int pcntl_wait(int &status [, int options=0]) - Функция просто ждёт завершения любого дочернего процесса. К сожалению, доступна только в PHP 5 (любой версии), но имеет аналог в виде pcntl_waitpid, который будет описан далее. status - переменная, в которую функция записывает статус выхода дочернего процесса, для использования в далее описанных функциях. options - необязательный параметр, про который я напишу в описании pcntl_waitpid. Здесь его использовать не рекомендую, т.к. он работает далеко не во всех системах. Возвращает функция PID завершившегося дочернего процесса, -1 при ошибке и 0, если ниодин дочерний процесс не завершился (при опции WNOHANG).
Пример приведу такой:

<?php
$pid = pcntl_fork();
if($pid == -1)
    exit("Unable to fork\n");
if(!$pid) {
    sleep(5);
    echo "My (child) PID: ".posix_getpid()."\n";
    echo "Parent PID: ".posix_getppid()."\n";
    sleep(5);
    exit(0);
}
echo "Child ".$pid." started.\n";
echo "Child PID: ".pcntl_wait($status)." exited.\n";
?>


Результат будет таким:

tem# php 1.php
Child 30908 started.
My (child) PID: 30908
Parent PID: 30907
Child PID: 30908 exited.
tem#


При этом, последняя строка выведется только после завершения дочернего процесса.

int pcntl_waitpid(int pid, int &status [, int $options=0]) - Функция служит для тех же целей, что и предыдущая. Однако, во-первых, доступна и в PHP 4, а во-вторых, можно указать PID конкретного дочернего процесса, чтобы ждать только его завершения.
Опции:
WNOHANG - возвращать 0 и не ждать завершения процесса, если ниодин дочерний процесс не завершился
WUNTRACED - возвращать PID даже для дочерних процессов, статус завершения которых не удалось отследить
Для использования обеих опций следует делать так: WNOHANG | WUNTRACED
В качестве PID можно указывать PID конкретного дочернего процесса, а можно указать -1, тогда поведение функции будет таким же, как у pcntl_wait. Существуют и другие варианты запуска, однако, я их не описываю здесь, т.к. они практически не используются.
Пример использования не привожу, т.к. он аналогичен предыдущему.

На последок, расскажу про несколько функций для отслеживания кода и причины завершения дочерних процессов. Примеры для этих функций я приводить не буду, т.к. они покажутся вам очень простыми :-)

bool pcntl_wifexited(int status) - Функция возвращает true, в случае, если дочерний процесс завершился сам (вызов exit). В противном случае, возвращает false.
В случае, если функция вернёт true, можно получить код завершения дочернего процесса функцией pcntl_wexitstatus. В качестве параметра необходимо указать статус выхода, полученный от одной из двух предыдущих функций.

int pcntl_wexitstatus(int status) - Функция возвращает код завершения дочернего процесса. Функция может использоваться только если предыдущая функция вернула true.

bool pcntl_wifsignaled(int status) - Функция возвращает true, в случае, если дочерний процесс завершился из-за не отловленного сигнала. В противном случае, возвращает false.
В случае, если функция вернёт true, сам сигнал можно определить функцией pcntl_wstopsig. В качестве параметра необходимо указать статус выхода, полученный от одной из двух предыдущих функций.

int pcntl_wtermsig(int status) - Функция возвращает сигнал, который привёл к завершению дочернего процесса.

bool pcntl_wifstopped(int status) - Функция возвращает true, если дочерний процесс приостановлен. Это возможно, только если pcntl_waitpid (или pcntl_wait) вызван с опцией WUNTRACED. В противном случае, функция вернёт false.

int pcntl_wstopsig(int status) - Функция возвращает сигнал, который привёл к приостановке дочернего процесса.

На этом, пожалуй, я закончу статью.
Автор статьи Eugen.
Всем спасибо за внимание и удачных разработок!