Linux内核深度解析
上QQ阅读APP看书,第一时间看更新

2.6 进程退出

进程退出分两种情况:进程主动退出和终止进程。

Linux内核提供了以下两个使进程主动退出的系统调用。

(1)exit用来使一个线程退出。

    void exit(int status);

(2)Linux私有的系统调用exit_group用来使一个线程组的所有线程退出。

    void exit_group(int status);

glibc库封装了库函数exit、_exit和_Exit用来使一个进程退出,这些库函数调用系统调用exit_group。库函数exit和_exit的区别是exit会执行由进程使用atexit和on_exit注册的函数。

注意:我们编写用户程序时调用的函数exit,是glibc库的函数exit,不是系统调用exit。


终止进程是通过给进程发送信号实现的,Linux内核提供了发送信号的系统调用。

(1)kill用来发送信号给进程或者进程组。

    int kill(pid_t pid, int sig);

(2)tkill用来发送信号给线程,参数tid是线程标识符。

    int tkill(int tid, int sig);

(3)tgkill用来发送信号给线程,参数tgid是线程组标识符,参数tid是线程标识符。

    int tgkill(int tgid, int tid, int sig);

tkill和tgkill是Linux私有的系统调用,tkill已经废弃,被tgkill取代。


当进程退出的时候,根据父进程是否关注子进程退出事件,处理存在如下差异。

(1)如果父进程关注子进程退出事件,那么进程退出时释放各种资源,只留下一个空的进程描述符,变成僵尸进程,发送信号SIGCHLD(CHLD是child的缩写)通知父进程,父进程在查询进程终止的原因以后回收子进程的进程描述符。

(2)如果父进程不关注子进程退出事件,那么进程退出时释放各种资源,释放进程描述符,自动消失。

进程默认关注子进程退出事件,如果不想关注,可以使用系统调用sigaction针对信号SIGCHLD设置标志SA_NOCLDWAIT(CLD是child的缩写),以指示子进程退出时不要变成僵尸进程,或者设置忽略信号SIGCHLD。


怎么查询子进程终止的原因?Linux内核提供了3个系统调用来等待子进程的状态改变,状态改变包括:子进程终止,信号SIGSTOP使子进程停止执行,或者信号SIGCONT使子进程继续执行。这3个系统调用如下。

(1)pid_t waitpid(pid_t pid, int *wstatus, int options);

(2)int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

(3)pid_t wait4(pid_t pid, int *wstatus, int options, struct rusage *rusage);

注意:wait4已经废弃,新的程序应该使用waitpid和waitid。


子进程退出以后需要父进程回收进程描述符,如果父进程先退出,子进程成为“孤儿”,谁来为子进程回收进程描述符呢?父进程退出时需要给子进程寻找一个“领养者”,按照下面的顺序选择领养“孤儿”的进程。

(1)如果进程属于一个线程组,且该线程组还有其他线程,那么选择任意一个线程。

(2)选择最亲近的充当“替补领养者”的祖先进程。进程可以使用系统调用prctl(PR_SET_CHILD_SUBREAPER)把自己设置为“替补领养者”(subreaper)。

(3)选择进程所属的进程号命名空间中的1号进程。