1.内核对于子进程会采用看顾模式,这种模式的概念是:一个进程可以衍生出很多子进程,那么该进程要看顾这些子进程,并对其退出做出相应。为了达到这个目的,内核会把每一个退出的子进程的信息依次加到内核队列中,以方便父进程在合适的时机通过Process.wait取回该信息。
所以,如果父进程比子进程先结束,就会造成 看顾 模式失效,从而导致内核中的子进程信息无法被取出,这时候那些子进程就会变成僵尸进程了。
- 内核会一直希望父进程调用Process.wait来获取子进程的信息,但是如果父进程先死掉了,或者父进程压根不去调用Process.wait来获取,那么子进程就会变成僵尸进程。
为了避免子进程变成僵尸进程,我们可以通过Process.detach来分离子进程,这样的话,子进程就和父进程没有关系了,不会变成僵尸进程。
- 进程皆可捕获信号,而且信号投递是不可靠的,我们可能在处理一个信号的时候接受不到下一个信号(通常是由于代码有问题)。而且信号可以中断任何代码,包括信号处理代码……
- Process.wait会阻塞的等待子进程结束,他还有一个非阻塞的版本,用于捕捉信号,Process.wait(-1,Process::WNOHANG)。当父进程想要捕捉子进程退出的信号的时候,可以使用这个版本。一个忠告是信号处理逻辑一定要放在while循环中进行,这样可以保证我们不错过其他信号。
while Process.wait
- 任何进程都可以捕捉信号并且给其他的线程发送信号。信号处理是全局的,也就是说一旦进程活着,他就可以捕获信号(感觉捕获信号类似于一个中断,当有信号产生的时候,就跳到捕获信号的代码处去执行了)
- 天字第一号进程的pid是1,他没有父进程,当子进程变为僵尸进程的时候,他自动成为这些僵尸进程的父进程
- 每个进程都属于一个进程组,每个进程组都有一个组id,进程组一般是父进程和他fork出来的子进程的集合,父进程的pid默认为该组的组id。但是我们可以通过
Process.setpgrp(id)
来设置某个进程属于哪一个进程组。还有一种进程组的情况是通过管道连接的一组进程,也会属于同一个进程组。
- 一般通过shell命令行启动的进程,都会创建一个进程组,比如通过shell启动python,然后在这个python中fork的子进程就都属于这个python进程组的成员。
- 通过命令行等终端,按下Ctrl+C等发送的信号,默认是发送给进程组中的所有进程,就上面的情况来说,在终端商按下Ctrl+C会终止python进程和它的子进程
10.会话组可以包含一个或者多个进程组,用户的一次登录就是一个会话组,两个用户登录会有两个会话组。如果会话组有一个命令行终端的话,那么会话组会有一个前台进程组和若干个后台进程组,在命令行终端上按下的Crtl+C等指令,会被发送给该会话组,然后会被转发到该会话组的前台进程组,进而被发送到该前台进程组中的所有进程
- 我们知道如果一个子进程先退出,父进程没有调用Process.wait,死去的子进程会变成僵尸进程,直到父进程调用了Process.wait。但是如果父进程先死了,子进程有变成僵尸进程的可能,但是由于这时候子进程自动把Init进程作为自己的父进程,也不一定会变成僵尸进程,此时的状态是孤儿进程。
- 守护进程就是完全不和终端交互的进程,也就是说这个进程不能有控制终端,不能成为进程组的领导进程,要达到这个目的有一个一般的步骤
1.fork出一个子进程,同时结束父进程
2.调用setsid,则为该子进程生成新的进程组和会话组。现在这个子进程成为了新进程组的领导进程,并且该子进程所属的会话组没有控制终端
3.子进程再次fork一次,生成孙进程,则该孙进程不属于进程组的领导进程,并且也没有控制终端。
4.重定向三个标准文件描述符到/dev/null
5.这样这个孙进程就是一个完全的守护进程了
- 使用exec可以转换进程,比如把一个Java进程转换成Python进程。一般都是fork+exec使用,这样做的好处是,exec转换的进程会保留原来进程所打开的所有资源,包括文件描述符等。
- 使用exec传递参数的时候,最好是传递字符数组,而不是字符串,因为我们知道每一个进程都有argv参数,传递字符数组的话会把该数组直接作为ARGV,而传递字符串的话,会启动一个shell来解释该字符串,这样会造成安全问题。