UNIX 中的进程何时终止?

When is a process in UNIX terminated?

我对 UNIX 中进程的结束感到困惑。 UNIX 中的进程何时终止?我们是否一定要写 exit(0) 来终止进程?在我看来 - 不,但我无法弄清楚这两段代码之间的区别。

pid_t pid=fork();
if(pid < 0)
{
             perror("Fork error\n");
             return 1;  
}
else if (pid==0) /* child */
{
             //Do some operations here
}
else
{
            wait(NULL);
            //Do some operations
}

pid_t pid=fork();
if(pid < 0)
{
                 perror("Fork error\n");
                 return 1;  
 }
 else if (pid==0) /* child */
 {
                 //Do some operations here
                 exit(0);
 }
 else
 {
                wait(NULL);
                //Do some operations here
 }

可能我们只需要 exit(0) 用于 wait() 功能。在这种情况下,如果我们在没有完成 exit(0) 的情况下制作 wait() 会发生什么 前?

POSIX

参见exit():

Terminating a Process

It is important that the consequences of process termination as described occur regardless of whether the process called _exit() (perhaps indirectly through exit()) or instead was terminated due to a signal or for some other reason.

[...]

As required by the ISO C standard, using return from main() has the same behavior (other than with respect to language scope issues) as calling exit() with the returned value. Reaching the end of the main() function has the same behavior as calling exit(0).

另见 wait()。正常终止:

If wait() or waitpid() return because the status of a child process is available, these functions shall return a value equal to the process ID of the child process. [...] The value stored at the location pointed to by stat_loc shall be 0 if and only if the status returned is from a terminated child process that terminated by one of the following means:

  • The process returned 0 from main().
  • The process called _exit() or exit() with a status argument of 0.
  • The process was terminated because the last thread in the process terminated.

指定终止原因列表:

  • WIFEXITED(stat_val)

    Evaluates to a non-zero value if status was returned for a child process that terminated normally.

  • WIFSIGNALED(stat_val)

    Evaluates to a non-zero value if status was returned for a child process that terminated due to the receipt of a signal that was not caught (see <signal.h>).

以及相应的状态信息:

  • WEXITSTATUS(stat_val)

    If the value of WIFEXITED(stat_val) is non-zero, this macro evaluates to the low-order 8 bits of the status argument that the child process passed to _exit() or exit(), or the value the child process returned from main().

  • WTERMSIG(stat_val)

    If the value of WIFSIGNALED(stat_val) is non-zero, this macro evaluates to the number of the signal that caused the termination of the child process.


总结

总而言之,POSIX提到了两种终止进程的方式:

  • 进程可以自行终止,如果它从 main() 调用 exit()_exit() 或 returns 或进程的最后一个线程终止;
  • 进程可以被信号杀死(由内核发送,或其他进程,或进程本身)。

线程

如果您需要防止所有线程在 main() returns 时终止,另请参阅 "Is it OK to call pthread_exit from main?"


在child

中使用exit()

In this case, what would happen if we make wait() without having done exit(0) before?

fork() 复制当前进程,当 fork() returns.

时,parent 和 child 从同一点继续执行

考虑以下示例:

int main() {
    if (fork() == 0) { /* child */
        foo();
    }
    else { /* parent */
        wait(NULL);
    }

    bar(); /* called in parent in child */
    return 0; /* or exit(0) */
}

在此示例中,child 不会在其 if 分支中调用 exit(),因此在调用 foo() 之后它会调用 bar() 而不是 returns 来自 main() 导致 child 进程终止。也就是说,child和parent都在这里调用了bar()

通常这不是你想要的,你会这样写:

int main() {
    if (fork() == 0) { /* child */
        foo();
        exit(0);
    }
    else { /* parent */
        wait(NULL);
    }

    bar(); /* called only in parent */
    return 0;
}

在这种情况下,child 在调用 foo() 后立即退出,因此 bar() 不会在 child 中调用。