в чем разница между всеми возвращаемыми значениями функции fork?

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define LG_LIGNE 256
int main(void){

char ligne[LG_LIGNE];
while(1){//afficher un symbole d'invite(prompt)
    fprintf(stderr, "-->");
    //lire une ligne de commandes
    if(fgets(ligne,LG_LIGNE,stdin)==NULL)
    break;
    //supprimer le retour chariot final
    ligne[strlen(ligne)-1]='\0';
    //lancer un processeur
    if(fork()==0){
    //processus fils
    //executer la commande
    execlp(ligne, ligne, NULL);
    //msg d'erreur si on echoue
    perror(ligne);
    exit(EXIT_FAILURE);
    }
    else{//process père
    //attendre la fin de son fils
    waitpid(-1,NULL,0);
    //et reprendre la boucle
    }
}
fprintf(stderr,"\n");
return EXIT_SUCCESS;
}

Я понимаю, что означает каждое возвращаемое значение, но я не совсем понял это здесь, что технически означает нахождение в дочернем/отцовском процессе, и я не видел использования waitpid, что произойдет, если я его не использую?


person flora    schedule 14.10.2017    source источник
comment
Не могли бы вы быть немного более ясным, что вы спрашиваете?   -  person Derek Brown    schedule 14.10.2017


Ответы (2)


Fork() создает копию памяти и состояния процесса и порождает дочерний процесс для запуска в нем. И дочерний, и родительский процессы выполняются в собственном процессе со своей собственной копией адресного пространства и виртуальной памяти. Так как же определить, какая из них является родительской, а какая — дочерней, поскольку обе задачи возобновляют выполнение после вызова fork() с идентичным состоянием и памятью?

Вы можете сказать, кто является родителем, а кто дочерним, на основе кода возврата от fork(). Если возвращаемое значение равно нулю, то вы являетесь дочерним процессом, и этот процесс обычно проверяет код возврата на нулевое значение и переходит к выполнению дочерней работы.

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

Прежде чем родительский процесс завершится, он обычно ожидает завершения своих дочерних процессов. Это то, что делает вызов waitpid. Вы можете передать идентификатор дочернего процесса, и в этом случае вызов waitpid не вернется, пока этот дочерний процесс не завершится. Передача -1 в waitpid говорит ему подождать, пока не завершится ОДНА из ваших дочерних задач.

Код возврата из waitpid содержит идентификатор процесса завершенной дочерней задачи. Для получения дополнительной информации на этот вопрос уже был дан ответ здесь https://stackoverflow.com/a/21249082/6693299

Для полного объяснения того, что делает ваша программа, прочитайте прекрасное подробное описание H.S. который отвечает на тот же вопрос здесь https://stackoverflow.com/a/46741392/6693299

person ScottK    schedule 14.10.2017

Ваш первый вопрос —

what being in the child/father process technically means

fork() создает дочерний процесс, дублируя вызывающий процесс. Процесс, вызвавший fork(), является родительским процессом, а вновь созданный процесс является дочерним процессом. Таким образом, fork() делит процесс на два и возвращает 0 дочернему процессу и PID дочернего процесса родительскому процессу или -1, если вилка не удалась.

Дочерний процесс и родительский процесс выполняются в разных областях памяти. Во время fork() оба пространства памяти имеют одинаковое содержимое.

Существует концепция под названием Копирование при записи, полезно знать об этом.

Копирование при записи — это оптимизация, при которой таблицы страниц настраиваются таким образом, что родительский и дочерний процессы начинают совместно использовать всю одну и ту же память, и при необходимости копируются только те страницы, которые записываются любым процессом.

Если процесс не изменяет память и сразу же запускает новый процесс, полностью заменяя адресное пространство. Таким образом, было бы расточительно копировать всю память процесса во время разветвления, и вместо этого используется метод копирования при записи.

Например, в вашей программе сразу после fork вызывается execlp:

if(fork()==0){
//processus fils
//executer la commande
execlp(ligne, ligne, NULL);

Ваш второй вопрос —

I couldn't see the use of waitpid, what will happen if I didn't use it?

Чтобы объяснить это, я изменил вашу программу и добавил оператор для печати pid родительского и дочернего процесса и прокомментировал этот оператор:

waitpid(-1,NULL,0);

Вывод программы -

parent process pid : 22325 -->ls child process pid : 22326 < here the output of ls command >

Теперь, если мы увидим вывод команды ps с идентификатором родительского процесса grep -

# ps -eaf | grep 22325
root     22325 21555  0 10:39 pts/4    00:00:00 ./a.out
root     22326 22325  0 10:39 pts/4    00:00:00 [ls] <defunct>
root     22339 21644  0 10:39 pts/5    00:00:00 grep 22325

Здесь в выводе первый столбец — это UID, второй — PID, а третий — PPID (родительский pid). Вы можете видеть, что дочерний процесс (pid - 22326) помечен как ‹ несуществующий >.

"Несуществующий" процесс (также называемый "зомби-процессом") – это процесс, который завершил выполнение и будет иметь статус выхода. сообщить своему родительскому процессу. Из-за этой последней небольшой информации процесс останется в таблице процессов операционной системы как процесс-зомби, что указывает на то, что его дальнейшее выполнение не планируется, но что он не может быть полностью удален (и его идентификатор процесса не может быть удален). повторно используется) до тех пор, пока не будет определено, что статус выхода больше не нужен.

А вот и использование waitpid() -

Функции wait() и waitpid() должны получать информацию о состоянии, относящуюся к одному из дочерних процессов вызывающей стороны.

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

Для получения других сведений о waitpid(), таких как синтаксис (статус, параметры) и возвращаемое значение, вы можете проверить его man страница.

Если я раскомментирую waitpid() в вашей программе, скомпилирую и запущу ее, вывод -

parent process id : 23069
-->ls
child process id : 23070
<here the output of ls command>

Теперь, если мы увидим вывод команды ps с идентификатором родительского процесса grep -

# ps -eaf | grep 23069
root     23069 21555  0 10:51 pts/4    00:00:00 ./a.out
root     23108 21644  0 10:51 pts/5    00:00:00 grep 23069

Процесса зомби нет. Дочерний процесс, выполняющий команду ls, был завершен, и родительский процесс прочитал свой статус выхода.

Надеюсь, это ответит на ваши оба вопроса.

person H.S.    schedule 14.10.2017