什么是进程间通信 Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
什么是管道 管道是一种最基本的IPC机制,也称匿名管道,应用于有血缘关系的进程之间,完成数据传递。调用pipe函数即可创建一个管道。
管道有以下特性:
管道的本质是一块内核缓冲区
由两个文件描述符引用,一个表示读端,一个表示写端。
规定数据从管道的写端流入管道,从读端流出。
当两个进程都终结的时候,管道也自动消失。
管道的读端和写端默认都是阻塞的。
管道创建步骤:
父进程调用pipe函数创建管道,得到两个文件描述符fd[0]和fd[1],分别指向管道的读端和写端。
父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管。
父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出,这样就实现了父子进程间通信。
相应代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> using namespace std; int main () { int fd[2 ]; int ret = pipe (fd); if (ret<0 ) { perror ("pipe error" ); return -1 ; } pid_t pid = fork(); if (pid<0 ) { perror ("fork error" ); return -1 ; } else if (pid>0 ) { close (fd[0 ]); sleep (5 ); write (fd[1 ], "hello world" , strlen ("hello world" )); wait (NULL ); } else { close (fd[1 ]); char buf[64 ]; memset (buf, 0x00 , sizeof (buf)); int n = read (fd[0 ], buf, sizeof (buf)); cout << "read over, n=" << n << " buf=" << buf << endl; } return 0 ; }
结果如下:
子进程读到了父进程发送的数据!!!
原文链接:https://blog.csdn.net/m0_60663280/article/details/121309197
扩展:父进程创建三个子进程并且通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/wait.h> #include <strings.h> main(){ int i, r, p1, p2, p3, fd[20 ]; char buf[50 ], s[50 ]; pipe(fd); while ((p1 = fork()) == -1 ); if (p1 == 0 ){ lockf(fd[1 ], 1 , 0 ); sprintf (buf, "child process P1 is sending messages!\n" ); printf ("child process P1!\n" ); write(fd[1 ], buf, 50 ); sleep(5 ); lockf (fd[1 ], 0 , 0 ); exit (0 ); }else { while ((p2 = fork()) == -1 ); if (p2 == 0 ){ lockf(fd[1 ], 1 , 0 ); sprintf (buf, "child process P2 is sending messages!\n" ); printf ("child process P2!\n" ); write(fd[1 ], buf, 50 ); sleep(5 ); lockf (fd[1 ], 0 , 0 ); exit (0 ); }else { while ((p3 = fork()) == -1 ); if (p3 == 0 ){ lockf(fd[1 ], 1 , 0 ); sprintf (buf, "child process P3 is sending messages!\n" ); printf ("child process P3!\n" ); write(fd[1 ], buf, 50 ); sleep(5 ); lockf (fd[1 ], 0 , 0 ); exit (0 ); } wait(0 ); if ((r = read(fd[0 ], s, 50 )) == -1 ){ printf ("can`t read pipe!\n" ); }else { printf ("%s\n" , s); } wait(0 ); if ((r = read(fd[0 ], s, 50 )) == -1 ){ printf ("can`t read pipe!\n" ); }else { printf ("%s\n" , s); } wait(0 ); if ((r = read(fd[0 ], s, 50 )) == -1 ){ printf ("can`t read pipe!\n" ); }else { printf ("%s\n" , s); } exit (0 ); } } }
显示的时候先显示child process P1!过了五秒才会显示chilld process P2!以及child process P1 is sending message!这是因为中间有一个sleep()函数的关系,以及lockf加锁使得其他进程不能插进来并发执行
原文链接:https://blog.csdn.net/bxy0806/article/details/121211176
如果将lockf函数去掉的话
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/wait.h> #include <strings.h> main(){ int i, r, p1, p2, p3, fd[20 ]; char buf[50 ], s[50 ]; pipe(fd); while ((p1 = fork()) == -1 ); if (p1 == 0 ){ sprintf (buf, "child process P1 is sending messages!\n" ); printf ("child process P1!\n" ); write(fd[1 ], buf, 50 ); sleep(5 ); exit (0 ); }else { while ((p2 = fork()) == -1 ); if (p2 == 0 ){ sprintf (buf, "child process P2 is sending messages!\n" ); printf ("child process P2!\n" ); write(fd[1 ], buf, 50 ); sleep(5 ); exit (0 ); }else { while ((p3 = fork()) == -1 ); if (p3 == 0 ){ sprintf (buf, "child process P3 is sending messages!\n" ); printf ("child process P3!\n" ); write(fd[1 ], buf, 50 ); sleep(5 ); exit (0 ); } wait(0 ); if ((r = read(fd[0 ], s, 50 )) == -1 ){ printf ("can`t read pipe!\n" ); }else { while ((p3 = fork()) == -1 ); if (p3 == 0 ){ sprintf (buf, "child process P3 is sending messages!\n" ); printf ("child process P3!\n" ); write(fd[1 ], buf, 50 ); sleep(5 ); exit (0 ); } wait(0 ); if ((r = read(fd[0 ], s, 50 )) == -1 ){ printf ("can`t read pipe!\n" ); }else { printf ("%s\n" , s); } wait(0 ); if ((r = read(fd[0 ], s, 50 )) == -1 ){ printf ("can`t read pipe!\n" ); }else { printf ("%s\n" , s); } wait(0 ); if ((r = read(fd[0 ], s, 50 )) == -1 ){ printf ("can`t read pipe!\n" ); }else { printf ("%s\n" , s); } exit (0 ); } } }
如果不加锁的话,迟来的process P2/P1/P3先后同时显示出来,由于sleep的关系再过5s显示下面剩下的语句
进程控制lockf()详解 lockf()函数
利用系统调用lockf(fd,mode,size),对指定区域(有size指示)进行加锁或解锁,以实现进程的同步或互斥。
其中,fd是文件描述字;
mode是锁定方式,mode=1表示加锁,mode=0表示解锁;
size是指定文件fd的指定区域,用0表示从当前位置到文件结尾。
注:有些Linux系统是locking(fd,mode,size)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main () {int pid ,i;if ((pid = fork())<0 ){printf ("child fails to create\n" );return 0 ;} else if (pid ==0 ){lockf(1 ,1 ,0 ); for (i = 0 ;i< 5 ;i++)printf ("This is child (pid = %d)process:b\n" ,getpid());lockf(1 ,0 ,0 ); return 0 ;} else {lockf(1 ,1 ,0 ); for (i =0 ;i<5 ;i++)printf ("Parent process:a\n" );lockf(1 ,0 ,0 ); } }
首先是在父进程中创建了一个子进程,子进程需要输出五次文本信息,并且在输出语句执行之前,将标准输出设备锁住,在输出语句执行之后,将标准输出设备解锁。同样,父进程也是要输出五次文本信息,对标准输出的操作同子进程。这样做能够防止父进程和子进程竞争标准输出(屏幕)资源,即如果父进程正在进行屏幕打印操作,则子进程将无法获得屏幕,即不可能在在输出“Parents Process:a”的时候,中间穿插有“This is child process :b”。从运行结果的截图中可以看到,实验结果和我的分析是相吻合的,先输出五条Parent,再输出五条Child,中间不会有交叉。为了进一步验证我的分析,从反面论证,我把父进程和子进程的lockf语句注释掉,期望运行的时候会看到Parent和Child交叉出现。但是运行了很多次,结果始终和有lockf语句时一样。经过分析,感觉是因为自己电脑运行速度太快了,输出五行文本信息,只需要极少的时间即可完成,因此不用等到子进程去争夺I/O资源,父进程已经输出完毕,把屏幕给释放了。为了验证这个猜想,我逐步增加输出的行数,当增加到500行时,发现了Parent和Child交叉出现的现象,如下图所示。
原文链接:https://blog.csdn.net/weixin_43074474/article/details/103404382