进程终止:
进程退出的场景:
- 代码运行完毕,结果正常
- 代码运行完毕,结果不正常
- 代码异常终止
进程常见的退出方式:
1.正常终止:
- 从main()函数返回
- 调用exit
- _exit
正常终止,可以通过echo $?来查看进程的退出码
2.异常退出:
- ctrl+c,信号终止
代码执行完后,如何得到代码退出的场景?
根据程序的退出码,不能根据函数的返回值
exit函数与_exit函数:
1._exit函数:
#include<unistd.h>
void _exit(int status);
参数:status定义了进程的终止状态,父进程通过wait来获取该值。虽然status是int,但是仅有低8为可以被父进程所用,所以_exit(-1)时,在中断执行$?发现返回值时255
2.exit函数:
#include<unistd.h>
void exit(int status);
3.exit函数和_exit函数有什么区别?
- exit最后也会调用_exit或on_exit定义的清理函数,但是exit还做了一些其他工作
- 执行用户定义的清理函数(atexit/on_exit)
- 关闭所有打开的流,所有的缓存数据均被写入
- 调用_exit
实例:
exit:
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 printf("haha");
6 exit(0);
7 }
运行结果:
_exit:
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 printf("haha");
7 _exit(0);
8 }
证明了exit清空了缓冲区,而_eixt是强制中止,并没有清空缓冲区,释放内存
return退出:
return是一种更常见的退出进程的方式,执行return n等于执行exit(n),因为调用main的运行时函数会将main的返回值最为exit的参数
进程等待:
进程等待的必要性:
- 子进程退出,父进程如果不管不顾,就会造成僵尸进程的问题,进而导致内存泄漏
- 另外,进程一旦变成了僵尸状态,就会刀枪不入,即使杀人不眨眼的kill -9也无能为力。因为谁也没有能力杀死一个已经死了的进程
- 最后,父进程派给子进程的任务完成的如何,我们需要知道,子进程运行完,是否正常退出?结果是对还是不对?
- 父进程通过进程等待的方式回收子进程资源。获取子进程的退出信息
进程等待的方法:
wait函数:
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
返回值:成功返回被等待进程的pid,失败返回-1
参数:输出型参数,获取子进程退出状态,终止进程的终止状态就放在他所指的内存单元里。如果不关心终止状态,则可以设置为NULL。
- wait函数,子进程未退出时,父进程将会被阻塞,直到子进程退出,wait()返回,父进程才能继续运行。而waipid()有一个选项可以使调用者非阻塞等待
- wait函数会默认等待任意子进程,但是waitpid可以等待指定的子进程
每个进程都是独立的,父进程怎么拿到子进程的信息的?
wait函数时系统调用,系统调用后并拷贝一份给父进程
waitpid函数:
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options);
返回值:
- 正常返回:返回收集到的子进程的进程id
- 如果设置了WNOHANG,而调用中waitpid发现自己没有已退出的子进程可回收,返回0
- 如果调用中出错,返回-1,这时errno会被设置成对应的值以指示错误所在
参数:
pid:
- pid=-1,等待任何一个子进程,与wait等效
- pid>0,等待其进程id与pid相等的子进程
status:
- WIFEXITED(status):查看进程是否正常退出,若正常终止子进程的返回的状态,则为真
- WEXITSTATUS(status):查看进程的退出码,若WIFEXITED非0,提取子进程退出码
options:
- WNOHANG:(等待时不要阻塞)若pid指定的子进程还没有结束,则waitpid()返回0,不予以等待,若正常结束,则返回该子进程的ID
wait/waitpid等待的方式:
- 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程的退出信息
- 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
- 如果不存在该子进程,则立即出错返回
获取子进程stutas:
- wait/waitpid,都有一个参数status参数,该参数是一个输出型参数,由操作系统填充
- 如果传递NULL,表示不关心子进程的退出状态信息
- 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程
- status不能简单的当作参数来看待,他可以当作一个位图(这里只研究团队低16位)
阻塞式等待:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6
7 int main()
8 {
9 pid_t pid;
10 pid = fork();
11 if(pid<0)
12 {
13 perror("fork");
14 return 1;
15 }
16 else if(pid==0)
17 {
18 printf("child is run,pid is:%d\n",getpid());
19 sleep(5);
20 exit(257);
21 }
22 else
23 {
24 int status = 0;
25 pid_t ret = waitpid(-1,&status,0);//等待任意进程,阻塞式
26 printf("this is test for wait\n");
27 if(WIFEXITED(status)&&ret == pid)//查看进程是否正常退出
28 {
29 printf("wiat child 5s success,child return code is:%d\n",
/WEXITSTATUS(status));//正常,查看退出码
30 }
31 else
32 {
33 printf("wait child failed,return.\n");
34 return 1;
35 }
36 }
37 return 0;
38 }
运行结果:
非阻塞式等待
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
7 int main()
8 {
9 pid_t pid;
10 pid = fork();
11 if(pid<0)
12 {
13 perror("fork");
14 return 1;
15 }
16 else if(pid==0)
17 {
18 printf("child is run,pid is:%d\n",getpid());
19 sleep(5);
20 exit(1);
21 }
22 else
23 {
24 int status = 0;
25 pid_t ret = 0;
26 do
27 {
28 ret = waitpid(-1,&status,WNOHANG);//阻塞式等待,轮询
29 if(ret ==0)
30 {
31 printf("child is running\n");
32 }
33 sleep(1);
34 }while(ret==0);
35 if(WIFEXITED(status)&&ret == pid)
36 {
37 printf("wiat child 5s success,child return code is:%d\n",
/WEXITSTATUS(status));
38 }
39 else
40 {
41 printf("wait child failed,return.\n");
42 return 1;
43 }
44 }
45 return 0;
46 }
运行结果如下:
阻塞式等待需要用轮询的方式,定期查看子进程的状态.
阻塞式等待可能失败,
返回值ret=0,表示子进程还在运行,父进程干其他的事 ;
>0,表示等待成功,子进程返回
<0,等待失败