上一次学习了子进程的概念,调用以及在子进程中使用exec族函数装载新的可执行程序的方法。现在来研究一下关于进程的其他知识。
1. 终止进程
A)进程终止分为正常终止和异常终止两大类。
正常终止的方式有:在main函数中,用return返回;或者调用类exit函数。
对于异常终止方式有:调用abort函数;或者收到一个信号终止。
B)正常终止方式中,在main函数中使用return指令的实质是自行调用exit类函数来终止进程。
在main函数中使用return 0,等效于exit(0)。
C)异常终止方式中,abort函数是通过SIGABRT信号来实现的。
2. 用wait函数获取子进程的退出状态
A)Wait函数存在的意义,或者说背景
首先要知道一点:在Linux中,进程结束后,内核会为它保留一定的退出状态信息,就放在内存中。这个信息是给父进程查用的,如果父进程一直不去查用这些信息,就会出问题。这牵扯出进程的两种状态:孤儿进程和僵尸进程。
孤儿进程:父进程先于子进程终止,这时候子进程还在继续运行,他就被形象的称为孤儿进程。孤儿进程并非没有父进程,系统会自动为其分配一个新的父进程,往往是init。当孤儿进程终止时,init进程会对其使用wait函数,这样孤儿进程会干净的结束,不占用系统资源。
僵尸进程:如果子进程先于父进程终止,而父进程却没有对其执行wait。那么这个子进程就成了僵尸进程,僵尸进程已经释放了大部分的占用的系统资源,但是它仍然占据着包括进程号在内的一些资源。这时候,如果父进程结束了,那么僵尸进程占用的这些资源,会被init进程使用wait函数来释放掉。(或许可以理解成,这些僵尸进程先变成了孤儿进程,进而被init进程处理掉)。而如果父进程一直不结束,比如死循环查询,那么僵尸进程将一直存在,一直占用资源。而这显然是不好的,我们应该避免写出可能产生僵尸进程的代码。
B)Wait函数
Wait函数将使得调用它的进程挂起等待,直到他的任何一个子进程终止。
它的函数声明是 pid_t wait(int *status)
它有一个参数,是个指针。这个指针所指向的存储位置,将用来存放子进程的退出状态。它的返回值是刚才被成功获取退出状态信息的子进程的PID号,否则就是-1。
注意:还有waitpid()函数,可以做类似wait函数的工作,不过它可以更有针对性的只等待某个PID号的子进程。
C)如何查看被保存的退出状态信息
系统专门提供了几个宏,可以用来检测子进程是正常终止的还是异常终止的。针对正常和异常终止两种状态,分别用专用的宏可以用来查看退出状态信息。见下面的表格。特别注意的是:这几个带参数的宏中,其参数的数据类型是整型而非指针。
这几个宏一般是像下面这样用:
3.守护进程
A)本质上是一个孤儿进程。其父进程在创建它之后就终止了,而后它被交给init继承。
B)运行于后台的“死循环”
C)常用函数daemon()来创建,函数声明如下:
#include <unistd.h>
int daemon(int nochdir, int noclose);
其中,参数nochdir用于设置进程的工作目录。如果是0,则将工作目录设置为根目录;否则,就保持工作目录不变
参数noclose用于设置是否将标准输入、标准输出和标准错误重定向到/dev/null。
坦白讲,以目前的知识储备,我看不懂啥叫进程的工作目录;也不理解标准输入、标准输出和标准错误输出的含义;并且,我在网上查了下守护进行的创建。普遍都涉及到进程组、会话期、控制终端这些概念。关于这一部分,本教材讲的不好。暂且先跳过吧