二、进程终止
1、退出码
概念:
其实main函数是间接性被操作系统所调用的,当main函数调用结束后就应该给操作系统返回相应的退出信息,而这个所谓的退出信息就是以退出码的形式作为main函数的返回值返回
我们一般以0表示代码成功执行完毕,以非0表示代码执行过程中出现错误,一般来说我们写的代码都不太规范,没有根据执行结果返回相应的退出码
注:退出码可以人为定义,也可以使用系统的错误码表
示图:系统错误码表
退出码查看:
使用指令 echo $?
示例:
注:如果main没有return,则echo $?查看的是最近函数的退出码,一般来说都是0
2、退出方法
- 进程退出场景:
- 代码运行完毕,结果正确,退出码为0
- 代码运行完毕,结果不正确,逻辑存在问题,退出码为非0
- 代码异常终止,层序崩溃,退出码没有意义
- 进程常见退出方法:
1) 调用_exit函数
- _exit函数原型:
#include <unistd.h> void _exit(int status);
- 注意:
- status 定义了进程的终止状态,父进程通过wait来获取该值
- 虽然status是int,但是仅有低8位可以被父进程所用
注:_exit(-1)时,在终端执行$?发现返回值是255
- 示图:
2)调用exit函数
- exit函数原型:
#include <unistd.h> void exit(int status);
exit与_exit的区别:
_exit仅仅是退出进程
exit在退出进程前,先执行用户通过 atexit或on_exit定义的清理函数,关闭所有打开的流,所有的缓存数据均写入(刷新缓冲区),最后调用_exit
示图:
示例:
3)main函数return
return是一种更常见的退出进程方法,执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做exit的参数
示图:
注意:
只有是在main函数的的return才算是终止进程,其他函数return只是结束函数,因为系统调用的是main函数,main函数返回才是进程的返回
调用main函数运行结束后,main函数的return返回值当做exit的参数来调用exit函数,而使用exit函数退出进程前,exit函数会先执行用户定义的清理函数、冲刷缓冲,关闭流等操作,然后再调用_exit函数终止进程
4)异常退出
向进程发生信号
如在进程运行过程中向进程发生kill -9信号使得进程异常退出,或是使用Ctrl+C迫使进程退出
代码运行异常
如代码当中存在野指针问题等bug问题使得进程运行时异常退出
3、理解终止
以OS角度理解:核心思想-归还资源
释放曾经为管理进程所维护的数据结构资源,并非销毁释放数据结构对象,而是将状态设置为无效并保存起来,下一次需要就直接使用不用申请,相当于建立对应的数据结构“内存池”
释放程序数据和代码占用的空间,并非清空数据和代码,而是将对应内存区域设置为无效,要再次使用时直接覆盖数据和代码就行了
取消曾经该进程在进程队列里的链接关系(避免”野指针“)
三、进程等待
进程等待必要性:
当子进程退出,并不是完全退出,子进程的PCB任然存在,父进程如果不等待回收,就可能造成‘僵尸进程’的问题,进而造成内存泄漏
注:进程一旦变成僵尸状态,并不能被父进程给kill掉,因为子进程已经死去,只能父进程等待回收
子进程的PCB保留着退出前任务执行的信息,而通过回收子进程我们可以知道子进程运行完成,结果对还是不对,或者是否正常退出
注:非必须,依执行的程序和需求而定
尽量使父进程晚于子进程退出,可以规范化进行资源的回收
注:所以一般来说,当我们fork之后,就需要父进程等待回收子进程
1、等待方法
wait方法:
wait函数原型:
#include<sys/types.h> #include<sys/wait.h> pid_t wait(int*status);
- 注意:
- wait函数作用的等待任意子进程
- 返回值:成功返回被等待进程pid,失败返回-1
- 参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
- waitpid方法:
waitpid函数原型:
#include<sys/types.h> #include<sys/wait.h> pid_ t waitpid(pid_t pid, int *status, int options);
注意:
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID
如果设置了选项options为WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在
参数pid:
Pid=-1,等待任一个子进程,与wait等效
Pid>0,等待其进程ID与pid相等的子进程
参数status:
参数status是一个输出型参数,需要我们传入一个整形变量的地址,以此获取子进程退出的信息
使用对应的宏可以查看我们需要的退出信息:WIFEXITED(status): 若为正常终止子进程返回的状态,则为真(查看进程是否是正常退出);WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码(查看进程的退出码)
参数options:
设置为0:表示默认的阻塞式等待子进程退出,即子进程没退出就不返回,一直等待到子进程退出回收子进程
设置为WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待;若正常结束,则返回该子进程的ID
示例1:阻塞等待
#include<stdio.h> #include<sys/types.h> #include<sys/wait.h> #include<unistd.h> #include<stdlib.h> int main() { pid_t id=fork(); if(id==0) { printf("I am child process: pid:%d ppid:%d\n",getpid(),getppid()); sleep(5); // int* p=12345; // *p=100;//野指针 exit(123); } printf("father wait...\n"); int status=0; //pid_t ret=wait(&status);//等待特定任意子进程 pid_t ret=waitpid(id,&status,0);//阻塞等待特定子进程 if(ret>0&&WIFEXITED(status))//等待成功并子进程退出正常 { printf("wait success: wait for id:%d status code:%d\n",ret,WEXITSTATUS(status)); } else if(ret>0)//等待成功但是子进程退出异常 { printf("exit error! status codedump:%d sign:%d\n",(status>>7)&1,status&0x7F); } return 0; }