进程终止
进程终止场景
- 代码运行正常,结果正确
- 代码运行完毕,运行不正确
- 代码异常终止
Linux下,我们可以通过echo $?命令查看进程退出状态,命令的实际作用是查看离它最近的一条命令的退出码
#include <stdio.h>
int main()
{
return 1;
}
进程常见退出方法
- 从main返回
- 调用exit
- 调用_exit
exit与_exit
_exit原型
#include <unistd.h>
void _exit(int status);
参数:status定义了进程的终止状态,父进程通过wait来获取该值
虽然status是int,但是仅有低8位可以被父进程所用。所以在_exit(-1)时,在终端执行$?发现返回值是255
exit原型
#include <unistd.h>
void exit(int status);
两者相同点和不同点
exit函数做的事情
1.exit会执行用户通过atexit()或on_exit()定义的清理函数,这两个函数也很简单,这里不做赘述
2.关闭所有打开的流,所有的缓存数据均被写入,即清空缓冲区,这个下面有演示
调用exit系统调用(在内核中执行),不同于exit()函数,不要搞混了
如果想深入了解atexit()和on_exit(),请戳这里->atexit和on_exit
代码演示
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello");
exit(0);
}
运行结果:
[wh@localhost os_work]$ ./a.outhello[wh@localhost os_work]$ vim test.c
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello");
_exit(0);
}
运行结果:
[wh@localhost os_work]$ ./a.out[wh@localhost os_work]$
证明exit清空了缓冲区,而_exit并没有,而是直接释放内存,终止程序。
return 退出
return 是一种更常见的退出进程方法。执行return n 等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做exit的参数
进程等待
进程等待必要性
- 父进程调用wait或waitpid获得子进程退出信息,回收子进程资源,避免资源浪费
- 如果父进程不等待子进程,也就是子进程退出,父进程对其不管不顾,或者父进程先退出,子进程就会变为僵尸进程,即使是 kill -9 命令也杀不死僵尸进程,造成不必要的资源浪费
wait方法
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
返回值:成功返回子进程pid,失败返回-1。
参数:输出型参数,获取子进程退出状态,不关心则可以设置为NULL
子进程如果没有退出,则父进程会一直阻塞式等待。
而且wait会默认等待任意子进程,这点不同于waitpid,但是waitpid中也可以通过传入参数的不同设置为等待任意子进程
waitpid方法
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项`WNOHANG`,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时error会被设置成相应的值以指示错误所在;
参数:
pid:
pid = -1,等待任意一个子进程,与wait等效。
pid > 0, 等待其进程ID与pid相同的子进程。
status:
WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
获取子进程status
- waitpid和wait都有一个输出型参数status,如果传的是NULL,表示不关心子进程的退出状态
- 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程
- status不是一个简单的整形,它其中是用特定的比特位来表示退出信息的,可以当做位图看待(我们这里只研究status地16比特位)
测试代码:
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main()
{
pid_t pid = fork();
if(pid == 0)
{
//子进程
sleep(50);
exit(2);
}
//父进程
int ret,status;
ret = waitpid(pid,&status,0);
if(ret > 0 && (status&0x7F) == 0)//正常退出
{
printf("Child exit code:%d\n",(status>>8)&0xFF);//提取子进程退出码
}else//异常退出
{
printf("Child exit code:%d\n",status&0xFF);
}
return 0;
}
正常退出
![]()
异常退出(在其他终端通过kill -9 命令杀死该进程)