【问题标题】:When is a process in UNIX terminated?UNIX 中的进程何时终止?
【发布时间】:2024-04-10 21:25:01
【问题描述】:

我对 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() 会发生什么 前?

【问题讨论】:

  • 当主程序返回时,将调用exit。
  • 每次我想退出进程时都应该使用 exit 吗?如果我使用或不使用 exit 有什么区别?
  • 如果不在main 中,则必须使用exit。 return 在 main 中是等价的,所以这取决于你。
  • 但我仍然不明白是否可以在不调用exit的情况下终止进程。另外,我在问题中描述的两个示例之间有什么区别?
  • 如果是主要的,那就没有区别了。

标签: unix process fork


【解决方案1】:

POSIX

exit():

终止进程

无论进程调用_exit()(可能间接通过exit())还是由于信号或其他原因而终止,进程终止的后果都会发生,这一点很重要。

[...]

根据 ISO C 标准的要求,从 main() 使用 return 与使用返回值调用 exit() 具有相同的行为(除了语言范围问题)。到达main() 函数的末尾与调用exit(0) 的行为相同。

另见wait()。正常终止:

如果wait()waitpid() 因为子进程的状态可用而返回,这些函数应返回等于子进程的进程ID 的值。 [...] 存储在stat_loc 指向的位置的值应为0 当且仅当返回的状态来自通过以下方式之一终止的已终止子进程:

  • 进程从main()返回0
  • 进程调用_exit()exit(),状态参数为0
  • 进程已终止,因为进程中的最后一个线程已终止。

指定终止原因列表:

  • WIFEXITED(stat_val)

    如果为正常终止的子进程返回状态,则计算为非零值。

  • WIFSIGNALED(stat_val)

    如果由于接收到未捕获的信号而终止的子进程返回状态,则计算为非零值(请参阅&lt;signal.h&gt;)。

以及各自的状态信息:

  • WEXITSTATUS(stat_val)

    如果WIFEXITED(stat_val) 的值非零,则此宏计算子进程传递给_exit()exit() 的状态参数的低8 位,或子进程返回的值来自main()

  • WTERMSIG(stat_val)

    如果WIFSIGNALED(stat_val) 的值非零,则此宏计算为导致子进程终止的信号的编号。


总结

综上所述,POSIX 提到了终止进程的两种方式:

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

线程

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


在孩子中使用exit()

在这种情况下,如果我们在之前没有做过exit(0) 就创建了wait() 会发生什么?

fork() 复制当前进程,当fork() 返回时,父子进程从同一点继续执行。

考虑以下示例:

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

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

在这个例子中,子进程没有在其if 分支中调用exit(),因此在调用foo() 之后它调用bar() 并从main() 返回导致子进程终止。换句话说,孩子和父母都在这里调用bar()

通常这不是你想要的,而是你写这个:

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

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

在这种情况下,孩子在调用foo() 后立即退出,这样bar() 就不会被孩子调用。

【讨论】:

    最近更新 更多