前文中笔者介绍了管道,本文接着介绍命名管道。文中演示所用环境为 Ubuntu 18.04 desktop。

命名管道(named pipe)又被称为先进先出队列(FIFO),是一种特殊的管道,存在于文件系统中。命名管道与管道非常类似,但是又有自身的显著特征:

  • 命名管道可以用于任何两个进程间的通信,而不限于同源的两个进程。
  • 命名管道作为一种特殊的文件存放在文件系统中,而不是像管道那样存放在内核中。当进程对命名管道的使用结束后,命名管道依然存在于文件系统中,除非对其进行删除操作,否则该命名管道不会自行消失。

和管道一样,命名管道也只能用于数据的单向传输,如果要用命名管道实现两个进程间数据的双向传输,建议使用两个单向的命名管道。

创建命名管道

在命令行上创建命名管道
可以通过命令行命令 mkfifo 或 mknod 创建命名管道:

$ mkfifo /tmp/testp
$ mknod /tmp/testp p

可以通过 ls 命令查看命名管道的文件属性:

输出中的第一个字符为 p,表示这个文件的类型为管道。最后的 | 符号是有 ls 命令的 -F 选项添加的,也表示这个一个管道。

在程序中创建命名管道
在程序中创建命名管道,可以使用 mkfifo 函数,其签名如下:

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

参数 pathname 是一个字符串指针,用于存放命名管道的文件路径。参数 mode 用于表示指定所创建文件的权限。该函数调用成功时返回 0;调用失败时返回 -1。
mkfifo 函数是一个专门用来创建命名管道的函数,而另外一个函数 mknod 却可以兼职创建命名文件,其函数签名如下:

#include <sys/types.h>
#include <sys/stat.h> int mknod(char *pathname, mode_t mode, dev_t dev);

创建命名管道只是 mknod 函数的功能之一,它的前两个参数和 mkfifo 函数相同。在创建命名管道时,为第三个参数 dev 传递 0 就可以了。该函数调用成功时返回 0;调用失败时返回 -1。

在程序中使用命名管道

下面的 demo 模拟一个生产者进程和消费者进程,二者通过命名管道传输数据。生产者的代码如下:

#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h> #define FIFO_NAME "/tmp/testp"
#define BUFFER_SIZE 4096
#define TEN_MEG (1024 * 1024 * 10) int main(void)
{
int pipe_fd;
int res;
int open_mode = O_WRONLY;
int bytes_sent = ;
char buffer[BUFFER_SIZE + ]; if(access(FIFO_NAME, F_OK) == -)
{
res = mkfifo(FIFO_NAME, );
if(res != )
{
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
} printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d opened fd %d\n", getpid(), pipe_fd); if(pipe_fd != -)
{
while(bytes_sent < TEN_MEG)
{
res = write(pipe_fd, buffer, BUFFER_SIZE);
if(res == -)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
bytes_sent += res;
}
(void)close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}

把上面的代码保存到文件 namedpipedemo.c 中。
消费者的代码如下:

#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h> #define FIFO_NAME "/tmp/testp"
#define BUFFER_SIZE 4096 int main(void)
{
int pipe_fd;
int res;
int open_mode = O_RDONLY;
int bytes_read = ;
char buffer[BUFFER_SIZE + ]; memset(buffer, '\0', sizeof(buffer)); printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d opened fd %d\n", getpid(), pipe_fd); if(pipe_fd != -)
{
do
{
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > );
(void)close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}

把上面的代码保存到文件 namedpipedemo2.c 中。并分别编译这两个程序:

$ gcc -Wall namedpipedemo.c -o pipe1
$ gcc -Wall namedpipedemo2.c -o pipe2

先在一个终端中执行生产者:

然后在另一个终端中执行消费者:

结果是二者完成数据传输后都返回了:

删除命名管道

删除命名管道和删除一个普通文件没有什么区别:

$ rm /tmp/testp

这就可以了!

参考:
《Linux 程序设计》
《Linux 环境下 C 编程指南》

Linux 命名管道的更多相关文章

  1. linux命名管道通信过程

    前一个道,这节学习命名管道. 二命名管道 无名管道仅仅能用来在父子进程或兄弟进程之间进行通信,这就给没有亲缘关系的进程之间数据的交换带来了麻烦.解决问题就是本节要学习的还有一种管道通信:命名管道. 命 ...

  2. Linux学习笔记(13)-进程通信|命名管道

    匿名管道只能在具有亲属关系的进程间通信,那么如果想要在不具有亲戚关系,想在陌生人之间通信,那又该怎么办呢? 别慌,Linux身为世界上*强大的操作系统,当然提供了这种机制,那便是命名管道-- 所谓命名 ...

  3. Linux进程间通信(四):命名管道 mkfifo()、open()、read()、close()

    在前一篇文章—— Linux进程间通信 -- 使用匿名管道 中,我们看到了如何使用匿名管道来在进程之间传递数据,同时也看到了这个方式的一个缺陷,就是这些进程都由一个共同的祖先进程启动,这给我们在不相关 ...

  4. 【LINUX/UNIX网络编程】之使用消息队列,信号量和命名管道实现的多进程服务器(多人群聊系统)

    RT,使用消息队列,信号量和命名管道实现的多人群聊系统. 本学期Linux.unix网络编程的第三个作业. 先上实验要求: 实验三  多进程服务器 [实验目的] 1.熟练掌握进程的创建与终止方法: 2 ...

  5. Linux进程间通信——使用命名管道

    在前一篇文章——Linux进程间通信——使用匿名管道中,我们看到了如何使用匿名管道来在进程之间传递数据,同时也看到了这个方式的一个缺陷,就是这些进程都由一个共同的祖先进程启动,这给我们在不相关的的进程 ...

  6. Linux学习记录--命名管道通信

    命名管道通信 什么是命名管道 一个主要的限制是,它是匿名管道的应用还没有名字,因此,只有它可以用于进程间通信的方式与亲缘关系.在命名管道(named pipe或FIFO)提出后,该限制得到了克服.FI ...

  7. Linux环境进程间通信(一):管道及命名管道

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  8. 【linux】mkfifo 命令创建命名管道实现进程之间通信

    mkfifo 命令 mkfifo命令创建一个FIFO特殊文件,是一个命名管道(可以用来做进程之间通信的桥梁) 管道也是一种文件,一般是linux中的一个页大小,4k,管道数据一旦被读取就没了.(管道大 ...

  9. Linux中的pipe(管道)与named pipe(FIFO 命名管道)

    catalogue . pipe匿名管道 . named pipe(FIFO)有名管道 1. pipe匿名管道 管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入,常 ...

随机推荐

  1. 基于Redis扩展模块的布隆过滤器使用

    什么是布隆过滤器?它实际上是一个很长的二进制向量和一系列随机映射函数.把一个目标元素通过多个hash函数的计算,将多个随机计算出的结果映射到不同的二进制向量的位中,以此来间接标记一个元素是否存在于一个 ...

  2. Java的反射机理

    Java反射是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的全部属性和方法等,调用方法以及访问属性,而且不需要提前在编译期知道运行的对象是什么 ...

  3. Fiddle无法抓取网页信息或HTTPS

    1:清除电脑根证书: 打开dos命令框,输入:certmgr.msc ![file](https://img2018.cnblogs.com/blog/1023158/201912/1023158-2 ...

  4. 每日JAVA面试

  5. pwn-200

    题目连接 https://adworld.xctf.org.cn/media/task/attachments/49bd95c78386423997fa044a9e750015 借鉴 https:// ...

  6. UDP组播

    多播(组播) 组播组可以是永久的也可以是临时的.组播组地址中,有一部分由官方分配的,称为永久组播组.永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化.永久组播组中成员的数量都可以是任意的 ...

  7. 集合系列 List(四):LinkedList

    LinkedList 是链表的经典实现,其底层采用链表节点的方式实现. public class LinkedList<E> extends AbstractSequentialList& ...

  8. laravel实现多模块

    一.这里使用Caffienate Modules 网址:modules maintained by caffeinated 二.根据自己的版本选择包的版本 三.在项目composer.json文件中加 ...

  9. JS获取url请求参数

    JS获取url请求参数,代码如下: // 获取url请求参数 function getQueryParams() { var query = location.search.substring(1) ...

  10. Java题库——Chapter11 继承和多态

    1)Analyze the following code: public class Test { public static void main(String[ ] args) { B b = ne ...