【Linux系统编程】进程的控制:结束进程、等待进程结束
结束进程
首先,我们回顾一下 C 语言中 continue, break, return 的作用:
continue: 结束本次循环
break: 跳出整个循环,或跳出 switch() 语句
return: 结束当前函数
而我们可以通过 exit() 或 _exit() 来结束当前进程。
所需头文件:
#include <stdlib.h>
void exit(int value);
功能:
结束调用此函数的进程。
参数:
status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。
返回值:
无
所需头文件:
#include <unistd.h>
void _exit(int value);
功能:
结束调用此函数的进程。
参数:
status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。
返回值:
无
exit() 和 _exit() 函数功能和用法是一样的,无非时所包含的头文件不一样,还有的区别就是:exit()属于标准库函数,_exit()属于系统调用函数。
下面的例子验证调用 exit() 函数,会刷新 I/O 缓冲区(关于缓冲区的更多详情,请看《浅谈标准I/O缓冲区》):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char *argv[])
{printf("hi, mike, you are so good"); // 打印,没有换行符"\n"exit(0); // 结束进程,标准库函数,刷新缓冲区,printf()的内容能打印出来// _exit(0); // 结束进程,系统调用函数,printf()的内容不会显示到屏幕while(1); // 不让程序结束return 0;
}
运行结果如下:
上面的例子,结束进程的时候改为调用 _exit(),代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char *argv[])
{printf("hi, mike, you are so good"); // 打印,没有换行符"\n"//exit(0); // 结束进程,标准库函数,刷新缓冲区,printf()的内容能打印出来_exit(0); // 结束进程,系统调用函数,printf()的内容不会显示到屏幕while(1); // 不让程序结束return 0;
}
printf() 的内容是不会显示出来的,运行结果如下:
接下来,我们一起验证一下结束函数( return )和结束进程( exit() )的区别。
测试代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void fun()
{sleep(2);return; // 结束 fun() 函数while(1);
}int main(int argc, char *argv[])
{fun();printf("after fun\n");while(1); // 不让程序结束return 0;
}
运行结果如下:
通过上面的运行结果得知,return 的作用只是结束调用 return 的所在函数,只要这个函数不是主函数( main() ),只要主函数没有结束,return 并不能结束进程。
我们将上面例子 fun() 里的 return 改为 exit():
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void fun()
{sleep(2);exit(0); // 结束当前进程while(1);
}int main(int argc, char *argv[])
{fun();printf("after fun\n");while(1); // 不让程序结束return 0;
}
exit() 是可以结束 fun() 所在的进程,即可让程序结束运行,结果图如下:
等待进程结束
当一个进程正常或异常终止时,内核就向其父进程发送 SIGCHLD 信号,相当于告诉父亲他哪个儿子挂了,而父进程可以通过 wait() 或 waitpid() 函数等待子进程结束,获取子进程结束时的状态,同时回收他们的资源(相当于,父亲听听死去儿子的遗言同时好好安葬它)。
wait() 和 waitpid() 函数的功能一样,区别在于,wait() 函数会阻塞,waitpid() 可以设置不阻塞,waitpid() 还可以指定等待哪个子进程结束。
所需头文件:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:
等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。
在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块的信息(包括进程号、退出状态、运行时间等)。
调用 wait() 函数的进程会挂起(阻塞),直到它的一个子进程退出或收到一个不能被忽视的信号时才被唤醒(相当于继续往下执行)。
若调用进程没有子进程,该函数立即返回;若它的子进程已经结束,该函数同样会立即返回,并且会回收那个早已结束进程的资源。
所以,wait()函数的主要功能为回收已经结束子进程的资源。
参数:
status: 进程退出时的状态信息。
如果参数 status 的值不是 NULL,wait() 就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的。
这个退出信息在一个 int 中包含了多个字段,直接使用这个值是没有意义的,我们需要用宏定义取出其中的每个字段。
下面我们来学习一下其中最常用的两个宏定义,取出子进程的退出信息:
WIFEXITED(status)
如果子进程是正常终止的,取出的字段值非零。
WEXITSTATUS(status)
返回子进程的退出状态,退出状态保存在 status 变量的 8~16 位。在用此宏前应先用宏 WIFEXITED 判断子进程是否正常退出,正常退出才可以使用此宏。
返回值:
成功:已经结束子进程的进程号
失败:-1
从本质上讲,系统调用 waitpid() 和 wait() 的作用是完全相同的,但 waitpid() 多出了两个可由用户控制的参数 pid 和 options,从而为我们编程提供了另一种更灵活的方式。
pid_t waitpid(pid_t pid, int *status, int options);
功能:
等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。
参数:
pid: 参数 pid 的值有以下几种类型:
pid > 0
等待进程 ID 等于 pid 的子进程。
pid = 0
等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
pid = -1
等待任一子进程,此时 waitpid 和 wait 作用一样。
pid < -1
等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。
status: 进程退出时的状态信息。和 wait() 用法一样。
options: options 提供了一些额外的选项来控制 waitpid()。
0:
同 wait(),阻塞父进程,等待子进程退出。
WNOHANG;
没有任何已经结束的子进程,则立即返回。
WUNTRACED:
如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料)
返回值:
waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:
当正常返回的时候,waitpid() 返回收集到的已经子进程的进程号;
如果设置了选项 WNOHANG,而调用中 waitpid() 发现没有已退出的子进程可等待,则返回 0;
如果调用中出错,则返回 -1,这时 errno 会被设置成相应的值以指示错误所在,如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD;
测试例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{pid_t pid;pid = fork(); // 创建进程if( pid < 0 ){ // 出错perror("fork");exit(0);}if( pid == 0 ){// 子进程int i = 0;for(i=0;i<5;i++){printf("this is son process\n");sleep(1);}_exit(2); // 子进程退出,数字 2 为子进程退出的状态}else if( pid > 0){ // 父进程int status = 0;// 等待子进程结束,回收子进程的资源// 此函数会阻塞// status 某个字段保存子进程调用 _exit(2) 的 2,需要用宏定义取出wait(&status); // waitpid(-1, &status, 0); // 和 wait() 没区别,0:阻塞// waitpid(pid, &status, 0); // 指定等待进程号为 pid 的子进程, 0 阻塞// waitpid(pid, &status, WNOHANG); // WNOHANG:不阻塞if(WIFEXITED(status) != 0){ // 子进程是否正常终止printf("son process return %d\n", WEXITSTATUS(status));}printf("this is father process\n"); }return 0;
}
运行结果如下:
【Linux系统编程】进程的控制:结束进程、等待进程结束相关推荐
- Linux系统编程--3(exec 函数族,僵尸进程和孤儿进程,wait和wait_pid回收子进程)
exec 函数族 fork 创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支) ,子进程往往要调用一种 exec 函数以执行另一个程序.当进程调用一种 exec 函数时,该进程的用户 ...
- Linux系统编程总结
day2 vim的三种工作模式 命令模式 vi hello.c zz 保存退出 2.编辑模式 i a o s (有大写)可以写东西 3.末行模式: 文本和末行模式不能直接切换 要切换回命令模式 再到末 ...
- 攻克 Linux 系统编程
课程亮点 完整学习路线图,系统掌握核心知识点 内核源码深入分析,知其然更知所以然 高频问题全面汇总,精准定位症结所在 八大主题商业案例,实操中获得拔高提升 专家推荐 曾与宇文拓共事五年,他对技术的钻研 ...
- vbs结束进程代码_物联网学习教程—Linux系统编程之进程控制
Linux系统编程之进程控制 一.结束进程 首先,我们回顾一下 C 语言中 continue, break, return 的作用: continue: 结束本次循环 break: 跳出整个循环,或跳 ...
- linux系统编程学习_(2)进程控制-- fork函数、exec函数族、回收子进程--孤儿进程僵尸进程、wait函数
linux系统编程学习_(2)进程控制-- fork函数.exec函数族.回收子进程–孤儿进程僵尸进程.wait函数 进程控制 fork()函数 创建一个子进程. pid_t fork(void); ...
- Linux系统编程之进程与线程控制原语对比
Linux系统编程之进程与线程控制原语对比 进程 线程 fork pthread_create exit pthread_exit wait pthread_join kill pthread_can ...
- 【Linux系统编程学习】Linux进程控制原语(fork、exec函数族、wait)
此为牛客Linux C++和黑马Linux系统编程课程笔记. 1. fork函数 1.1 fork创建单个子进程 #include<unistd.h> pid_t fork(void); ...
- Linux系统编程——进程基础知识
Linux系统编程--进程基础知识 1.程序和进程 程序,是指编译好的二进制文件,在磁盘上,不占用系统资源(cpu.内存.打开的文件.设备.锁-) 进程,是一个抽象的概念,与操作系统原理联系紧密.进程 ...
- 【Linux系统编程】特殊进程之守护进程
00. 目录 文章目录 00. 目录 01. 守护进程概述 02. 守护进程查看方法 03. 编写守护进程的步骤 04. 守护进程代码 05. 附录 01. 守护进程概述 守护进程(Daemon Pr ...
- 【Linux系统编程】进程概述和进程号
00. 目录 文章目录 00. 目录 01. 进程概述 02. 进程状态 03. 进程控制块 04. 进程号 05. 进程号相关函数 06. 案例实战 07. 附录 01. 进程概述 我们平时写的 C ...
最新文章
- 自己不清醒,却要拉更多人陪葬的人!
- 最牛X的GCC 内联汇编
- 在SQL Server 2005 Express 中添加报表服务后 (Business Intelligence Development Studio) vs2005 加载程序集问题解决办法...
- Tensorflow学习-工具相关
- C语言Huffman Encode霍夫曼编码的算法(附完整源码)
- 《统计学》学习笔记之时间序列分析和预测
- 計算機二級-java06
- Android时代的赢创之路
- Guitar Pro如何更改五线谱的符杆方向
- 辗转相减法的发展应用-最大比例
- word 生成pdf_为什么Microsoft Word生成的PDF文件这么大?
- 【五线谱】调号 ( 调号标识位置 | 调号标记列表 | A 大调标识原理 | F、C、G 位置标记升号 # | F 大调标识原理 | B 位置标记降号 b )
- 贝壳找房2018算法笔试
- 学习笔记DL003:神经网络第二、三次浪潮,数据量、模型规模,精度、复杂度,对现实世界冲击
- 学习Java真的可以改变你的人生?
- 2.89亿元寻求股权转让“接盘侠”!紫光存储风波未平,紫光德瑞风云再起
- 机房动环监控系统3大价值,第一个太惊艳了
- 你知道抖音的洗脑,却不知道他的产品设计有多牛
- 新农慕课python答案第零周答案_智慧树慕课答案农产品加工工艺学参考答案公众号...
- 【web程序开发】前端标签介绍
热门文章
- 原生android 权限管理,Android 权限管理(原生、EasyPermissions、RxPermissions)
- python的jsonpath_python 提取json数据的jsonPath介绍及简单使用
- 中考计算机考试作文,中考理化实验计算机考试作文
- pip安装了 但是python找不到_python安装完毕后,用pip安装提示找不到ssl模块怎么解决?...
- spring boot apollo demo
- Nuget 多平台多目标快速自动打包
- ssh服务、密钥登陆配置
- saltstack批量加用户脚本
- HDU 1814 Peaceful Commission / HIT 1917 Peaceful Commission /CJOJ 1288 和平委员会(2-sat模板题)...
- 运维与自动化运维发展概括