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

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

 

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

目录 [隐藏]
  1. FIFO(命名管道)概述
  2. mkfifo函数
  3. 命名管道读写规则
    1. 从FIFO中读取数据
    2. 从FIFO中写入数据
  4. 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
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
#include
#include
#include
int main ( )
{
        int res = mkfifo ( "/tmp/my_fifo", 0777 ) ;
        if (res == 0 )
        {
                printf ( "FIFO created\n" ) ;
        }
        exit ( EXIT_SUCCESS ) ;
}

使用FIFO

#include
#include
#include

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

你可能感兴趣的:(linux系统管理,IPC,linux,Linux,LINUX,mkfifo,mknode,命名管道)