当前位置: 首页 > Linux

Linux开发简单的多进程应用

时间:2023-04-07 02:06:26 Linux

一个进程中的多个线程共享一个进程的堆和其他内存空间,因此很方便实现数据交互;但是在多进程架构中,相对来说就困难多了!进程间通信(IPC,InterProcessCommunication)是指不同进程之间传递或交换信息。IPC的常用方法有:管道(无名管道、命名管道)、消息队列、信号量、共享内存、磁盘文件、套接字等。其中,套接字网络方式可以实现不同主机上的多进程IPC。一个有坑的小例子如下在Linux平台下使用fork()和pipe()实现一个简单的多进程多进程通信程序#include#include#includeintmain(){intpipefd[2];//两个文件描述符pid_tpid;焦炭[20];诠释我;//创建管道if(0>pipe(pipefd)){printf("CreatePipeError!\n");}//创建2个子进程for(i=0;i<2;i++){pid=fork();如果((pid_t)0>=pid){睡眠(1);休息;}}//如果fork的返回值小于0,说明子进程创建失败if((pid_t)0>pid){printf("ForkError!\n");返回3;}//如果fork的返回值为0,表示进入子进程流程逻辑if((pid_t)0==pid){//向主进程发送格式化数据FILE*f;f=fdopen(pipefd[1],"w");fprintf(f,"我是%d\n",getpid());f关闭(f);睡觉(1);//接收父进程发送的数据read(pipefd[0],buff,20);printf("MyPid:%d,消息:%s",getpid(),buff);}//进入父进程逻辑else{//循环接收所有子进程发送的数据,并将数据返回给子进程for(i=0;i<2;i++){//接收子进程发送的数据read(pipefd[0],buff,20);printf("MyPid:%d,Message:%s",getpid(),buff);//发送数据给子进程write(pipefd[1],"HelloMySon\n",14);}//这里调用sleep(3)的主要作用是等待子进程运行完毕//当然这不是很标准!睡觉(3);}return0;}编译程序gccprocess.c-oprocess,然后执行./process。输入的信息如下,但是我们可以从输出的内容中发现一些异常,比如为什么第二行开头有一个@字符,最后一行明显丢失了一些字符信息等等。上面的程序不是只是输出不符合预期的表面问题,还有很多坑,都是因为一开始对多进程和流水线的深层机制理解不正确!下面对Linux管道进行更深入的挖掘,大家可以发现上面的小程序存在很多坑。管道堵塞。管道读写被阻塞。当管道中没有数据,但进程在尝试读取时会阻塞。进程,如#include#include#includeintmain(){intpipefd[2];pid_tpid;焦炭[20];诠释我;if(0>pipe(pipefd)){printf("创建管道错误!\n");}pid=fork();if((pid_t)0>pid){printf("分叉错误!\n");返回3;}if((pid_t)0==pid){//write(pipefd[1],"Hello\n",6);}else{读(pipefd[0],buff,20);printf("MyPid:%d,Message:%s",getpid(),buff);}return0;}它的运行效果如下,可以看到主进程阻塞了,可以修改它让子进程往管道里写数据,主进程再读,这样就不会阻塞了#包括#include#includeintmain(){intpipefd[2];pid_tpid;焦炭[20];诠释我;if(0>pipe(pipefd)){printf("创建管道错误!\n");}pid=fork();if((pid_t)0>pid){printf("分叉错误!\n");返回3;}if((pid_t)0==pid){}else{read(pipefd[0],buff,20);printf("MyPid:%d,Message:%s",getpid(),buff);}return0;}运行程序,可以看到主进程没有被阻塞。所谓半双工就是半双工,就是说数据只能在一个方向上传输。对于管道,只能从管道[1]写入,从管道[0]读取。数据只能单向传输;可以结合socket来理解,socket是全双工的,即一个socket可以读写。第一个例程中创建了一个pipeline,但是希望通过这个pipeline可以实现主进程向子进程传递数据,子进程向主进程传递数据。它完全是为了在两个方向上传输数据。结果,主进程和两个子进程同时写入管道和从管道读取,所以上面提到的Weird现象比如下面的例子,创建了管道,但是没有创建子进程进程,你可以在主进程中写入和读取管道!#include#include#includeintmain(){intpipefd[2];pid_tpid;焦炭[20];诠释我;if(0>pipe(pipefd)){printf("创建管道错误!\n");}write(pipefd[1],"你好\n",6);读(pipefd[0],浅黄色,20);打印("MyPid:%d,Message:%s",getpid(),buff);return0;}因为pipeline是半双工的,所以如果要保证数据不乱,不能只用一个pipeline,需要有一套pipeline,有些pipeline是主进程到sub进程的数据-进程,还有一些是子进程到主进程的完整程序#include#include#includeintmain(){//流水线1,用于子进程向主进程发送数据intpipefd[2];//管道数组2,用于主进程分别向子进程发送数据intpipearr[3][5];pid_tpid;焦炭[20];诠释我;//创建管道if(0>pipe(pipefd)){printf("CreatePipeError!\n");}for(i=0;i<3;i++){if(0>pipe(pipearr[i])){printf("创建管道错误!\n");}}//创建3个子进程for(i=0;i<3;i++){pid=fork();//创建子进程失败if((pid_t)0>pid){printf("ForkError!\n");返回3;}//子进程逻辑if((pid_t)0==pid){//向主进程发送格式化数据FILE*f;f=fdopen(pipefd[1],"w");fprintf(f,"我是%d\n",getpid());f关闭(f);//接收父进程发送的数据read(pipearr[i][0],buff,20);printf("MyPid:%d,Message:%s",getpid(),buff);//完成后及时退出循环,继续循环会出大问题,这和fork的运行逻辑有关!休息;}}//主进程逻辑if((pid_t)0#includeintmain(){printf("PIPE_BUF=%d\n",PIPE_BUF);return0;}编译后运行效果如下: