目录
一、进程终止
我们在c/c++中,总是在main函数中的结尾设置 return 0,那为什么要返回0呢?返回其他的可不可以。
当然可以,return 0代表代码跑完了,结果正确。return 非0代表结果不正确。以前一股脑的都写return 0当然是不正确的。
我们成功了,自然是好,失败了,我们最关心的就是为什么失败?return 非0的数来表示不同的原因。
我们把main函数中的返回值叫做进程退出码。
在上一篇文章中,我们说了,fork可以创建一个子进程,可以让子进程去干我们要他干的事情,那究竟有没有干好,这个进程退出码由父进程来接受去判断有没有执行成功结果对不对。在linux环境下我们可以说这个return X就是给父进程返回的。
我们简单看下:
我们在main函数中,return 3,这个3要被返回给父进程,但这个进程据我们了解并不是通过fork得来的,返回给谁。在这里我们要知道我们在命令行启动的进程,父进程都是BASH。
通过,echo $?来获取BASH中的接受到的进程退出码。
所以结果是:
我们还可以获得上一个在命令行中启动的进程的进程退出码。
我们在命令行输入ls
这个ls进程执行成功了,所以返回0。
那如果ls进程执行失败呢?
则返回2,所以查看子进程运行结果,通过进程退出码来判断,这是它的作用。
在linux中这些,进程退出码都是什么意思?我们可以用strerror来了解一下,这个函数可以将退出码转换成char*,在这里我们打印下0-19的退出码所表示的错误信息。
当然我们可以自己设置退出码所对应的退出信息。
也可以通过exit退出程序,exit(退出码)。
二、进程等待
1.我们为什么要进程等待?
在先前我们了解到子进程比父进程先退出,如果父进程不管不顾,子进程就会大杀四方,会导致内存泄漏,就算大名鼎鼎的Kill -9都干不掉他,所以我们通过进程等待的方式,来回收子进程的资源,获取子进程的退出信息。
我们来尝试下如何等待,先使用wait这个函数。
我们通过这样的代码来看看结果,先将它运行起来。
通过kill -9 加子进程代码,杀掉子进程,父进程就可以等待成功。
所以,证明wait可以回收子进程。
其实,我们平常用的最多的是waitpid,这是今天的重头戏。
注意:wait/waitpid()是系统调用。
2.waitpid
先讲讲返回值pid_t :
分两种情况如果返回值>0,则说明等待成功,返回值是等待的子进程的pid
<0,意味着等待失败。
第一个参数pid:
是几,就代表等待的是哪一个子进程,如果是123,就代表的是等待pid为123的子进程。
为-1,代表等待任意进程,和上文的wait一样,wait的功能就是等待任意进程。
参数二status
这个参数是一个输出形参数,通过调用该函数从函数内部拿出来特定的数据。
内部是哪里?要知道wait/waitpid()是系统调用,是从操作系统中拿。前一篇文章讲过,os在创建一个进程时要建立内核数据结构(struct mm_struct task_struct 页表)+代码继承父进程,数据以写时拷贝的形式,与父进程独立或共享。拿什么?从os中哪里拿?子进程结束后,进程的退出信息将被写入task_struct进程控制块。我们就要从task_struct进程控制块拿子进程的退出信息。
我们看下linux中的源码:在进程控制块中,有其中几个信息exit_code(退出码) exit_signal
聊了参数从哪里来,我们看看拿来的参数具体是什么
我们不能把它当作整形来看待,虽然它的类型是整形,但我们只用关注低16位 中的 次低8位
我们可以用(status>>8)&0xFF来获取退出码。
参数三options
0代表阻塞等待,子进程退出了我才能等待成功,等待子进程结束了再回收。为WNOHANG,则不再等待,如果要回收的⼦进程都在运⾏,则直接返回0,然后接着执⾏后续程序;为WUNTRACED,如果⼦进程由于被停⽌产⽣的SIGCHLD,waitpid则⽴即返回;WCONTINUED,如果⼦进程由于被SIGCONT唤醒⽽产⽣的SIGCHLD,waitpid则⽴即返回。
3.尝试
说了这么多,我们来尝试下用waitpid去等待一下子进程的退出码。
运行结果如图,我们等待成功了,并获得了子进程的返回码。
当然对status一顿操作,很是麻烦,linux提供了宏
WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看是否正常退出)
WEXITSTATUS(status):若WIFEXITED非零(真),提取子进程的退出码。(提取子进程的退出码)
代码可以改写成这样:
运行结果如图:
4.非阻塞式的等待的轮询检测
waitpid的第三个参数改为WNOHANG,则为非阻塞式等待。
非阻塞等待就是,我直接去等待子进程,无论子进程是否在进行还是已经退出,等待一次,如果返回值为子进程的pid说明等待成功,子进程退出。如果等于0,说明等待成功了,但是子进程没有退出。
因为只等待一次,一般我们都会用轮询检测,其实就是一直循环调用waitpid()去等待子进程。