Linux/Unix进程间的通信之命名管道

目录

 

管道(fifo)

命名管道的定义和特点

管道与命名管道的区别

说明

代码示例

运行结果


  • 管道(fifo)

管道是一种两个进程间进行单向通信的机制。因为管道传递数据的单向性,管道又称为半双工管道。管道的这一特点决定了器使用的局限性。管道是Linux支持的最初Unix IPC形式之一,具有以下特点:

  1. 数据只能由一个进程流向另一个进程(其中一个读管道,一个写管道);如果要进行双工通信,需要建 立两个管道;
  2. 管道只能用于父子进程或者兄弟进程间通信。,也就是说管道只能用于具有亲缘关系的进程间通信。
  • 命名管道的定义和特点

  POSIX标准中的FIFO又名有名管道或命名管道。我们知道前面讲述的POSIX标准中管道是没有名称的,所以它的最大劣势是只能用于具有亲缘关系的进程间的通信。FIFO最大的特性就是每个FIFO都有一个路径名与之相关联,从而允许无亲缘关系的任意两个进程间通过FIFO进行通信。所以,FIFO的两个特性:

  1. 和管道一样,FIFO仅提供半双工的数据通信,即只支持单向的数据流;
  2. 和管道不同的是,FIFO可以支持任意两个进程间的通信。
  • 管道与命名管道的区别

 前面讲到的未命名的管道只能在两个具有亲缘关系的进程之间通信,通过命名管道(Named PiPe)FIFO,不相关的进程也能 交换数据。FIFO不同于管道之处在于它提供一个路径与之关联,以FIFO的文件形式存在于系统中。它在磁盘上有对应的节点,但 没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建 立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被 进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。

  • 说明

下面这个例程创建了两个掩藏的命名管道文件(.fifo1和.fifo2)在不同的进程间进行双向通信。该程序需要运行两 次(即两个进程),其中进程0(flag=0)从标准输入里读入数据后通过命名管道2(.fifo2)写入数据给进程 1(flag=1);而进程1(flag=1)则从标准输入里读入数据后通过命名管道1(.fifo1)写给进程0,这样使用命名管 道实现了一个进程间聊天的程序。

flag =0

 

 falg = 1

  • 代码示例

#include <stdio.h>
#include <string.h> 
#include <unistd.h>
#include <errno.h>
#include <fcntl.h> 
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <libgen.h> 
#include <stdlib.h>
#define FIFO_FILE1      ".fifo1"
#define FIFO_FILE2      ".fifo2"
#define BUF_SIZE        1024
int        g_stop = 0;
void signal_pipe(int   signum)  //捕捉SIGPIPE信号
{
    if (SIGPIPE == signum)
    {
        printf("Get pipe broken signal and let porgrame exit...\n");
        g_stop = 1;
    }

}
int main(int argc, char *argv[])
{
    int        fdr_fifo = -1;
    int        fdw_fifo = -1;
    int        rv = -1;
    fd_set     rdset;
    char       buf[BUF_SIZE];
    int        flag  = -1;
    if (argc != 2)
    {
        printf("Usage: %s[0/1]\n", basename(argv[0]));
        printf("This program need run twice, first time is running with[0] and the second time is runing with[1]...\n");

        return -1;
    }
    flag = atoi(argv[1]); 

    if (access(FIFO_FILE1, F_OK)) //判断FIFO_FILE1 是否存在
    {
        printf("Fifo file \"%s\" not exist and will create it now.\n", FIFO_FILE1);
        mkfifo(FIFO_FILE1, 06666);
    }

    if (access(FIFO_FILE2, F_OK))
    {
        printf("Fifo file \"%s\" not exist and will create it now.\n", FIFO_FILE2);
        mkfifo(FIFO_FILE2, 06666);
    }

    signal(SIGPIPE, signal_pipe);  ///注册信号函数

    if (0 == flag)
    {
        /*
         *   在这里打开的命名管道FIFO_FILE1默认是阻塞模式,
         *   如果命名管道的写端不打开open会一直阻塞在这里;
         *   
         *   如果在这里打开FIFO_FILE1的读端,则另一个程序必须打开FIFO_FILE1的写端,
         *   否则会出现死锁,反之亦然
         *
         * */
        printf("Start open '%s'for read and it will bolcked here untill write pipe opened...\n", FIFO_FILE1);
        if ( (fdr_fifo = open(FIFO_FILE1, O_RDONLY)) < 0) //以只读的形式打开FIFO_FILE1的读端
        {
            printf("Open fifo[%s] for read error: %s\n", FIFO_FILE1, strerror(errno));
            return -1;

        }

        printf("Start open '%s' for write ...\n", FIFO_FILE2);
        if ( (fdw_fifo = open(FIFO_FILE2, O_WRONLY)) < 0)//以只写的形式打开FIFO_FILE2的写端
        {
            printf("Open fifo[%s] for write error: %s\n", FIFO_FILE2, strerror(errno));
            return -1;

        }
    }
    else 
    {
        printf("Start open '%s'for write and it will bolcked here untill write pipe opened...\n", FIFO_FILE1);
        if ( (fdw_fifo = open(FIFO_FILE1, O_WRONLY)) < 0) //以只写的形式打开FIFO_FILE1的写端
        {
            printf("Open fifo[%s] for write error: %s\n", FIFO_FILE1, strerror(errno));
            return -1;

        }

        printf("Start open '%s' for read ...\n", FIFO_FILE2);
        if ( (fdr_fifo = open(FIFO_FILE2, O_RDONLY)) < 0)//以只读的形式打开FIFO_FILE2的读端
        {
            printf("Open fifo[%s] for read error: %s\n", FIFO_FILE2, strerror(errno));
            return -1;
        }
    }

    printf("Start chating with another program now, enter the messsage you want to send:\n");
    while(!g_stop)
    {
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset); //标准输入加入道select的监听列表
        FD_SET(fdr_fifo, &rdset);     //命名管道的读端加入道select的监听列表

        rv = select(fdr_fifo + 1, &rdset, NULL, NULL, NULL);//使用select的多路复用分别监听标准输入和命名管道的读端
        if (rv <= 0)
        {
            printf("Select get timeout or error: %s\n", strerror(errno));

            continue;
        }

        if (FD_ISSET(fdr_fifo, &rdset)) //如果是fdr_fifo事件到来,则进行命名管道的读操作
        {
            memset(buf, 0, BUF_SIZE);
            rv  = read(fdr_fifo, buf,sizeof(buf));
            if (  rv< 0)
            {
                printf("Read form fifo get error: %s\n", strerror(errno));
                break;
            }
            else if (0 == rv)
            {
                printf("Another side of fifo get closed and program will exit now.\n");
                break;
            }

            printf("<---------%s\n", buf);
        }
        if (FD_ISSET(STDIN_FILENO, &rdset)) //如果是标准输入的事件发生,则进行写操作
        {
            memset(buf, 0, BUF_SIZE);
            fgets(buf, BUF_SIZE, stdin);
            write(fdw_fifo, buf, strlen(buf));

        }
    }

    printf("The fifo file will delete\n");
    system("rm -r .fifo*");//删除产生的fifo文件
    return 0;
}

注:本代码涉及到select的多路复用的使用,大家有不懂的可以参考博客:https://blog.csdn.net/qq_44045338/article/details/104879888

  • 运行结果

  1. 运行时没有输入flag: 1/0:

  2. 正常运行结果flag = 0:(发送Hello Word)

  3. 正常运行结果flag = 1:(发送Goodbye)

发布了47 篇原创文章 · 获赞 37 · 访问量 3688

猜你喜欢

转载自blog.csdn.net/qq_44045338/article/details/104976246