进程通讯—有名管道

Linux内核实现进程通讯

思路

FIFO 和 Pipe 一样,提供了双向进程间通信渠道。但要注意的是,无论是有名管道还 是匿名管道同一条管道只应用于单向通信,否则可能出现通信混乱(进程读到自己发的 数据)

采用库函数 mkfifo() 创建fifo有名管道.

函数说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <sys/types.h>
#include <sys/stat.h>
/**
* @brief 用于创建有名管道。该函数可以创建一个路径为 pathname 的 FIFO 专用文件,
mode 指定了 FIFO 的权限,FIFO 的权限和它绑定的文件是一致的。FIFO 和 pipe 唯一的
区别在于创建方式的差异。一旦创建了 FIFO 专用文件,任何进程都可以像操作文件一样
打开 FIFO,执行读写操作。
*
* @param pathname 有名管道绑定的文件路径
* @param mode 有名管道绑定文件的权限
* @return int
*/
int mkfifo(const char *pathname, mode_t mode);

管道发送端

创建fifo_write.cpp

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
#include <stdio.h>
#include "stdlib.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "unistd.h"
#include "fcntl.h"
#include "string.h"
#include "errno.h"

int main(int argc,char *argv[]) {
   char *pipe_path="/tmp/fifo";
   int resFifo=mkfifo(pipe_path,0664);
   if(resFifo!=0){
       perror("mkfifo error");
       exit(EXIT_FAILURE);
  }
   int fd= open(pipe_path,O_WRONLY);
   if(fd ==-1){
       perror("open error");
       exit(EXIT_FAILURE);
  }
   char buffer[100];
   ssize_t readNum;
   while((readNum= read(STDIN_FILENO,buffer,sizeof(buffer)))>0){
       write(fd,buffer, readNum);
  }
   if (readNum<0){
       perror("read error");
       close(fd);
       exit(EXIT_FAILURE);
  }
   printf("写入成功\n");
   return 0;
}

管道接收端

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
#include <stdio.h>
#include "stdlib.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "unistd.h"
#include "fcntl.h"
#include "string.h"
#include "errno.h"

int main(int argc,char *argv[]) {
   char *pipe_path="/tmp/fifo";
   int fd= open(pipe_path,O_RDONLY);
   if(fd ==-1){
       perror("open error");
       exit(EXIT_FAILURE);
  }
   char buffer[100];
   ssize_t readNum;
   while((readNum= read(fd,buffer,sizeof(buffer)))>0){
       write(STDOUT_FILENO,buffer, readNum);
  }
   if (readNum<0){
       perror("read error");
       close(fd);
       exit(EXIT_FAILURE);
  }
   printf("读取成功\n");
   return 0;
}

Makefile编译

1
2
3
4
5
6
CC :=gcc
fifo_read: fifo_read.cpp
-$(CC) -o $@ $^

fifo_write: fifo_write.cpp
-$(CC) -o $@ $^

注意

调用 open()打开有名管道时,flags 设置为 O_WRONLY 则当前进程用于向有名管道写 入数据,设置为 O_RDONLY 则当前进程用于从有名管道读取数据。设置为 O_RDWR 从技术 上是可行的,但正如上文提到的,此时管道既读又写很可能导致一个进程读取到自己发送 的数据,通信出现混乱。因此,打开有名管道时,flags 只应为 O_WRONLY 或 O_RDONLY。

内核为每个被进程打开的 FIFO 专用文件维护一个管道对象。当进程通过 FIFO 交换数 据时,内核会在内部传递所有数据,不会将其写入文件系统。因此,/tmp/myfifo 文件大 小始终为 0