linux进程间通讯-命名管道

来源:互联网 发布:c语言中char是几位 编辑:程序博客网 时间:2024/05/23 17:54

1、什么是命名管道

  命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。
  有名管道是有名有形的,为了使用这种管道Linux中设立了一个专门的特殊文件系统–管道文件,它存在于文件系统中,任何进程可以在任何时候通过有名管道的路径和文件来访问管道,但是在磁盘上的只是一个节点,而文件的数据则只存在于内存缓冲页面中与普通管道一样

2、命名管道的使用

1. 命名管道的创建

  我们可以使用两下函数之一来创建一个命名管道,他们的原型如下:

int mkfifo(const char *filename, mode_t mode);  int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);

  这两个函数都能创建一个FIFO文件,注意是创建一个真实存在于文件系统中的文件,filename指定了文件名,而mode则指定了文件的读写权限。
  mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议在可能的情况下,尽量使用mkfifo而不是mknod。

2. 命名管道的访问

  与打开其他文件一样,FIFO文件也可以使用open调用来打开。注意,mkfifo函数只是创建一个FIFO文件,要使用命名管道还得将其打开。
  但是有两点要注意:
  1、就是程序不能以O_RDWR模式打开FIFO文件进行读写操作,其行为未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递。
  2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。

  打开FIFO文件通常有四种方式:

open(const char *path, O_RDONLY);//1  open(const char *path, O_RDONLY | O_NONBLOCK);//2  open(const char *path, O_WRONLY);//3  open(const char *path, O_WRONLY | O_NONBLOCK);//4 

  在open函数的调用的第二个参数中,你看到一个陌生的选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。
  open调用的阻塞是什么一回事呢?很简单,对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
  对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。

3、命名管道的安全问题

  试想这样一个问题,只使用一个FIFO文件,如果有多个进程同时向同一个FIFO文件写数据,而只有一个读FIFO进程在同一个FIFO文件中读取数据时,会发生怎么样的情况呢,会发生数据块的相互交错?
  为了解决这一问题,就是让写操作的原子化。怎样才能使写操作原子化呢?答案很简单,系统规定:在一个以O_WRONLY(即阻塞方式)打开的FIFO中, 如果写入的数据长度小于等待PIPE_BUF,那么或者写入全部字节,或者一个字节都不写入。如果所有的写请求都是发往一个阻塞的FIFO的,并且每个写请求的数据长度小于等于PIPE_BUF字节,系统就可以确保数据决不会交错在一起。

4、使用例子

  这里有两个源文件,一个fifowrite.c,它在需要时创建管道,然后向管道写入数据,数据由文件Data.txt提供,大小为10M,内容全是字符‘0’。另一个源文件为fiforead.c,它从FIFO中读取数据,并把读到的数据保存到另一个文件DataFormFIFO.txt中。

    /***fifowrite.c文件***/    #include <unistd.h>      #include <stdlib.h>      #include <fcntl.h>      #include <limits.h>      #include <sys/types.h>      #include <sys/stat.h>      #include <stdio.h>      #include <string.h>      int main()      {          const char *fifo_name = "/tmp/my_fifo";          int pipe_fd = -1;          int data_fd = -1;          int res = 0;          const int open_mode = O_WRONLY;          int bytes_sent = 0;          char buffer[PIPE_BUF + 1];          if(access(fifo_name, F_OK) == -1)          {              //管道文件不存在              //创建命名管道              res = mkfifo(fifo_name, 0777);              if(res != 0)              {                  fprintf(stderr, "Could not create fifo %s\n", fifo_name);                  exit(EXIT_FAILURE);              }          }          printf("Process %d opening FIFO O_WRONLY\n", getpid());          //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件          pipe_fd = open(fifo_name, open_mode);          data_fd = open("Data.txt", O_RDONLY);          printf("Process %d result %d\n", getpid(), pipe_fd);          if(pipe_fd != -1)          {              int bytes_read = 0;              //向数据文件读取数据              bytes_read = read(data_fd, buffer, PIPE_BUF);              buffer[bytes_read] = '\0';              while(bytes_read > 0)              {                  //向FIFO文件写数据                  res = write(pipe_fd, buffer, bytes_read);                  if(res == -1)                  {                      fprintf(stderr, "Write error on pipe\n");                      exit(EXIT_FAILURE);                  }                  //累加写的字节数,并继续读取数据                  bytes_sent += res;                  bytes_read = read(data_fd, buffer, PIPE_BUF);                  buffer[bytes_read] = '\0';              }              close(pipe_fd);              close(data_fd);          }          else              exit(EXIT_FAILURE);          printf("Process %d finished\n", getpid());          exit(EXIT_SUCCESS);      }  
    /***fiforead.c文件***/    #include <unistd.h>      #include <stdlib.h>      #include <stdio.h>      #include <fcntl.h>      #include <sys/types.h>      #include <sys/stat.h>      #include <limits.h>      #include <string.h>      int main()      {          const char *fifo_name = "/tmp/my_fifo";          int pipe_fd = -1;          int data_fd = -1;          int res = 0;          int open_mode = O_RDONLY;          char buffer[PIPE_BUF + 1];          int bytes_read = 0;          int bytes_write = 0;          //清空缓冲数组          memset(buffer, '\0', sizeof(buffer));          printf("Process %d opening FIFO O_RDONLY\n", getpid());          //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名          pipe_fd = open(fifo_name, open_mode);          //以只写方式创建保存数据的文件          data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);          printf("Process %d result %d\n",getpid(), pipe_fd);          if(pipe_fd != -1)          {              do              {                  //读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中                  res = read(pipe_fd, buffer, PIPE_BUF);                  bytes_write = write(data_fd, buffer, res);                  bytes_read += res;              }while(res > 0);              close(pipe_fd);              close(data_fd);          }          else              exit(EXIT_FAILURE);          printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);          exit(EXIT_SUCCESS);      }  
0 0
原创粉丝点击