разница между выходом и возвратом после вызова vfork () - PullRequest
4 голосов
/ 06 октября 2011

У меня есть программа с неопределенным поведением (vfork () используется неуместно):

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

int main ( int argc, char *argv[] )
{
    pid_t pid;
    printf("___________befor fork______________.\n");
    if((pid=vfork()) < 0)
        perror("fork");
    else if(pid > 0)
        printf("parent\n");
    else
        printf("child\n");

    printf("pid: %d, ppid: %d\n", getpid(), getppid());

    //exit(0);
    return 0;
}

Если вместо использования return я использую функцию exit(0), вывод:

___________befor fork______________.
child
pid: 4370, ppid: 4369
parent
pid: 4369, ppid: 2924

Если я использую return 0 - я получаю бесконечный вывод, как это:

___________befor fork______________.
child
pid: 4455, ppid: 4454
parent
pid: 4454, ppid: 2924
___________befor fork______________.
child
pid: 4456, ppid: 4454
parent
pid: 4454, ppid: 2924
___________befor fork______________.
child
pid: 4457, ppid: 4454
parent
pid: 4454, ppid: 2924
    and so on ...

Я знаю, что вы не можете использовать return в дочернем элементе после функции vfork().Но я не понимаю, почему родитель не заканчивается после return вызова?

Спасибо.

Ответы [ 4 ]

4 голосов
/ 06 октября 2011

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

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

2 голосов
/ 09 декабря 2012

вы можете увидеть определение vfork в kernel.org: http://www.kernel.org/doc/man-pages/online/pages/man2/vfork.2.html. это хорошее объяснение.

2 голосов
/ 06 октября 2011

Текущий стандарт POSIX вообще не поддерживает vfork(). В старом (1997-ом) стандарте POSIX на странице vfork() говорилось:

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

Ваш код не вызывает ни семейную функцию exec, ни _exit(), поэтому вы вызываете неопределенное поведение, и все, что происходит, является законным.

Вы только что продемонстрировали, почему обычно нецелесообразно использовать vfork(). ИМНШО, это в том же лагере, что и gets(); Я знаю, что он существует, но я бы никогда не использовал его добровольно, и я был бы очень рад, чтобы реализации были:

pid_t vfork(void) { abort(); }
char *gets(char *buffer) { abort(); }
2 голосов
/ 06 октября 2011

Возвращаясь в свой дочерний процесс, вы вызываете неопределенное поведение, поэтому на самом деле все может произойти.

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

...