• 管道和FIFO


    1. 管道(pipe)
          管道在Unix及Linux进程间通信是最基础的,很容易理解。管道就像一个自来水管,一端注入水,一端放出水,水只能在一个方向上流动,而不能双向流动。管道是典型的单向通信,即计算机网络中所说的“半双工”。管道又名匿名管道,所以只能用在具有公共祖先的进程之间使用,通常使用在父子进程之间通信。通常是父进程创建一个管道,然后fork一个子进程,此后父子进程共享这个管道进行通信。
      管道由pipe函数创建,函数原型如下:
          #include<unistd.h>
          int  pipe(int fd[2]); 成功返回0,否则返回-1;参数fd返回两个文件描述符,fd[0]为读,fd[1]为写,fd[1]的输入是fd[0]的输出。即fd[0]对应读端,fd[1]对应写端。
          举例说明一下管道的用法:模拟client-server通信过程,父进程模拟client,子进程模拟server。server向client发送一个字符串,client接收到输出到屏幕。
      
    View Code
     1 #include <unistd.h>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <errno.h>
     6 #include <string.h>
     7 
     8 int main()
     9 {
    10     int fd[2];
    11     pid_t childpid;
    12     char buf[100];
    13 
    14     memset(buf,0,100);
    15     //创建一个管道
    16     if(pipe(fd) == -1)
    17     {
    18         perror("pipe() error");
    19         exit(-1);
    20     }
    21     //创建一个子进程
    22     childpid = fork();
    23     if(childpid == 0)
    24     {
    25         printf("server input a message : ");
    26         scanf("%s",buf);
    27         //关闭读端
    28         close(fd[0]);
    29         write(fd[1],buf,strlen(buf));
    30         exit(0);
    31     }
    32     if(childpid == -1)
    33     {
    34         perror("fork() error");
    35         exit(-1);
    36     }
    37     //父进程关闭写端
    38     close(fd[1]);
    39     read(fd[0],buf,100);
    40     printf("client read a message: %s\n",buf);
    41     waitpid(childpid,NULL,0);
    42     return 0;
    43 }

    程序执行结果如下:

    上面程序的细节问题在于子进程需要关闭读端,父进程需要关闭写端。因为管道最早提出时候是单向,虽然现在有些系统提供全双工的管道。那么如何采用管道实现双向通信呢?很显然我们需要两个管道,控制两个不同的数据流向。现在有模拟一个Client和Server双向通信的过程,Client与Server之间可以相互发送和接收信息。此时需要两个管道进行模拟,管道1模拟Server写Client读数据流向,管道2模拟Client写Server读数据流向。代码如下所示:

    View Code
     1 #include <unistd.h>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <errno.h>
     6 #include <string.h>
     7 
     8 int main()
     9 {
    10     int fd1[2],fd2[2];
    11     pid_t childpid;
    12     char buf[100];
    13 
    14     memset(buf,0,100);
    15     
    16     if(pipe(fd1) == -1)
    17     {
    18         perror("pipe() error");
    19         exit(-1);
    20     }
    21     if(pipe(fd2) == -1)
    22     {
    23         perror("pipe() error");
    24         exit(-1);
    25     }
    26     childpid = fork();
    27     if(childpid == 0)
    28     {
    29         printf("Server input a message : ");
    30         gets(buf);
    31         close(fd1[0]);
    32         close(fd2[1]);
    33         write(fd1[1],buf,strlen(buf));
    34         read(fd2[0],buf,100);
    35         printf("Server received message from client:%s\n",buf);
    36         exit(0);
    37     }
    38     if(childpid == -1)
    39     {
    40         perror("fork() error");
    41         exit(-1);
    42     }
    43     close(fd1[1]);
    44     close(fd2[0]);
    45     read(fd1[0],buf,100);
    46     printf("Client receive a message from server: %s\n",buf);
    47     printf("Client input a message : ");
    48     gets(buf);
    49     write(fd2[1],buf,strlen(buf));
    50     waitpid(childpid,NULL,0);
    51     return 0;
    52 }

    程序执行结果如下:

      2 FIFO(first in first out)

      FIFO又名有名管道,相对于上述管道而言。管道没有名字,因此只能在具有共同祖先进程的各个进程之间通信,无法在无亲缘关系的两个进程之间创建一个管道进行通信。为此有了FIFO,类似管道,也是一个单向(半双工)数据流,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO有mkfifo函数创建。

    #include <sys/types.h>

    #include <sys/stat.h>

    int mkfifo(const char *pathname,mode_t mode); 成功返回0,出错返回-1。pathname是一个普通的路径名,是FIFO的名字,mode指定文件的权位。

    在创建FIFO后,必须打开来读或者打开来写,不能打开来既读既写(因为FIFO是半双工)。现在采用FIFO实现上面的第二个例子,代码如下:

    View Code
     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <fcntl.h>
     9 
    10 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
    11 
    12 #define FIFO1   "/tmp/fifo.1"
    13 #define FIFO2   "/tmp/fifo.2"
    14 
    15 int main()
    16 {
    17     int readfd,writefd;
    18     pid_t   childpid;
    19     char buf[100];
    20     memset(buf,0,100);
    21     //创建FIFO
    22     if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST))
    23     {
    24         perror("mkfifo() error");
    25         exit(-1);
    26     }
    27     if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST))
    28     {
    29         unlink(FIFO1);
    30         perror("mkfifo() error");
    31         exit(-1);
    32     }
    33      //创建子进程
    34     childpid = fork();
    35     if(childpid == 0)
    36     {
    37         readfd = open(FIFO1,O_RDONLY,0);
    38         writefd = open(FIFO2,O_WRONLY,0);
    39         printf("Server input a message: ");
    40         gets(buf);
    41         write(writefd,buf,strlen(buf));
    42         read(readfd,buf,100);
    43         printf("Server received a message from Client: %s\n",buf);
    44         exit(0);
    45     }
    46     if(childpid == -1)
    47     {
    48         perror("frok() error");
    49         exit(-1);
    50     }
    51      //防止死锁,注意顺序
    52     writefd = open(FIFO1,O_WRONLY,0);
    53     readfd = open(FIFO2,O_RDONLY,0);
    54     read(readfd,buf,100);
    55     printf("Client received a message form Server: %s\n",buf);
    56     printf("Client input a mesage: ");
    57     gets(buf);
    58     write(writefd,buf,strlen(buf));
    59     waitpid(childpid,NULL,0);
    60     close(readfd);
    61     close(writefd);
    62     unlink(FIFO1);
    63     unlink(FIFO2);
    64     return 0;
    65 }

    运行结果如下:

     上面的程序当中父进程打开FIFO的顺序不能颠倒,否则会造成死锁。因为在当前没有任何进程打开某个FIFO来写的时候,打开该FIFO来读的进程将会阻塞。交换父进程中两个open的调用顺序后,父子进程都将打开同一个FIFO进行读,而当前没有任何进程来打开该文件进行写,于是父子进程都阻塞,造成死锁。

    下面采用FIFO实现无亲缘关系的两个进程之间的通信。Client与Server是两个独立的进程。

    View Code
     1 //公共同文件fifo.h
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <unistd.h>
     5 #include <sys/types.h>
     6 #include <sys/stat.h>
     7 #include <errno.h>
     8 #include <string.h>
     9 #include <fcntl.h>
    10 
    11 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
    12 
    13 #define FIFO1   "/tmp/fifo.1"
    14 #define FIFO2   "/tmp/fifo.2"
    View Code
     1 //server进程 server.c 
     2 #include "fifo.h"
     3 
     4 int main()
     5 {
     6     int readfd,writefd;
     7     pid_t   childpid;
     8     char buf[100];
     9     memset(buf,0,100);
    10     //创建FIFO
    11     if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST))
    12     {
    13         perror("mkfifo() error");
    14         exit(-1);
    15     }
    16     if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST))
    17     {
    18         unlink(FIFO1);
    19         perror("mkfifo() error");
    20         exit(-1);
    21     }
    22     readfd = open(FIFO1,O_RDONLY,0);
    23     writefd = open(FIFO2,O_WRONLY,0);
    24     printf("Server input a message: ");
    25     gets(buf);
    26     write(writefd,buf,strlen(buf));
    27     read(readfd,buf,100);
    28     printf("Server received a message from Client: %s\n",buf);
    29     return 0;
    30 }
    View Code
     1 //client进程 client。c
     2 #include "fifo.h"
     3 
     4 int main()
     5 {
     6     int readfd,writefd;
     7     pid_t   childpid;
     8     char buf[100];
     9     memset(buf,0,100);
    10     //创建FIFO
    11     if((mkfifo(FIFO1,FILE_MODE) < 0) && (errno != EEXIST))
    12     {
    13         perror("mkfifo() error");
    14         exit(-1);
    15     }
    16     if((mkfifo(FIFO2,FILE_MODE) < 0) && (errno != EEXIST))
    17     {
    18         unlink(FIFO1);
    19         perror("mkfifo() error");
    20         exit(-1);
    21     }
    22 
    23      //防止死锁,注意顺序
    24     writefd = open(FIFO1,O_WRONLY,0);
    25     readfd = open(FIFO2,O_RDONLY,0);
    26     read(readfd,buf,100);
    27     printf("Client received a message form Server: %s\n",buf);
    28     printf("Client input a mesage: ");
    29     gets(buf);
    30     write(writefd,buf,strlen(buf));
    31     close(readfd);
    32     close(writefd);
    33     unlink(FIFO1);
    34     unlink(FIFO2);
    35     return 0;
    36 }

    先执行server进程,然后执行client进程:结果如下:

    以上介绍了管道和FIFO的操作方法。

    参考资料:

    《Unix环境高级编程》

  • 相关阅读:
    有关数据库锁表
    order by 排序的数字异常
    索引建议
    有关文件在浏览器中打开window.open
    vscode 常用快捷键
    jQuery中preventDefault()、stopPropagation()、return false 之间的区别
    理解Linux系统负荷(WDCP系统后台参数之一)
    JavaScript toString() 方法
    1-4:CSS3课程入门之文本新增属性
    1-3:CSS3课程入门之伪类和伪元素
  • 原文地址:https://www.cnblogs.com/Anker/p/2810142.html
Copyright © 2020-2023  润新知