Linux之进程间通信(一)---管道

进程是一个独立的资源分配单位,每个进程的资源都是独立的,有独立的用户地址空间,当有多个进程同时存在时,当有一个进程突然出现异常时,其它的进程是不受影响的.那么,有时候就需要不同进程的信息进行交换或传递才可以工作.所以就出现了进程间通信.


进程间通信的目的:
- 数据传输:一个进程需要将它的数据发送给另一个进程.
- 资源共享:多个进程之间共享同样的资源(比如:共享同一段内存).
- 通知事件:一个进程需要向其它进程发送消息,通知它们发生的事件.(比如:子进程在退出时需要向父进程发送自己的退出信息)
- 进程控制:有些进程需要完全控制另一个进程的执行.


匿名管道

实质:就是操作系统向内核申请的一段内存,这段内存不同的进程都可以访问.
1.匿名管道(pipe)


函数:
#include<unistd.h>
功能:创建一个无名管道.但是进程间必须有亲缘关系.
int pipe(int fd[2]);
fd:文件描述符数组.其中fd[0]表示读端,fd[1]表示写端(可将1看做一支笔,便于记忆~~~)
返回值:成功返回0,失败返回错误码.

创建管道失败的原因:
- 内存不够
- 文件描述符已达到上限,无法再打开文件描述符

简单的画一下管道的通信:
这里写图片描述
这里写图片描述
这里写图片描述


管道的特点:
- 管道是单向通信的,只能一端进行读操作,另一端进行写操作.如果想要双向通信,那么就要创建两个管道.
- 使用匿名管道的进程间具有亲缘关系.
- 管道自带同步与互斥事件.
- 进程退出,管道退出,管道的生命周期随进程.
- 管道是提供面向字节流的通信.


下面是代码实现:
1.父进程关闭读端,子进程关闭了写端.
父进程一直写,子进程一直读.

  1 #include<stdio.h>
  2 #include<string.h>                                                                                                     
  3 #include<unistd.h>
  4 #include<sys/wait.h>
  5 
  6 int main()
  7 {
  8     int fd[2];
  9     int ret = pipe(fd);
 10     if(ret == -1)
 11     {
 12         perror("pipe");
 13         return 1;
 14     }
 15     ret = fork();
 16     if(ret == 0)
 17     {
 18         //子进程关闭了写端
 19         close(fd[1]);
 20         char buf[100];
 21         while(1)
 22         {
 23             ssize_t size = read(fd[0],buf,sizeof(buf) - 1);
 24             if(size > 0)
 25             {
 26                 buf[size] = '\0';
 27                 printf("child sys:%s\n",buf);
 28             }
 29         }
 30     }
 31     else if(ret > 0)
 32     {
 33         //父进程关闭了读端
 34         close(fd[0]);
 35         const char* msg = "I am child....\n";
 36         while(1)
 37         {
 38             write(fd[1],msg,strlen(msg));
 39             sleep(1);
 40         }
 41     }
 42     int status = 0;
 43     wait(&status);
 44     printf("sig:%d\n",status&0x7F);
 45     return 0;
 46 }

程序运行结果
2.父进程关闭了写端,子进程关闭了读端.过来一会儿后,父进程也关闭了读端.而子进程却一直在写.那么此时子进程就会收到一个SIGPIPE信号,导致进程终止.

  1 #include<stdio.h>                                                                                                      
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<sys/wait.h>
  5 
  6 int main()
  7 {
  8     int fd[2];
  9     int ret = pipe(fd);
 10     if(ret == -1)
 11     {
 12         perror("pipe");
 13         return 1;
 14     }
 15     ret = fork();
 16     if(ret > 0)
 17     {
 18         //父进程关闭了写端
 19         close(fd[1]);
 20         char buf[100];
 21         int i = 0;
 22         while(i < 5)
 23         {
 24             ++i;
 25             ssize_t size = read(fd[0],buf,sizeof(buf) - 1);
 26             if(size > 0)
 27             {
 28                 buf[size] = '\0';
 29                 printf("child sys:%s\n",buf);
 30             }
 31         }
 32         close(fd[0]);
 33     }
 34     else if(ret == 0)
 35     {
 36         //子进程关闭了读端
 37         close(fd[0]);
 38         const char* msg = "I am child....\n";
 39         while(1)
 40         {
 41             write(fd[1],msg,strlen(msg));
 42             sleep(1);
 43         }
 44     }
 45     int status = 0;
 46     wait(&status);
 47     printf("sig:%d\n",status&0x7F);
 48     return 0;
 49 }

结果如图所示

如果此时,父进程没有关闭读端,而是子进程在写了一会儿以后关闭了自己的写端,那么父进程在读取时就像读到了文件的结尾一样.读端就会阻塞着去读取.


下面来讲述一下管道的自带同步互斥机制是什么意思?
在上述两个例子的基础上,再进行拓展:
情况1:读端不读,写端一直写
如果父进程fork()一个子进程后,父进程的读端打开却一直不读,而子进程的写端却一直在写.那么此时当管道被写满时,再进行写操作时就无法进行,就要一直阻塞的等待读取后空出位置才能写.
情况二:读端一直读,写端不写.
与情况一相似,那么此时读端就会一直阻塞的等待有东西写进管道.


命名管道

命名管道和匿名管道的区别:
1.命名管道:是一种特殊的文件存在于磁盘上;而匿名管道是内核中的一段内存.
2.命名管道可以用于任意进程间的通信;而匿名管道只能用于具有亲缘关系的进程间通信.
3.命名管道和匿名管道的创建与打开方式不同.命名管道当读写端都打开时,才会在内存中资源.

其它特性都与匿名管道相同.


创建命名管道的函数:
int mkfifo(const char* filename,mode_t mode);
注意:filename相同,文件相同.

简单的代码实例,两个进程间的通信,若需要请戳
该实例在两个文件同时执行时,在两个不同的会话中执行.就可以实现两个进程的通信.

猜你喜欢

转载自blog.csdn.net/yinghuhu333333/article/details/80647230