Linux进程通信之管道解析

系统 Linux
所谓的管道,也就是内核里面的一串缓存,从管道的一段写入的数据,实际上是缓存在内核中的,令一端读取,也就是从内核中读取这段数据。

 [[409600]]

概述

管道是 UNIX系统 IPC的最古老的形式,所有的UNIX系统都提供此种通信。所谓的管道,也就是内核里面的一串缓存,从管道的一段写入的数据,实际上是缓存在内核中的,令一端读取,也就是从内核中读取这段数据。对于管道传输的数据是无格式的流且大小受限。对于管道来说,也分为匿名管道和命名管道,其中命名管道也被叫做 FIFO,下面则分别阐述这两种管道。

匿名管道

默认情况下,在 Shell命令执行过程中,任何一个命令都有一个标准输入设备(键盘)、标准输出设备(显示器)和标准输出设备(显示器),使用管道"|"可以将两个命令连接起来,从而改变标准的输入输出方式,下面是在 Linux 端运行命令行的一个截图:

上述命令中的意思也就是,将ls命令得到的结果作为 grep tags命令的输入。

连接输入输出的中间设备即为一个管道文件,综上,也就是说使用管道可以将一个命令的输出作为另一个命令的输入(在运行的时候,一个命令将创建一个进程),而这种管道是临时的,命令执行完毕之后就会自动消失,这类管道称为无名管道。

匿名管道例子

匿名管道在使用前要先创建,其函数的声明如下:

  1. extern int pipe (int __pipedes[2]); 

此函数的参数是一个整型数组,如果执行成功,pipe 将存储两个整型文件描述符于__pipedes[0]和__pipedes[1]中,他们分别指向管道的两端。如果系统调用失败,则返回 -1。

读无名管道,该函数的声明如下:

  1. extern ssize_t read (int __fd, void *__buf, size_t __nbytes); 

第一个参数fd为打开的文件描述符,buf为读出数据的存储位置,nbytes为读取数据的大小,调用 read 函数将从 fd 指向的文件描述符指定的打开文件中宏读 n 字节到 buf 指向的缓冲区内。

如果试图向已经填满的管道写入,系统会自动阻塞。一个管道不能同时被两个进程打开。

  1. extern ssize_ t write(int __fd, __const void *__buf, size_t __n); 

从 buf指向的缓冲区中向管道中写入nbytes字节,且每次写入的内容都附件在管道的末端。

那要如何使用管道在两个进程之间通信呢,我们可以使用 fork()创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个fd[0]与fd[1],两个进程就可以通过各自的fd写入和读取同一个管道文件实现进程通信了,具体原理如下所示:

具体的例子如下所示:

  1. #include<unistd.h> 
  2. #include<stdio.h> 
  3. #include<stdlib.h> 
  4.  
  5. int main(int argc, char *argv[]) 
  6.     pid_t pid; 
  7.     int temp
  8.     int pipedes[2]; 
  9.     char s[14] = "test message!"
  10.     char d[14]; 
  11.  
  12.     if (pipe(pipedes) == -1) // 创建管道 
  13.     { 
  14.         perror("pipe"); 
  15.         exit(EXIT_FAILURE); 
  16.     } 
  17.  
  18.     if (pid == fork() == -1) 
  19.     { 
  20.         perror("fork"); 
  21.         exit(EXIT_FAILURE); 
  22.     } 
  23.     else if (pid == 0)      // 子进程 
  24.     { 
  25.         printf("now,write data to pipe\n"); 
  26.         if (write(pipedes[1], s, 14) == -1)   // 写数据到管道 
  27.         { 
  28.             perror("write"); 
  29.             exit(EXIT_FAILURE); 
  30.         } 
  31.         else 
  32.         { 
  33.             printf("the written data is:%s\n",s); 
  34.             exit(EXIT_SUCESS); 
  35.         } 
  36.     } 
  37.     else if (pid > 0)     // 父进程 
  38.     { 
  39.         slepp(2); 
  40.         printf("now, read from pipe\n"); 
  41.         if ((read(pipedes[0], d, 14)) == -1) 
  42.         { 
  43.             perror("read"); 
  44.             exit(EXIT_FAILURE); 
  45.         } 
  46.         printf("the data from pipe is:%s\n",d); 
  47.     } 
  48.     return 0; 

代码运行的结果如下所示:

命名管道

命名管道又被称之为是 FIFO ,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要又一个共同创建了他们的祖先进程,但是,通过 FIFO ,不相关的进程也能交换数据。

首先,介绍下是如何创建命名管道的:

  1. extern int mkfifo (__const char *__path, __mode_t __mode); 

mkfifo会根据参数建立特殊的有名管道文件,该文件必须不存在,而参数mode为该文件的权限。

下面是一个使用命名管道进行进程间通信的例子,例子分为两个程序,分别是读部分和写部分,首先看先往管道写数据的代码,代码如下所示:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <sys/stat.h>  
  5. #include <sys/types.h>  
  6. #include <unistd.h>  
  7.  
  8. int main()  
  9. {  
  10.     int fd;  
  11.  
  12.     // FIFO file path  
  13.     char * myfifo = "/tmp/myfifo";  
  14.  
  15.     // Creating the named file(FIFO)  
  16.     // mkfifo(<pathname>, <permission>)  
  17.     mkfifo(myfifo, 0666);  
  18.  
  19.     char arr1[80], arr2[80];  
  20.     while (1)  
  21.     {  
  22.         // Open FIFO for write only  
  23.         fd = open(myfifo, O_WRONLY);  
  24.         printf("The fd is:%d\n",fd); 
  25.  
  26.         // Take an input arr2ing from user.  
  27.         // 80 is maximum length  
  28.         fgets(arr2, 80, stdin);  
  29.  
  30.         // Write the input arr2ing on FIFO  
  31.         // and close it  
  32.         write(fd, arr2, strlen(arr2)+1);  
  33.         close(fd);  
  34.  
  35.         // Open FIFO for Read only  
  36.         fd = open(myfifo, O_RDONLY);  
  37.  
  38.         // Read from FIFO  
  39.         read(fd, arr1, sizeof(arr1));  
  40.  
  41.         // Print the read message  
  42.         printf("User2: %s", arr1);  
  43.         close(fd);  
  44.     }  
  45.     return 0;  

然后是先往管道读数据的代码,代码如下所示:

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <fcntl.h>  
  4. #include <sys/stat.h>  
  5. #include <sys/types.h>  
  6. #include <unistd.h>  
  7.  
  8. int main()  
  9. {  
  10.     int fd1;  
  11.  
  12.     // FIFO file path  
  13.     char * myfifo = "/tmp/myfifo";  
  14.  
  15.     char str1[80], str2[80];  
  16.     while (1)  
  17.     {  
  18.         // First open in read only and read  
  19.         fd1 = open(myfifo,O_RDONLY);  
  20.         printf("The fd is:%d\n",fd1); 
  21.         read(fd1, str1, 80);  
  22.  
  23.         // Print the read string and close  
  24.         printf("User1: %s", str1);  
  25.         close(fd1);  
  26.  
  27.         // Now open in write mode and write  
  28.         // string taken from user.  
  29.         fd1 = open(myfifo,O_WRONLY);  
  30.         fgets(str2, 80, stdin);  
  31.         write(fd1, str2, strlen(str2)+1);  
  32.         close(fd1);  
  33.     }  
  34.     return 0;  
  35. }  

下面是代码运行的一个结果:

说明一下,就是说当运行 write程序的时候,会创建fifo文件,命名管道,然后,在 write文件中就执行open操作,但是,这里存在的一个问题就是,因为在运行 write程序的时候,没有进程打开读端,也就阻塞了 open函数的运行,只有运行read操作,以读的方式读取管道的数据,这样才能使得write中的open函数继续执行。

综上,也就是命名管道在进程中通信的一个例子。

小结

上述就是本次进程通信中关于管道的相关内容,其中就包括匿名管道以及命名管道,他们之间存在着差别吗,也各有各的应用,本次的分享就到这里啦~

本文转载自微信公众号「wenzi嵌入式软件」,可以通过以下二维码关注。转载本文请联系wenzi嵌入式软件公众号。

 

责任编辑:武晓燕 来源: wenzi嵌入式软件
相关推荐

2021-09-30 10:45:33

Linux进程通信

2019-05-13 10:00:41

Linux进程间通信命令

2010-03-02 10:27:56

Linux进程状态

2010-03-02 10:43:45

Linux进程状态

2010-01-05 10:00:48

Linux进程间通信

2011-06-22 16:50:09

Qt 进程 通信机制

2010-01-21 11:22:35

Linux多线程同步

2023-03-05 16:12:41

Linux进程线程

2011-06-24 14:01:34

Qt QCOP 协议

2018-01-12 14:35:00

Linux进程共享内存

2017-06-19 13:36:12

Linux进程消息队列

2009-12-24 14:47:42

Linux系统进程

2011-06-22 17:27:19

QT 进程通信

2021-02-14 21:05:05

通信消息系统

2023-03-02 23:50:36

Linux进程管理

2009-02-23 15:55:29

ASP.NET.NET性能提升

2011-01-11 13:47:27

Linux管理进程

2011-08-08 10:02:55

iPhone开发 进程 通信

2019-05-08 11:10:05

Linux进程语言

2018-05-30 13:58:02

Linux进程通信
点赞
收藏

51CTO技术栈公众号