命名管道的概述

无名管道,由于没有名字,只能用于亲缘关系的进程间通信(更多详情,请看《无名管道》)。为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道、FIFO 文件。

命名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据

命名管道(FIFO)和无名管道(pipe)有一些特点是相同的,不一样的地方在于:

1、FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中。

2、当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。

3、FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。

命名管道的创建

所需头文件:

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo( const char *pathname, mode_t mode);

功能:

命名管道的创建。

参数:

pathname: 普通的路径名,也就是创建后 FIFO 的名字。

mode: 文件的权限,与打开普通文件的 open() 函数中的 mode 参数相同,相关说明请点此链接。

返回值:

成功:0
失败:如果文件已经存在,则会出错且返回 -1。

示例代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>int main(int argc, char *argv[])
{int ret;ret = mkfifo("my_fifo", 0666); // 创建命名管道if(ret != 0){  // 出错perror("mkfifo");}return 0;
}

运行结果如下:

命名管道的默认操作

后期的操作,把这个命名管道当做普通文件一样进行操作:open()、write()、read()、close()。但是,和无名管道一样,操作命名管道肯定要考虑默认情况下其阻塞特性。

下面验证的是默认情况下的特点,即 open() 的时候没有指定非阻塞标志( O_NONBLOCK )。

1)

open() 以只读方式打开 FIFO 时,要阻塞到某个进程为写而打开此 FIFO
open() 以只写方式打开 FIFO 时,要阻塞到某个进程为读而打开此 FIFO。

简单一句话,只读等着只写,只写等着只读,只有两个都执行到,才会往下执行。

只读端代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666);if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_RDONLY);//等着只写if(fd < 0){perror("open fifo");}printf("after open\n");return 0;
}

只写端代码如下

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666);if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_WRONLY); //等着只读if(fd < 0){perror("open fifo");}printf("after open\n");return 0;
}

大家开启两个终端,分别编译以上代码,读端程序和写端程序各自运行,如下图,大家自行验证其特点,因为光看结果图是没有效果,大家需要分析其运行过程是如何变化。

如果大家不想在 open() 的时候阻塞,我们可以以可读可写方式打开 FIFO 文件,这样 open() 函数就不会阻塞。

// 可读可写方式打开
int fd = open("my_fifo", O_RDWR);

2)假如 FIFO 里没有数据,调用 read() 函数从 FIFO 里读数据时 read() 也会阻塞。这个特点和无名管道是一样的。

写端代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666);//创建命名管道if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_WRONLY); //等着只读if(fd < 0){perror("open fifo");}printf("after open\n");printf("before write\n");// 5s后才往命名管道写数据,没数据前,读端read()阻塞sleep(5);char send[100] = "Hello Mike";write(fd, send, strlen(send));printf("write to my_fifo buf=%s\n", send);return 0;
}

读端代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666); //创建命名管道if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_RDONLY);//等着只写if(fd < 0){perror("open fifo");}printf("after open\n");printf("before read\n");char recv[100] = {0};//读数据,命名管道没数据时会阻塞,有数据时就取出来read(fd, recv, sizeof(recv)); printf("read from my_fifo buf=[%s]\n", recv);return 0;
}

请根据下图自行编译运行验证:

3)通信过程中若写进程先退出了,就算命名管道里没有数据,调用 read() 函数从 FIFO 里读数据时不阻塞;若写进程又重新运行,则调用 read() 函数从 FIFO 里读数据时又恢复阻塞。

写端代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666); // 创建命名管道if(ret != 0){perror("mkfifo");}fd = open("my_fifo", O_WRONLY); // 等着只读if(fd < 0){perror("open fifo");}char send[100] = "Hello Mike";write(fd, send, strlen(send)); //写数据printf("write to my_fifo buf=%s\n",send);while(1); // 阻塞,保证读写进程保持着通信过程return 0;
}

读端代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666);// 创建命名管道if(ret != 0){perror("mkfifo");}fd = open("my_fifo", O_RDONLY);// 等着只写if(fd < 0){perror("open fifo");}while(1){char recv[100] = {0};read(fd, recv, sizeof(recv)); // 读数据printf("read from my_fifo buf=[%s]\n",recv);sleep(1);}return 0;
}

请根据下图自行编译运行验证:

5)通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE 信号)退出。

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

5)和 6)这两个特点和无名管道是一样的,这里不再验证,详情请看《无名管道》。

命名管道非阻塞标志操作

命名管道可以以非阻塞标志(O_NONBLOCK)方式打开:

fd = open("my_fifo", O_WRONLY|O_NONBLOCK);
fd = open("my_fifo", O_RDONLY|O_NONBLOCK);

非阻塞标志(O_NONBLOCK)打开的命名管道有以下特点:
1、先以只读方式打开,如果没有进程已经为写而打开一个 FIFO, 只读 open() 成功,并且 open() 不阻塞。

2、先以只写方式打开,如果没有进程已经为读而打开一个 FIFO,只写 open() 将出错返回 -1。

3、read()、write() 读写命名管道中读数据时不阻塞。

请根据以下代码自行编译运行验证。

写端代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666); // 创建命名管道if(ret != 0){perror("mkfifo");}// 只写并指定非阻塞方式打开fd = open("my_fifo", O_WRONLY|O_NONBLOCK);if(fd<0){perror("open fifo");}char send[100] = "Hello Mike";write(fd, send, strlen(send));printf("write to my_fifo buf=%s\n",send);while(1);return 0;
}

读端代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd;int ret;ret = mkfifo("my_fifo", 0666); // 创建命名管道if(ret != 0){perror("mkfifo");}// 只读并指定非阻塞方式打开fd = open("my_fifo", O_RDONLY|O_NONBLOCK);if(fd < 0){perror("open fifo");}while(1){char recv[100] = {0};read(fd, recv, sizeof(recv));printf("read from my_fifo buf=[%s]\n",recv);sleep(1);}return 0;
}

本教程示例代码下载请点此处。

Linux系统编程——进程间通信:命名管道(FIFO)相关推荐

  1. 【Linux】进程间通信-命名管道FIFO

    命名管道概述 如果我们要在不相关的进程间交换数据,那么使用FIFO文件将会十分方便. FIFO文件通常也称为命名管道(named pipe).命名管道是一种特殊类型的文件,它在文件系统中以文件名的形式 ...

  2. linux 进程间通信 命名管道FIFO的原理与使用

    参考文章1:管道 pipe是什么?(进程通信的一种方式)(可以先大致了解管道) 参考文章2:FIFO(命名管道) FIFO常被称为命名管道,以区分管道(pipe).管道(pipe)只能用于" ...

  3. Linux系统编程—进程间通信—信号量

    信号量 信号量是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用.在进入一个关键代码段之前,线程必须获取一个信号量:一旦该关键代码段完成了,那么该线程必须释放信号量.其它想 ...

  4. Linux系统编程阶段:管道和信号相关知识及部分函数介绍。

    进程间的通讯方式: 管道.信号.共享内存.消息队列.信号量.套接字 1.管道 1)无名管道 特点: 不会在进程空间产生一个实实在在的管道文件: 只有用于亲缘关系的进程: 数据读出后就不存在管道: 创建 ...

  5. Linux系统编程—进程间通信—共享内存

    Linux共享内存 共享内存就是允许两个或多个进程共享一定的存储区.就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时候,其它进程都会察觉 ...

  6. 入门Linux系统编程--网络编程

    文章目录 一.网络编程 1.socket服务端代码实现(无连接客户端) 6.socket服务端代码实现(连接客户端) 7.socket客户端代码实现 8.实现双方聊天 9.多方消息收发 二.往期文章 ...

  7. 【Linux系统编程学习】匿名管道pipe与有名管道fifo

    此为牛客Linux C++和黑马Linux系统编程课程笔记. 0. 关于进程通信 Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间.任何一个进程的全局变量在另一个进程中都看不到 ...

  8. Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

    整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...

  9. 【Linux系统编程】进程间通信之无名管道

    00. 目录 文章目录 00. 目录 01. 管道概述 02. 管道创建函数 03. 管道的特性 04. 管道设置非阻塞 05. 附录 01. 管道概述 管道也叫无名管道,它是是 UNIX 系统 IP ...

最新文章

  1. ListView.getChildCount() 详解
  2. Linux基础知识之挂载详解(mount,umount及开机自动挂载)
  3. py脚本:linux系统下定时清理文件
  4. uwsgi怎么通过浏览器访问某个脚本_4个Shell小技巧帮你提高机器学习效率:写好脚本,事半功倍...
  5. 管理数据,应用程序和主机安全-A
  6. Linux读写I2C设备I2C_RDWR用法
  7. 因果推断综述及基础方法介绍(二)
  8. iptables基本用法和linux网络相关
  9. 比较JavaScript中的集合及其检索效率
  10. linux卸载jdk权限不够,linux中卸载jdk,一个简单有关问题整了一上午
  11. java高级学习视频下载
  12. FFmpeg下载秒级 支持各个平台各个版本
  13. Axure RP 9 原型图的绘制及交互
  14. FPGA实验2:ADDA测试
  15. 测试显卡游戏里FPS温度性能的软件,求一个在游戏中显示FPS 显卡温度之类的工具...
  16. 61种u盘问题解决工具合集解决无法格式化,u盘写保护等问题。
  17. 计算机专业小米笔记本推荐,小米笔记本电脑怎么样有哪些型号,哪款性价比高?...
  18. 连接网络计算机提示没有访问权限,无网络访问权限,教您无网络访问权限怎么办...
  19. html怎么改默认浏览器,怎样设置默认浏览器?3种更改默认浏览器方法介绍
  20. HDU6348 Buy and Resell

热门文章

  1. [gitlab] release功能
  2. 怎样做到微信支付二维码链接,单独跳转?
  3. linux内核发包工具pktgen
  4. hpgen8服务器进不了系统,HP Gen8 服务器安装操作系统的一些出错信息及解决办法...
  5. 楚留香服务器最多人,《楚留香传奇》七大顶尖高手排名,香帅屈居第五,第一毫无悬念...
  6. python twisted教程_python中twisted实例用法
  7. linux基础操作练习题
  8. chomd 更改“***”的权限: 不允许的操作
  9. BLAS、OpenBLAS、ATLAS、MKL
  10. Webpack5 Federation