首页
最新活动
服务器租用
香港服务器租用
台湾服务器租用
美国服务器租用
日本服务器租用
新加坡服务器租用
高防服务器
香港高防服务器
台湾高防服务器
美国高防服务器
裸金属
香港裸金属服务器
台湾裸金属服务器
美国裸金属服务器
日本裸金属服务器
新加坡裸金属服务器
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
CDN
CDN节点
CDN带宽
CDN防御
CDN定制
行业新闻
官方公告
香港服务器资讯
帮助文档
wp博客
zb博客
服务器资讯
联系我们
关于我们
机房介绍
机房托管
登入
注册
帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
联系客服
服务器资讯
/
香港服务器租用
/
香港VPS租用
/
香港云服务器
/
美国服务器租用
/
台湾服务器租用
/
日本服务器租用
/
官方公告
/
帮助文档
Linux进程控制【创建、终止、等待】
发布时间:2024-02-27 19:43:51 分类:帮助文档
Linux进程控制【创建、终止、等待】 ✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Good judgment comes from experience, and a lot of that comes from bad judgment. 好的判断力来自经验,其中很多来自糟糕的判断力。 文章目录 🌇前言🏙️正文1、进程创建1.1、fork函数1.2、写时拷贝 2、进程终止2.1、退出码2.2、退出方式 3、进程等待3.1、等待原因3.2、等待函数3.3、等待时执行 🌆总结 🌇前言 进程 创建后,需要对其进行合理管理,光靠 OS 是无法满足我们的需求的,此时可以运用 进程 控制相关知识,对 进程 进行手动管理,如创建 进程、终止 进制、等待 进程 等,其中等待 进程 可以有效解决僵尸 进程 问题 汽车的中控台,可以对汽车进行各种操作 🏙️正文 本文涉及的代码都是以 C语言 实现的 1、进程创建 在学习 进程控制 相关知识前,先要对回顾如何创建 进程,涉及一个重要的函数 fork 1.1、fork函数 #include
//所需头文件 pid_t fork(void); //fork 函数 fork 函数的作用是在当前 进程 下,创建一个 子进程,子进程 创建后,会为其分配新的内存块和内核数据结构(PCB),将 父进程 中的数据结构内容拷贝给 子进程,同时还会继承 父进程 中的环境变量表 进程具有独立性,即使是父子进程,也是两个完全不同的进程,拥有各自的 PCB假设 子进程 发生改写行为,会触发写时拷贝机制 fork 函数返回类型为 pid_t,相当于 typedef int,不过是专门用于进程的,同时它拥有两个返回值: 如果进程创建失败,返回 -1进程创建成功后 给子进程返回 0给父进程返回子进程的 PID 值 通过代码理解 进程 创建 #include
#include
#include
#include
//进程等待相关函数头文件 int main() { //创建两个子进程 pid_t id1 = fork(); if(id1 == 0) { //子进程创建成功,创建孙子进程 pid_t id2 = fork(); if(id2 == 0) { printf("我是孙子进程,PID:%d PPID:%d\n", getpid(), getppid()); exit(1); //孙子进程运行结束后,退出 } wait(0); //等待孙子进程运行结束 printf("我是子进程,PID:%d PPID:%d\n", getpid(), getppid()); exit(1); //子进程运行结束后,退出 } wait(0); //等待子进程运行结束 printf("我是父进程,PID:%d PPID:%d\n", getpid(), getppid()); return 0; //父进程运行结束后,退出 } 观察结果不难发现,两个子进程已经成功创建,但最晚创建的进程,总是最先运行,这是因为 fork 创建进程后,先执行哪个进程取决于调度器 得到子进程后,此时可以在一个程序中同时执行两个进程!(父进程非阻塞的情况下) 注意:fork 可能创建进程失败 系统中的进程过多时实际用户的进程数超过了限制 1.2、写时拷贝 在【进程地址空间】一文中,谈到了写时拷贝机制,实现原理就是通过 页表+MMU 机制,对不同的进程进行空间寻址,达到出现改写行为时,父子进程使用不同真实空间的效果 验证写时拷贝现象很简单,创建子进程后,使其对生命周期长的变量作出修改,再观察父子进程的结果即可 #include
#include
#include
#include
//进程等待相关函数头文件 const char* ps = "This is an Apple"; //全局属性 int main() { pid_t id = fork(); if(id == 0) { ps = "This is a Banana"; //改写 printf("我是子进程,我认为:%s\n", ps); exit(0); //子进程退出 } wait(0); //等待子进程退出 printf("我是父进程,我认为:%s\n", ps); return 0; } 不难发现,子进程对指针 ps 指向内容做出改变时,父进程并不受影响,这就是写时拷贝机制 通过地址打印,发现父子进程中的 ps 地址一致,因为此时是虚拟地址在虚拟地址相同的情况下,真实地址是不同的,得益于 页表+MMU 机制寻址不同的空间 写时拷贝机制本质上是一种按需申请资源的策略 注意: 写时拷贝不止可以发生在常规栈区、堆区,还能发生在只读的数据段和数据段写时拷贝后,生成的是副本,不会对原数据造成影响 2、进程终止 假设某个进程陷入了死循环状态,可以通过特定方法终止此程序,如在命令行中莫名其妙输入了一个指令,导致出现非正常情况,可以通过 ctrl + c 终止当前进程;对于自己写的程序,有多种终止方法,程序退出时,还会有一个退出码,供 父进程 接收 2.1、退出码 echo $? main 函数中的最后一条语句 return 0 表示当前程序的退出码,0 表示程序正常退出,可以通过指令 echo $? 查看最近一次子进程运行的 退出码 退出码是给父进程看的,可以判断子进程是否成功运行 子进程运行情况: 运行失败或异常终止,此时出现终止信号,无退出码运行成功,返回退出码,可能出现结果错误的情况 进程退出后,OS 会释放对应的 内核数据结构+代码和数据 main 函数退出,表示整个程序退出,而程序中的函数退出,仅表示该函数运行结束 2.2、退出方式 对一个正在运行中的进程,存在两种终止方式:外部终止和内部终止,外部终止时,通过 kill -9 PID 指令,强行终止正在运行中的程序,或者通过 ctrl + c 终止前台运行中的程序 内部终止是通过函数 exit() 或 _exit() 实现的 之前在程序编写时,发生错误行为时,可以通过 exit(-1) 的方式结束程序运行,代码中任意地方调用此函数,都可以提前终止程序 void exit(int status); void _exit(int status); 这两个退出函数,从本质上来说,没有区别,都是退出进程,但在实际使用时,还是存在一些区别,推荐使用 exit() 比如在下面这段程序中,分别使用 exit() 和 _exit() 观察运行结果 int main() { printf("You can see me"); //exit(-1); //退出程序 //_exit(-1); //第二个函数 return 0; } 使用 exit() 时,输出语句 使用 _exit() 时,并没有任何语句输出 原因: exit() 是对 _exit() 做的封装实现_exit() 就只是单纯的退出程序而 exit() 在退出之前还会做一些事,比如冲刷缓冲区,再调用 _exit()程序中输出语句位于输出缓冲区,不冲刷的话,是不会输出内容的 3、进程等待 僵尸进程 是一个比较麻烦的问题,如果不对其做出处理,僵尸进程 就会越来越多,导致 内存泄漏 和 标识符 占用问题 3.1、等待原因 子进程运行结束后,父进程没有等待并接收其退出码和退出状态,OS 无法释放对应的 内核数据结构+代码和数据,出现 僵尸进程 为了避免这种情况的出现,父进程可以通过函数等待子进程运行结束,此时父进程属于阻塞状态 注意: 进程的退出状态是必要的进程的执行结果是非必要的 也就是说,父进程必须对子进程负责,确保子进程不会连累 OS,而子进程执行的结果是否正确,需要我们自行判断 3.2、等待函数 系统提供的父进程等待函数有两个 wait() 和 waitpid(),后者比较常用 #include
#include
pid_t wait(int* status); pid_t waitpid(pid_t pid, int* status, int options); wait() 函数前面已经演示过了,这里着重介绍 waitpid() 返回值及其参数 wait() 中的返回值和参数,包含在 waitpid() 中 返回值: 等待成功时,返回 >0 的值等待失败时,返回 -1等待中,返回 0 参数列表: pid 表示所等子进程的 PIDstatus 表示状态,为整型,其中高 16 位不管,低 16 位中,次低 8 位表示退出码,第 7 位表示 core dump,低 7 位表示终止信号options 为选项,比如可以选择父进程是否需要阻塞等待子进程退出 需要特别注意 status 通过代码演示 waitpid() 的使用 int main() { //演示 waitpid() pid_t id = fork(); //创建子进程 if(id == 0) { int time = 5; int n = 0; while(n < time) { printf("我是子进程,我已经运行了:%d秒 PID:%d PPID:%d\n", n + 1, getpid(), getppid()); sleep(1); n++; } exit(244); //子进程退出 } int status = 0; //状态 pid_t ret = waitpid(id, &status, 0); //参数3 为0,为默认选项 if(ret == -1) { printf("进程等待失败!进程不存在!\n"); } else if(ret == 0) { printf("子进程还在运行中!\n"); } else { printf("进程等待成功,子进程已被回收\n"); } printf("我是父进程, PID:%d PPID:%d\n", getpid(), getppid()); //通过 status 判断子进程运行情况 if((status & 0x7F)) { printf("子进程异常退出,core dump:%d 退出信号:%d\n", (status >> 7) & 1, (status & 0x7F)); } else { printf("子进程正常退出,退出码:%d\n", (status >> 8) & 0xFF); } return 0; } 不发出终止信号,让程序自然跑完 发出终止信号,强行终止进程 waitpid() 的返回值可以帮助我们判断此时进程属于什么状态(在下一份测试代码中表现更明显),而 status 的不同部分,可以帮助我们判断子进程因何而终止,并获取 退出码(终止信号) 在进程的 PCB 中,包含了 int _exit_code 和 int _exit_signal 这两个信息,可以通过对 status 的位操作间接获取其中的值 注意: status 的位操作需要多画图理解正常退出时,终止信号为0;异常终止时,退出码没有,两者是互斥的code dump 现阶段用不到,但它是伴随着终止信号出现的 如果觉得 (status >> 8) & 0xFF 和 (status & 0x7F) 这两个位运算难记,系统还提供了两个宏来简化代码 WIFEXITED(status) 判断进程退出情况,当宏为真时,表示进程正常退出WEXITSTATUS(status) 相当于 (status >> 8) & 0xFF,直接获取退出码 3.3、等待时执行 //options 参数 WNOHANG //比如 waitpid(id, &status, WNOHANG); 父进程并非需要一直等待子进程运行结束(阻塞等待),可以通过设置 options 参数,进程解除 夯 状态,父进程变成 等待轮询 状态,不断获取子进程状态(是否退出),如果没退出,就可以干点其他事 #include
#include
#include
#include
//进程等待相关函数头文件 int main() { //演示 waitpid() pid_t id = fork(); //创建子进程 if(id == 0) { int time = 9; int n = 0; while(n < time) { printf("我是子进程,我已经运行了:%d秒 PID:%d PPID:%d\n", n + 1, getpid(), getppid()); sleep(1); n++; } exit(244); //子进程退出 } int status = 0; //状态 pid_t ret = 0; while(1) { ret = waitpid(id, &status, WNOHANG); //参数3 设置为非阻塞状态 if(ret == -1) { printf("进程等待失败!进程不存在!\n"); break; } else if(ret == 0) { printf("子进程还在运行中!\n"); printf("我可以干一些其他任务\n"); sleep(3); } else { printf("进程等待成功,子进程已被回收\n"); //通过 status 判断子进程运行情况 if(WIFEXITED(status)) { printf("子进程正常退出,退出码:%d\n", WEXITSTATUS(status)); break; } else { printf("子进程异常退出,code dump:%d 退出信号:%d\n", (status >> 7) & 1, (status & 0x7F)); break; } } } return 0; } 程序正常运行,父进程通过 等待轮询 的方式,在子进程执行的同时,执行其他任务 当然也可以通过 kill -9 PID 命令使子进程异常终止 可以看到程序能分别捕捉到正常和异常的情况 注意: 如果不写进程等待函数,会引发僵尸进程问题 🌆总结 以上就是关于 Linux进程控制(创建、终止、等待) 的相关知识了,我们学习了 子进程 是如何被创建的,创建后又是如何终止的,以及 子进程 终止 父进程 需要做些什么,有了这些知识后,在对 进程 进行操作时能更加灵活和全面 如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力! 如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正 相关文章推荐 Linux进程学习【进程地址】 Linux进程学习【环境变量】 Linux进程学习【进程状态】 Linux进程学习【基本认知】 =============== Linux工具学习之【gdb】 Linux工具学习之【git】 Linux工具学习之【gcc/g++】 Linux工具学习之【vim】
上一篇
cf日本服务器下载
下一篇
【1分钟开服】腾讯云幻兽帕鲁服务器一键部署保姆教程
相关文章
有服务器怎么做游戏吗
Conda Pycharm Python 服务器等的使用记录
服务器多段速怎么设置
无需公网IP,在家SSH远程连接公司内网服务器「cpolar内网穿透」
网络原理(HTTP篇)
uni-app uni-file-picker文件上传实现拍摄从相册选择获取图片上传文档服务器(H5上传-微信小程序上传)
【服务器使用】vscode & winscp进行服务器容器连接(含修改初始密码)
2024年幻兽帕鲁服务器创建 幻兽帕鲁云服务器搭建教程
服务器ip怎么攻击
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价
7*24H在线售后
高可用资源,安全稳定
1v1专属客服对接
无忧退款试用保障
德讯电讯股份有限公司
电话:00886-982-263-666
台湾总部:台北市中山区建国北路一段29号3楼
香港分公司:九龙弥敦道625号雅兰商业二期906室
服务器租用
香港服务器
日本服务器
台湾服务器
美国服务器
高防服务器购买
香港高防服务器出租
台湾高防服务器租赁
美国高防服务器DDos
云服务器
香港云服务器
台湾云服务器
美国云服务器
日本云服务器
行业新闻
香港服务器租用
服务器资讯
香港云服务器
台湾服务器租用
zblog博客
香港VPS
关于我们
机房介绍
联系我们
Copyright © 1997-2024 www.hkstack.com All rights reserved.