您的位置:首页 > 运维架构 > Linux

Linux下进程间通信:命名管道-mkfifo

2012-11-10 16:39 274 查看

Linux下进程间通信:命名管道-mkfifo

 
摘要:进程间通信的方法有很多,FIFO与管道是最古老,也是相对来说更简单的一个通信机制。FIFO相对管道有一个优势,就是FIFO只要求两个进程是同一主机的,而不要求进程之间存在亲缘关系。FIFO是存在于文件系统的文件,可以使用诸如open、read、write等函数来操作。本文总结网络和APUE关于FIFO讨论,同时参考了Linux系统手册。

目录 [隐藏]

FIFO(命名管道)概述
mkfifo函数
命名管道读写规则
从FIFO中读取数据
从FIFO中写入数据

FIFO示例

FIFO(命名管道)概述

FIFO是一种进程通信机制,它突破通常管道无法进行无关进程之间的通信的限制,使得同一主机内的所有的进程都可以通信。FIFO是一个文件类型,stat结构中st_mode指明一个文件结点是不是一个FIFO,可以使用宏S_ISFIFO来测试这一点。

当一个FIFO存在于文件系统里时,我们只需要在想进行通信的进程内打开这个文件就可以了。当然FIFO作为一个特殊的文件,它有一些不同普通文件特性,下面会详细详述它的读写规则,这些相对精通文件来有一定的区别。

我们可以使用open、read、write来操作FIFO文件,从而实现进程间通信的目的。在shell环境下,也可以直接使用FIFO,这时往往与重写向有一些关联,一般系统都提供mkfifo实用程序来创建一个FIFO文件,这个程序实际上使用mkfifo系统调用来完成这个事。

mkfifo函数

mkfifo创建一个指定名字的FIFO,它的函数原型如下:

#include<sys/stat.h>
int mkfifo(const
char* pathname, mode_t mode);

返回值:成功,0;失败,-1

参数pathname指出想要创建的FIFO路径,参数mode指定创建的FIFO访问模式。这个访问会与当前进程的umask进程运算,以产生实际应用的权限模式。

mkfifo返回-1时表示创建过程中遇到某种错误,此时会设置errno,用户可以检测errno来取得进一步信息:

EACCES: 路径所在的目录不允许执行权限
EEXIST:路径已经存在,这时包括路径是一个符号链接,无论它是悬空还没有悬空。
ENAMETOOLONG:要么全部的文件名大于PATH_MAX,要么是单独的文件名大于NAME_MAX。在GNU系统里没有这个文件名长度的限制,但在其它系统里可能存在。

ENOENT:目录部分不存在,或者是一个悬空链接。
ENOTDIR:目录部分不一个目录。
EROFS:路径指向一个只读的文件系统。

命名管道读写规则

FIFO又叫命名管道,事实上它与管道确实在下许多相似之处,下面关于规则的讨论很体现这个相似。

从FIFO中读取数据

约定:如果一个进程为了从FIFO中读取数据而阻塞打开了FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。

如果有进程写打开FIFO,且当前FIFO为空,则对于设置了阻塞标志的读操作来说,将一直阻塞下去,直到有数据可以读时才继续执行;对于没有设置阻塞标志的读操作来说,则返回0个字节,当前errno值为EAGAIN,提醒以后再试。
对于设置了阻塞标志的读操作来说,造成阻塞的原因有两种:一、当前FIFO内有数据,但有其它进程在读这些数据;二、FIFO本身为空。

解阻塞的原因是:FIFO中有新的数据写入,不论写入数据量的大小,也不论读操作请求多少数据量,只要有数据写入即可。
读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程中有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。
如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。
如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数少于请求的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。

从FIFO中写入数据

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

FIFO的长度是需要考虑的一个很重要因素。系统对任一时刻在一个FIFO中可以存在的数据长度是有限制的。它由#define PIPE_BUF定义,在头文件limits.h中。在Linux和许多其他类UNIX系统中,它的值通常是4096字节,Red Hat Fedora9下是4096,但在某些系统中它可能会小到512字节。

虽然对于只有一个FIFO写进程和一个FIFO的读进程而言,这个限制并不重要,但只使用一个FIFO并允许多个不同进程向一个FIFO读进程发送请求的情况是很常见的。如果几个不同的程序尝试同时向FIFO写数据,能否保证来自不同程序的数据块不相互交错就非常关键了à也就是说,每个写操作必须“原子化”。

设置了阻塞标志的写操作:

当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。即写入的数据长度小于等于PIPE_BUF时,那么或者写入全部字节,或者一个字节都不写入,它属于一个一次性行为,具体要看FIFO中是否有足够的缓冲区。
当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
没有设置阻塞标志的写操作:

当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写。
当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。

FIFO示例

本段给出使用FIFO一个示例,它体现了两个使用FIFO的典型情景。

创建FIFO

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
int main()
{

        int res = mkfifo("/tmp/my_fifo", 0777);

        if(res
== 0)

        {

                printf("FIFO created\n");

        }

        exit(EXIT_SUCCESS);
}

使用FIFO

#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>

FIFO "/tmp/my_fifo"
//本程序从一个FIFO读数据,并把读到的数据打印到标准输出
//如果读到字符“Q”,则退出
int main(int argc,
char** argv)
{

        char buf_r[100];

        int fd;

        int nread;

        if((mkfifo(FIFO, O_CREAT)
< 0)
&& (errno
!= EEXIST))

        {

                printf("不能创建FIFO\n");

                exit(1);

        }

        printf("准备读取数据\n");

        fd = open(FIFO, O_RDONLY, 0);

        if(fd
== -1)

        {

                perror("打开FIFO");

                exit(1);

        }

        while(1)

        {

                if((nread
= read(fd, buf_r, 100))
== -1)

                {

                        if(errno
== EAGAIN)
printf("没有数据\n");

                }

                //假设取到Q的时候退出

                if(buf_r[0]=='Q')
break;

                buf_r[nread]=0;

                printf("从FIFO读取的数据为:%s\n", buf_r);

                sleep(1);

        }

}

原文:http://cpp.ezbty.org/content/science_doc/linux%E4%B8%8B%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1%EF%BC%9A%E5%91%BD%E5%90%8D%E7%AE%A1%E9%81%93_mkfifo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息