前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux进程通信——有名管道

Linux进程通信——有名管道

作者头像
zy010101
发布2019-05-25 19:57:41
2.7K0
发布2019-05-25 19:57:41
举报
文章被收录于专栏:程序员程序员

管道(pipe)是无名管道,他是进程资源的一部分,随着进程的结束而消失。并且它只能在拥有公共祖先进程的进程内通信。而有名管道(FIFO)的出现则解决了这个问题。FIFO提供了一个路径名与它关联。这样可以通过访问该路径就能使得两个进程之间相互通信。此处的FIFO严格遵守“先进先出”原则。读总是从头开始的,写总是从尾部进行的。匿名管道和FIFO都不支持lseek函数对他们操作。Linux下建立有名管道的函数是mkfifo。

函数原型: int mkfifo(const char * pathname,mode_t mode);

函数功能:创建一个FIFO文件,用于进程之间的通信。pathname就是路径名,mode是该文件的权限。返回值表示是否成功,返回0表示成功,返回-1表示失败,失败原因在errno中。(建立FIFO的时候,要求不存在这样的FIFO)。

例如执行下面代码来创建一个FIFO。

代码语言:javascript
复制
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>

int main()
{
    int ret;
    ret = mkfifo("My_FIFO",0666);
    if(0 != ret)
    {
        perror("mkfifo");
    }
    return 0;
}

可以看到,它以P开头,表面它是一个FIFO文件。它是真实存在于磁盘上的,不仅仅在内存中。进程结束了这个文件仍然在。FIFO和匿名管道一样,默认下要考虑阻塞。

  1. 当使用O_NONBLOCK标志的时候,打开FIFO文件,读取操作会立即返回。但是如果没有进程读取FIFO文件,那么写入FIFO的操作会返回ENXIO错误代码。
  2. 不使用O_NONBLOCK标志时,打开FIFO的操作必须等待其他进程写入FIFO后才执行,当然写入FIFO的操作也必须等到其他进程来读取FIFO以后才能执行。

当存在这个FIFO文件的时候,再次创建这个FIFO会显示File exists。首先,第一种情形的测试。

代码语言:javascript
复制
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>


int main()
{
    int ret;
    int fd;
    ret = mkfifo("My_FIFO",0666);
    if(0 != ret)
    {
        perror("mkfifo");
    }
    pid_t pid;
    pid = fork();
    if(0 < pid)
    {
        fd = open("My_FIFO",O_NONBLOCK|O_WRONLY);
        if(0 > fd)
        {
            perror("open FIFO");
        }
        else
        {
            write(fd,"Hello",5);
            printf("Write Over\n");
        }
        exit(0);
    }
    if(0 == pid)
    {
        sleep(2);       //在设置O_NONBLOCK标志的情形下,让父进程先写入
    }
    if(-1 == pid)
    {
        perror("fork");
    }
    return 0;
}

运行结果如下:

可以看到会发生错误,因为它没有阻塞。那么接着试一下直接读一个FIFO文件,看看会发生什么。

代码语言:javascript
复制
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>


int main()
{
    int ret;
    int fd;
    char str[30] = {0};
    ret = mkfifo("My_FIFO",0666);
    if(0 != ret)
    {
        perror("mkfifo");
    }
    pid_t pid;
    pid = fork();
    if(0 < pid)
    {
        fd = open("My_FIFO",O_NONBLOCK|O_RDONLY);   //注意这里和上面不一样,改成O_RDONLY
        if(0 > fd)
        {
            perror("open FIFO");
        }
        else
        {
            read(fd,str,sizeof(str));
            printf("Read Over\n");
        }
        exit(0);
    }
    if(0 == pid)
    {
        sleep(2);       //在设置O_NONBLOCK标志的情形下,让父进程直接读取
    }
    if(-1 == pid)
    {
        perror("fork");
    }
    return 0;
}

看到的结果是对一个空的FIFO文件打开并执行read操作是没有问题的。

先以只读方式打开,如果没有进程已经为写而打开一个 FIFO, 只读 open() 成功,并且 open() 不阻塞。

下面,当不设置O_NONBLOCK标志的时候,FIFO和匿名管道的处理方式是一样的。管道这个名字是非常形象的,一个管道必须有两端(就是在一个进程中必须读,另一个进程必须写),只有这样,才能正常操作,否则进程将会阻塞。例如下面这样。

代码语言:javascript
复制
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>

int main()
{
    int ret;
    int fd;
    char str[30] = {0};
    ret = mkfifo("My_FIFO",0666);
    if(0 != ret)
    {
        perror("mkfifo");
    }
    pid_t pid;
    pid = fork();
    if(0 < pid)
    {
        fd = open("My_FIFO",O_WRONLY);
        if(0 > fd)
        {
            perror("Write FIFO");
        }
        else
        {
            write(fd,"Hello",5);
            printf("Write Over\n");
        }
        wait(NULL);
        exit(0);
    }
    if(0 == pid)
    {
        sleep(2);
        // fd = open("My_FIFO",O_RDONLY);
        // read(fd,str,5);
        exit(0);
    }
    if(-1 == pid)
    {
        perror("fork");
    }
    return 0;
}

我们仅仅在父进程中进行了写(write),没有其他进程在读(read)。这样造成的结果是进程一直阻塞在这里,如下

没有输出结果,阻塞在这里不动了。而当我们加上注释掉了那两句话以后,程序就会有输出,结果如下:

或者说,这也体现了进程的并发行,管子有了一端以后,还必须有另一端,这才能构成管道。

测试一下,FIFO用于两个无关进程直接的通信。首先建立我们有两个进程,一个是test1,另一个是test2.

代码语言:javascript
复制
//test1的源代码
//test1是写FIFO

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
    int fd,ret;
    char str[10] = {"Hello"};
    ret = mkfifo("fifo",0666);      //test1.c中创建FIFO文件
    fd = open("fifo",O_WRONLY);     //只写方式打开
    write(fd,str,5);
    close(fd);

    return 0;
}
代码语言:javascript
复制
//test2的源代码
//test2是读FIFO

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    int fd;
    char str[10] = {0};
    fd = open("fifo",O_RDONLY);
    read(fd,str,5);
    printf("%s\n",str);
    close(fd);
    return 0;
}

我们把test1和test2的源代码生成可执行文件后,打开两个终端。如果我先运行test1,然后运行test2.那么test2将读取到FIFO中的数据。如下所示。

我们没有设置O_NONBLOCK,先运行test1之后,会发现test1阻塞在这里。等我们把test2也运行了之后,test1不在阻塞,运行结束,然后test2也成功打印出了Hello。

换个运行顺序,我们先运行test2,然后运行test1.这样会发现test2阻塞在这里。等我们把test1也运行了之后,test2不在阻塞,向屏幕打印Hello.

如果我们不想让FIFO阻塞,那么打开文件的时候设置为可读可写即可。

代码语言:javascript
复制
fd = open("fifo", O_RDWR);

当然,如果FIFO是空的,那么即使设置了可读可写,read()操作仍旧会阻塞。这样的运行结果和上面所说的是一致的。自己运行一下才能深刻理解。这里不好用图片说明。

调用 write() 函数向 FIFO 里写数据,当缓冲区已满时 write() 也会阻塞。

通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会退出。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年11月10日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档