试用平台网站建设,重庆企业建站公司,购买域名和服务器,郑州发布会最新消息目录
进程终止
errno
exit和_exit
进程等待
wait和waitpid
宏#xff1a;WIFEXITED
非阻塞等待 进程终止
下面要谈的一个话题就是进程终止#xff0c;就是说一个进程退出了#xff0c;可能有三种情况
1.进程代码执行完#xff0c;结果是正确的
2.进程代码执行完WIFEXITED
非阻塞等待 进程终止
下面要谈的一个话题就是进程终止就是说一个进程退出了可能有三种情况
1.进程代码执行完结果是正确的
2.进程代码执行完结果是错误的
3.进程代码没有执行完进程出异常了中途退出了
其实我们写的main函数执行起来就是一个进程而我们一般写的return 0就叫做进程的退出码。一般0表示正确执行第一种情况而非0表示执行失败第二种情况。因为main函数的return 0就已经是代码的最后部分了。
为什么用非0表示执行失败呢因为成功就是成功了而失败可能会有很多种原因。进程的退出码是给机器看的要是给人看就要把退出码转化成错误描述这个错误描述可以是系统或语言自带的也可以自定义下面我们先用strerror函数看一下系统中的错误描述 我们可以看到有很多错误描述到133个了
这时我们就能解释我们瞎给比如ls 后面一个选项bash进程就是命令行解释器报的错是什么了比如 这不就是上面的二号错误吗另外下面的指令可以查看最近一次进程的退出码 为什么第二次用显示0呢因为echo $?也是一个进程它是成功执行的
我们上面说也可以自定义那就可以创建几个字符串枚举值并且枚举值是可以表示整数的这样就可以自己去定义错误码了比如 上面我们说了前两种情况第三种情况是进程没有执行完中途出现异常了只要中间出现异常其实结果对与否就没有意义了。其实中途出现异常本质上就是进程收到了异常信号就是kill -l那一系列的信号 比如 8号信号对应的是 SIGFPEFloating-Point Exception信号。这个信号用于指示浮点运算异常比如除以零或溢出等情况。 11号信号对应的是 SIGSEGVSegmentation Fault信号。这个信号用于指示进程发生了内存段错误即试图访问无效的内存地址。 并且有一个细节就是这些错误是从1开始的这跟我们下面的如何用16比特位表示退出码和收到什么信号是有关系的并且这些大写的字母都是宏定义。
所以进程执行的情况可以由两个数字表示一个是收到什么信号0就表示没有收到信号一个是退出码。
errno
除了进程退出就是main函数退出我们还有普通函数退出那我们如何知道它的运行情况呢我们也有个存放错误码的东西叫errno就是说函数如果执行失败的话那么错误码会放在erron这个整形变量里这个一般库函数才会有这个错误码因为库函数内部一般是有这个赋值的我们一般写的函数没有比如fopen可以看一下它的返回值 可以看到errno这个东西下面写一个代码看一下 其实我当前目录根本就没有这个文件并且是以只读的方式打开文件所以它肯定会出错错误信息就存在errno里面我们也可以看具体字符信息运行之后就是这样 exit和_exit
我们如果想让一个进程退出可以用exit或_exit前者是库函数后者是系统调用我们可以来查一下 这里的参数status就是你想让进程退出时的退出码我们通过一段代码来展示一下它们的区别 我们写这样一个代码运行完后发现什么都不打印而把_exit改成exit后就会打印这就说明exit会刷新缓冲区其实exit就是封装了_exit为什么要这样做呢
其实我们知道库函数和系统调用是上下层关系不同的操作系统的系统调用是不同的比如Windows下_exit就用不了所以这时我们把系统调用再封装一层成为库函数不同的操作系统封装不同的系统调用但是它们的库函数的接口就是一样的了这就通过库函数屏蔽掉了系统调用的差异就实现了语言的可移植性和跨平台性所以不同的操作系统就会安装不同的库文件。并且这里的缓冲区是库级别的缓冲区所以系统调用是无法刷新的如果是操作系统级别的那就会刷新因为操作系统不会让它白白占着空间的。
进程等待
我们之前说过父进程要回收子进程的PCB来拿到子进程的退出信息如果父进程不管不顾子进程就会进入僵尸状态就会造成内存泄漏这时就算kill -9也无能为力因为谁也不能杀死一个已经死掉的进程。而父进程回收子进程就是通过进程等待
wait和waitpid
我们有两种等待方式分别是wait和waitpid我们可以man查一下 这里的status是一个输出型参数通过给一个整型变量的地址这个函数内部就可以将退出信息写入到这个地址中如果不想让它写入可以给NULLpid就是要等待的子进程的pid如果是-1的话那么等待任一子进程这时与wait等效options是确定父进程是阻塞等待还是非阻塞等待
我们再看一下返回值是什么意思 就是说如果成功等待到了子进程结束就返回子进程的PID如果等待失败如果pid参数指定的子进程不存在或不是当前进程的子进程或是如果调用被一个信号中断就返回-1如果非阻塞等待WNOHANG等待后子进程状态没变那么返回0。 知道了各个参数和返回值是什么意思那我们就可以简单的来使用一下wait和waitpid 我们上面说过任何进程最终的执行情况可以有两个数字表明一个是退出码一个是退出信号如果收到退出信号那么最终的退出码没有意义。waitpid就是通过status这个输出型参数拿到这两个数字的那么这两个数字是怎么存在一个status中的呢我们来看一下
我们只用status中32个比特位中的低16位也就是0-15位如果进程没有收到退出信号那么8-15位就表示子进程的退出码所以我上面获取退出码时是先进行位右移8位然后按位与上0xff如果收到退出信号那么0-6位表示收到什么退出信号第七位表示core dump标志这个标志先不用管所以我上面是直接按位与上0x7f。
为了验证我们也可以故意给一个错误的代码比如访问空指针这时让程序运行看看父进程能否分析出子进程的退出信号 我们可以看到父进程确实等待到了子进程的退出信号11
宏WIFEXITED
其实不一定非得像上面那样进行位操作才可以得到退出码我们还可以通过宏来得到退出码或退出信号像下面这样 wait if exited这个表示如果进程是正常终止就是没有收到退出信号那就返回真所以我们用wait exit status来获取退出码如果为假我们用wait terminate signal来获取退出信号。因为只要有退出信号退出码就没意义退出码有意义时没有退出信号所以它们两个只要根据不同情况获取一个即可。 非阻塞等待
我们上面说过waitpid的第三个参数如果是0那么就是阻塞等待如果是WNOHANG就是非阻塞等待什么是阻塞等待呢其实就是父进程在等待子进程退出时如果什么都不做就叫做阻塞等待如果父进程使用WNOHANG后waitpid后就会立即返回不管是否有子进程退出如果等到了子进程退出返回值就是子进程的pid如果没有等到就返回0。因为waitpid只会运行一回有可能等不到子进程所以一般把它放进一个循环中并且父进程执行完waitpid后还可以执行自己的工作我们一般可以这样去实现 进程结束之后只留下进程PCB所以父进程肯定是通过子进程的PCB获取退出码和退出信号的我们在Linux源码中也确实可以看到