linux进程间管道通讯方法

2021年09月15日 阅读数:2
这篇文章主要向大家介绍linux进程间管道通讯方法,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

 

Linux下进程间通讯:命名管道-mkfifo
 
由 lgb 于 星期日, 2010/09/12 - 21:24 发表

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

FIFO(命名管道)概述

FIFO是一种进程通讯机制,它突破一般管道没法进行无关进程之间的通讯的限制,使得同一主机内的全部的进程均可以通讯。FIFO是一个文件类型,stat结构中st_mode指明一个文件结点是否是一个FIFO,可使用宏S_ISFIFO来测试这一点。linux

当一个FIFO存在于文件系统里时,咱们只须要在想进行通讯的进程内打开这个文件就能够了。固然FIFO做为一个特殊的文件,它有一些不一样普通文件特性,下面会详细详述它的读写规则,这些相对精通文件来有必定的区别。shell

咱们可使用open、read、write来操做FIFO文件,从而实现进程间通讯的目的。在shell环境下,也能够直接使用FIFO,这时每每与重写向有一些关联,通常系统都提供mkfifo实用程序来建立一个FIFO文件,这个程序实际上使用mkfifo系统调用来完成这个事。网络

mkfifo函数

mkfifo建立一个指定名字的FIFO,它的函数原型以下:ide

#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又叫命名管道,事实上它与管道确实在下许多类似之处,下面关于规则的讨论很体现这个类似。spa

从FIFO中读取数据

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

  • 若是有进程写打开FIFO,且当前FIFO为空,则对于设置了阻塞标志的读操做来讲,将一直阻塞下去,直到有数据能够读时才继续执行;对于没有设置阻塞标志的读操做来讲,则返回0个字节,当前errno值为EAGAIN,提醒之后再试。
  • 对于设置了阻塞标志的读操做来讲,形成阻塞的缘由有两种:1、当前FIFO内有数据,但有其它进程在读这些数据;2、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 createdn");
}
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("不能建立FIFOn");
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读取的数据为:%sn", buf_r);
sleep(1);
}

}