linux c 多进程fork基本用法及阻塞和非阻塞方式回收

2024-06-18 22:32

本文主要是介绍linux c 多进程fork基本用法及阻塞和非阻塞方式回收,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!



一、基本用法


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>#include <sys/types.h>
#include <unistd.h>#include <iostream>using namespace std;/*  多进程的基本用法  */
int main()
{pid_t ret;if ( (ret=fork()) < 0 ){fprintf(stderr,"fork fail. ErrNo[%d],ErrMsg[%d]\n",errno,strerror(errno));}else if ( 0==ret ){fprintf(stdout,"** child  process run. pid:[%6d], ppid:[%6d],ret:[%6d]  **\n",getpid(),getppid(),ret);//exit(0);  //  可以结束掉子进程,那么程序将不会再运行最后一行的printf,原因在于:fork之后的代码父子进程都可见都会执行,通过if可以控制父子进程进行执行不同的内容,原理在于fork不同于其他函数他返回两个值,给父进程返回的是子进程的pid,给子进程自己返回的是0,失败返回 -1,于是当在if中碰到exit自然子进程就结束了}else{fprintf(stdout,"** parent process run. pid:[%6d], ppid:[%6d],ret:[%6d]  **\n",getpid(),getppid(),ret);}printf("========== last line.  pid:[%6d], ppid:[%6d],ret:[%6d] ==========\n",getpid(),getppid(),ret);
}


二、阻塞方式回收进程防止僵尸进程产生



#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>using namespace std;/*  循环创建多个进程,并通过wait阻塞的方式回收进程  */
int main()
{int p_num=5;/* 使用循环创建多个进程 */for ( int i=0; i < p_num; i++ ){int ret = 0;if ( (ret = fork()) < 0 ){fprintf(stderr,"create child process error\n");}else if ( 0 == ret ){printf("child process [%d], pid[%4d] ppid[%4d]\n",i,getpid(),getppid());sleep(i);exit(0);}}printf("main pid[%4d] ppid[%4d]\n",getpid(),getppid());/* 回收进程,否则在主程序未结束之前,子进程不回收则变成了僵尸进程(主进程结束后僵尸的子进程的父进程会变成init有init回收令系统没有僵尸进程),若主进程是常驻进程则会产生很多僵尸进程 */pid_t child_pid;int status;for ( int i=0; i< p_num; i++ ){if ( (child_pid = wait(&status)) > 0 )printf("pid[%4d] revoked, status[%d]\n",child_pid,status);elsefprintf(stderr,"wait error ErrNo[%d],ErrMsg[%s]\n",errno,strerror(errno));}/* wait()方式回收进程是阻塞式的,即wait会一直等待直到回收了进程,程序才回执行随后的代码 */printf("revoked over\n");sleep(5);printf("all over\n");
}/*
一、使用time ./a.out运行
]# time a.out
child process [0], pid[9925] ppid[9924]
child process [1], pid[9926] ppid[9924]
child process [2], pid[9927] ppid[9924]
main pid[9924] ppid[24077]
child process [3], pid[9928] ppid[9924]
child process [4], pid[9929] ppid[9924]
pid[9925] revoked, status[0]
pid[9926] revoked, status[0]
pid[9927] revoked, status[0]
pid[9928] revoked, status[0]
pid[9929] revoked, status[0]
revoked over
all overreal    0m9.013s
user    0m0.000s
sys     0m0.008s结论:1.time可以统计程序运行的时间,其中real值就是程序从调用到结束花费的时间2.子进程总共sleep时间是max(0,1,2,3,4),父进程因为是阻塞方式所以等子进程都结束后再sleep了5秒,总共9秒3.进程间是并发执行的,所以总共的sleep时间是其中的最大值,而非累加二、ps查看进程
]# ps -ef | grep -i a.out
root    9924 24077  0 16:04 pts/10   00:00:00 a.out
root    9927  9924  0 16:04 pts/10   00:00:00 a.out
root    9928  9924  0 16:04 pts/10   00:00:00 a.out
root    9929  9924  0 16:04 pts/10   00:00:00 a.out
root    9931  9508  0 16:04 pts/6    00:00:00 grep -i a.out
]# ps -ef | grep -i a.out
root    9924 24077  0 16:04 pts/10   00:00:00 a.out
root    9928  9924  0 16:04 pts/10   00:00:00 a.out
root    9929  9924  0 16:04 pts/10   00:00:00 a.out
root    9933  9508  0 16:04 pts/6    00:00:00 grep -i a.out结论:1.随着时间的推移,子进程被回收,进程数减少2.相比不回收进程的情况,在主进程的整个生命周期中没有出现僵尸进程的出现三、注释掉回收的代码运行]# time ./a.out
child process [0], pid[10591] ppid[10590]
child process [1], pid[10592] ppid[10590]
child process [2], pid[10593] ppid[10590]
child process [3], pid[10594] ppid[10590]
main pid[10590] ppid[24077]
child process [4], pid[10595] ppid[10590]
all overreal    0m5.012s
user    0m0.000s
sys     0m0.008s结论:1.相比之前的阻塞回收,本次主进程和五个子进程虽为父子关系,但在系统调用过程中是并发执行的2.故最终耗时将是父进程和子进程花费时间的最大值 5 秒四、ps查看进程]# ps -ef | grep -i a.out
root   10461 24077  0 16:12 pts/10   00:00:00 a.out
root   10462 10461  0 16:12 pts/10   00:00:00 [a.out] <defunct>
root   10463 10461  0 16:12 pts/10   00:00:00 [a.out] <defunct>
root   10464 10461  0 16:12 pts/10   00:00:00 [a.out] <defunct>
root   10465 10461  0 16:12 pts/10   00:00:00 a.out
root   10466 10461  0 16:12 pts/10   00:00:00 a.out
root   10468  9508  0 16:12 pts/6    00:00:00 grep -i a.out]# ps -ef | grep -i a.out
root   10461 24077  0 16:12 pts/10   00:00:00 a.out
root   10462 10461  0 16:12 pts/10   00:00:00 [a.out] <defunct>
root   10463 10461  0 16:12 pts/10   00:00:00 [a.out] <defunct>
root   10464 10461  0 16:12 pts/10   00:00:00 [a.out] <defunct>
root   10465 10461  0 16:12 pts/10   00:00:00 [a.out] <defunct>
root   10466 10461  0 16:12 pts/10   00:00:00 a.out
root   10470  9508  0 16:12 pts/6    00:00:00 grep -i a.out]# ps -ef | grep -i a.out
root   10472  9508  0 16:12 pts/6    00:00:00 grep -i a.out结论:1.不回收的情况下,在主进程未结束sleep的5秒内,子进程逐渐exit结束后变为僵尸进程(<defunct>便是僵尸进程)2.因为不同进程sleep时间不同故,随着时间推移,僵尸进程逐渐增多3.五秒后主进程结束,僵尸进程被系统init接管,init回收僵尸进程4.系统中没有一个a.out的进程了
*/



三、非阻塞方式回收进程



#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>#include <iostream>using namespace std;/**子进程退出的时候,会发送SIGCHLD信号,默认的POSIX不响应,*所以,用该函数处理SIGCHLD信号便可,同时使用signal设置处理信号量的规则(或跳转到的函数)*/
void sig_handle( int num )
{int status;pid_t pid;while( (pid = waitpid(-1,&status,WNOHANG)) > 0){if ( WIFEXITED(status) ){printf("child process revoked. pid[%6d], exit code[%d]\n",pid,WEXITSTATUS(status));}elseprintf("child process revoked.but ...\n");}
}/*  循环创建多进程,采用非阻塞方式回收进程  */
int main()
{int p_num=5;signal(SIGCHLD, sig_handle);/* 使用循环创建多个进程 */for ( int i=0; i < p_num; i++ ){pid_t ret = 0;if ( (ret = fork()) < 0 ){fprintf(stderr,"fork fail. ErrNo[%d],ErrMsg[%s]\n",errno,strerror(errno));}else if ( 0 == ret ){printf("child process [%d], pid[%6d] ppid[%6d]\n",i,getpid(),getppid());sleep(i);exit(0);}}printf("main pid[%6d] ppid[%6d]\n",getpid(),getppid());/* 不再显式,阻塞方式回收进程,子进程退出的时候,会发送SIGCHLD信号,通过signal()注册处理信号的方法 */sleep(5);printf("sleep 1 over\n");sleep(5);printf("sleep 2 over\n");sleep(5);printf("sleep 3 over\n");sleep(5);printf("sleep 4 over\n");sleep(5);printf("all over\n");
}
/*
]# time ./a.out
child process [0], pid[ 19711] ppid[ 19710]
child process [1], pid[ 19712] ppid[ 19710]
child process [2], pid[ 19713] ppid[ 19710]
main pid[ 19710] ppid[ 15683]
child process [3], pid[ 19714] ppid[ 19710]
child process [4], pid[ 19715] ppid[ 19710]
child process revoked. pid[ 19711], exit code[0]
sleep 1 over
child process revoked. pid[ 19712], exit code[0]
sleep 2 over
child process revoked. pid[ 19713], exit code[0]
sleep 3 over
child process revoked. pid[ 19714], exit code[0]
sleep 4 over
child process revoked. pid[ 19715], exit code[0]
all overreal    0m4.009s
user    0m0.000s
sys     0m0.008s1.问题:如果程序末尾只有一个sleep函数的话,信号处理完后就会结束主进程的sleep,于是主程序结束,程序最终仅回收了一个进程,故而达不到回收所有进程的目的
2.不是解决方法的方法:加了5个sleep于是,可以看到程序将五个进程都回收了
3.总结1)异步回收依靠的是信号,子进程结束会发信号SIGCHLD,系统默认不处理,我们可通过signal()自定义该信号的处理方法2)因为信号也就是中断,是不确定时间点产生的,所以没有阻塞等待的过程,等到信号发生了去处理就行,于是达到了不阻塞情况的进程回收3)信号的到达会唤醒将主进程从sleep状态唤醒,于是导致并不能回收所有进程4)由于3)的原因,需要进一步研究,今天不早了,还有事就先到这5)如果主进程是常驻进程该方法就没有问题了
4.附1)信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关2)非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。3)
*/




四、结论非阻塞方式与阻塞方式的对比


1)阻塞方式便于流程控制,清晰易懂

2)非阻塞方式因为依靠的是信号,信号的发出和处理会唤醒主进程的sleep,这将导致假如主进程中用sleep控制程序将出现不sleep的情况。如果写上两个sleep的确可以解决不sleep的问题,但是在没有子进程回收时又多sleep了一次。

3)进程的回收主要原因是,子进程使用了return或者exit结束后,在主进程结束前,没人为已经结束的子进程收尸,子进程便成了僵尸进程。如果主进程会结束,那么这些僵尸进程将在主进程结束后由系统init为其收尸,如果主进程是常驻进程,那么可想而知,僵尸进程将越来越多,这就是进程回收的必要性。

4)经测试,所创建的子进程如果不结束,阻塞式回收进程会立即返回,错误码10,含义是没有子进程,而不是死等需要回收的子进程产生,所以,这种情况下,主进程是常驻进程,使用阻塞回收不会影响主进程的循环工作。


五、关于处理僵尸进程,感觉看下文就够了


http://www.cnblogs.com/wuchanming/p/4020463.html


或者,看这个也有点感觉的


http://www.cnblogs.com/pied/p/4441734.html


这篇关于linux c 多进程fork基本用法及阻塞和非阻塞方式回收的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1073235

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

#error用法

/* *检查编译此源文件的编译器是不是C++编译器 *如果使用的是C语言编译器则执行#error命令 *如果使用的是 C++ 编译器则跳过#error命令 */ #ifndef __cplusplus #error 亲,您当前使用的不是C++编译器噢! #endif #include <stdio.h> int main() {

如何突破底层思维方式的牢笼

我始终认为,牛人和普通人的根本区别在于思维方式的不同,而非知识多少、阅历多少。 在这个世界上总有一帮神一样的人物存在。就像读到的那句话:“人类就像是一条历史长河中的鱼,只有某几条鱼跳出河面,看到世界的法则,但是却无法改变,当那几条鱼中有跳上岸,进化了,改变河道流向,那样才能改变法则。”  最近一段时间一直在不断寻在内心的东西,同时也在不断的去反省和否定自己的一些思维模式,尝试重

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

idea lanyu方式激活

访问http://idea.lanyus.com/这个地址。根据提示将0.0.0.0 account.jetbrains.com添加到hosts文件中,hosts文件在C:\Windows\System32\drivers\etc目录下。点击获得注册码即可。

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

以canvas方式绘制粒子背景效果,感觉还可以

这个是看到项目中别人写好的,感觉这种写法效果还可以,就存留记录下 就是这种的背景效果。如果想改背景颜色可以通过canvas.js文件中的fillStyle值改。 附上demo下载地址。 https://download.csdn.net/download/u012138137/11249872

vue同页面多路由懒加载-及可能存在问题的解决方式

先上图,再解释 图一是多路由页面,图二是路由文件。从图一可以看出每个router-view对应的name都不一样。从图二可以看出层路由对应的组件加载方式要跟图一中的name相对应,并且图二的路由层在跟图一对应的页面中要加上components层,多一个s结尾,里面的的方法名就是图一路由的name值,里面还可以照样用懒加载的方式。 页面上其他的路由在路由文件中也跟图二是一样的写法。 附送可能存在