进程间通信( 管道 )

Posted 李憨憨_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程间通信( 管道 )相关的知识,希望对你有一定的参考价值。

进程间通信( 管道 )


前言

  进程间通信: 进程间的几种通信方式的学习(管道, 共享内存, 消息队列, 信号量)
   为什么进程间无法直接进行通信?
    因为进程之间具有独立性, 每个进程访问的都是自己的独立的虚拟地址空间, 使用的都是虚拟地址, 通过页表映射到物理内存. 因此就算将数据的内存地址交给其他进程, 其他进程也无法访问(当然其实也无法直接给), 只能通过操作系统提供的几种方式来进行.
本质上来说是操作系统为多个进程提供了一处公共的数据传输媒介(内存)。

一、管道

管道用于传输数据
特性半双工通信–可以选择方向的单向通信
本质管道就是内核中的一块缓冲区(内存)
分类匿名管道,命名管道
  匿名管道:管道没有名字--缓冲区没有标识符;
   只能用于具有亲缘关系的进程间通信
 命名管道:管道具有标识符;
   可用于同一主机上的任意进程间通信
 匿名管道:
int pipe(int pipefd[2])

在这里插入图片描述

在这里插入图片描述


 pipefd[0]--读
 pipefd[1]--写
 返回值:成功返回0;失败返回-1;
  匿名管道一定要在创建子进程之前创建

代码如下(示例):

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <string.h>
  5 int main()
  6 {
  7     int pipefd[2];
  8     int ret = pipe(pipefd);
  9     if(ret < 0){
 10         perror("pipe error");
 11         return -1;
 12     }
 13     pid_t pid = fork();
 14     if(pid < 0){
 15         perror("fork error");
 16         exit(-1);
 17     }else if(pid == 0){
 18         //child
 19         char buf[1024] = {0};
 20         int ret = read(pipefd[0], buf, 1023);
 21         if(ret < 0){
 22             perror("read error");
 23             return -1;
 24         }
 25         printf("buf:%s\\n", buf);
 26     }else{
 27         //parent
 28         char *ptr = "今天好困呐!!\\n";
 29         int ret = write(pipefd[1], ptr, strlen(ptr));
 30         if(ret < 0){
 31             perror("write error");
 32             return -1;
 33         }
 34         printf("写入成功\\n");
 35     }
 36     return 0;
 37 }


结果:
在这里插入图片描述

 通常情况下, 父子进程运行的顺序是不一定的, 一般情况下, 父进程先于子进程运行. 但在这里, 子进程明显先于父进程运行, 这是由于管道的特性所决定的.
管道中的读取特性 :
  1.若管道中没有数据则read会阻塞,管道中数据满了,则write会阻塞。
  2.若管道的所有读端被关闭,则write会触发异常--导致进程退出;
  3.若管道的所有写端被关闭,则read读完所有数据后返回0;




 1.若管道中没有数据则read会阻塞,管道中数据满了,则write会阻塞。

在这里插入图片描述



 2.若管道的所有读端被关闭,则write会触发异常–导致进程退出;
在这里插入图片描述




 3.若管道的所有写端被关闭,则read读完所有数据后返回0;
在这里插入图片描述




 shell中的管道符: | --连接两个命令,将前边命令的结果交给后边命令进行处理
ps -ef | grep pipe模拟实现
ps -ef:将系统中的进程信息写入标准输出
grep pipe:不断从标准输入读取数据,进行匹配过滤.
使用管道连接两个命令之后,将ps进程原本要写入标准输出的数据,不再 写入标准输出,而是通过管道写入端写入管道。grep进程原本从标准输入读取数据,修改为从管道读取端读取数据,进行匹配过滤
1.当前进程作为shell进程,所有命令都是子进程完成的;
2.创建匿名管道;
3.创建一个子进程,先进行标准输出重定向到管道写入端,然后经过程序替换运行ps -ef指令;
4.创建一个子进程,先进行标准输入重定向到管道读取端,然后经过程序替换运行grep pipe指令;
5.子进程等待两个父进程退出;

注意:在进程中把不使用的管道一端一定要关掉,防止一些不必要的问题

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <sys/wait.h>
  5 int main()
  6 {
  7     int pipefd[2];
  8     int ret = pipe(pipefd);
  9     if(ret < 0){
 10         perror("pipe error");
 11         return -1;
 12     }
 13     if(fork() == 0){
 14         dup2(pipefd[1], 1);
 15         execlp("ps", "ps", "-ef", NULL);
 16         exit(-1);
 17     }
 18     if(fork() == 0){
 19         dup2(pipefd[0], 0);
 20         execlp("grep","grep", "pipe", NULL);
 21         exit(-1);
 22     }
 23     wait(NULL);
 24     wait(NULL);
 25     return 0;
 26 }

在这里插入图片描述


 命名管道:内核中的缓冲区具有标识符,能够被其他进程找到,因此可用于同一主机上的任意进程间通信
命名管道具有标识符,这个标识符是可见于文件系统的管道文件,但是要注意,这个文件只是一个标识符,只是为了让多个进程 可以通过打开同一个管道文件进而访问内核中的同一块缓冲区进行通信而已。

代码操作:

int mkfifo(const char*pathname,mode_t mode);

在这里插入图片描述

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <sys/stat.h>
  5 #include <errno.h>
  6 #include <fcntl.h>
  7 int main()
  8 {
  9     umask(0);
 10     char *fifo_name = "./test.fifo";
 11     int ret = mkfifo(fifo_name, 0664);
 12     if(ret < 0 && errno != EEXIST){
 13         perror("mkfifo error");
 14         return -1;
 15     }
 16     //open(文件名, 打开方式, 权限)
 17     int fd = open(fifo_name, O_RDONLY);
 18     if(fd < 0){
 19         perror("open error");
 20         return -1;
 21     }
 22     printf("open fifo success\\n");
 23     return 0;
 24 }

程序阻塞
在这里插入图片描述


 命名管道的打开特性:

  如果命名管道以只读的方式打开,会阻塞直到这个管道被以写的方式打开;
  如果命名管道以只写的方式打开,会阻塞直到这个管道被以读的方式打开;

在这里插入图片描述
写端 :

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <errno.h>
  5 #include <fcntl.h>
  6 #include <sys/stat.h>
  7 #include <string.h>
  8 int main()
  9 {
 10     umask(0);
 11     char *fifo_name = "./test.fifo";
 12     int ret = mkfifo(fifo_name, 0664);
 13     if(ret < 0 && errno != EEXIST){
 14         perror("mkfifo error");
 15         return -1;
 16     }
 17     int fd = open(fifo_name, O_WRONLY);
 18     if(fd < 0){
 19         perror("open error");
 20         return -1;
 21     }
 22     while(1){
 23         char buf[1024] = {0};
 24         scanf("%s", buf);
 25         int ret = write(fd, buf, strlen(buf));
 26         if(ret < 0){
 27             perror("write error");
 28             return -1;
 29         }
 30     }
 31     close(fd);
 32     return 0;
 33 }

在这里插入图片描述

以上是关于进程间通信( 管道 )的主要内容,如果未能解决你的问题,请参考以下文章

Windows进程间通信—命名管道

linux c之通过管道实现兄弟间进程通信:

概述Linux进程间通信方式

利用管道实现进程间通信

Linux系统编程--进程间通信 ---管道篇

Linux_Centos进程间通信_管道(匿名管道_命名管道)