几种常见进程间通信(IPC)方式之有名管道FIFO

本文深入探讨了有名管道(FIFO)这一进程间通信方式,解释了其工作原理,包括创建、打开规则、读写操作及原子性保证,并对比了其与匿名管道的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

几种常见进程间通信(IPC)方式-有名管道FIFO

前言

进程间通信是指在不同进程之间传播或交换信息,在Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间,进程之间不能相互访问。必须通过内核才能进行数据交换。如图:
在这里插入图片描述
常见的通信方式有以下几种:

  • 管道pipe
  • 有名管道FIFO
  • 消息队列MessageQueue
  • 共享存储
  • 信号量Semaphore
  • 信号Signal
  • 套接字Socket

接下来我们将详细介绍有名管道

有名管道FIFO

与前面介绍到的管道pipe不同,通过有名管道(命名管道),不相关(没有血缘关系)的进程也能交换数据。

  • 原理:FIFO是Linux基础文件类型中的一种,但是FIFO文件在磁盘上没有数据块,只是用来标识内核中一条通道。进程可以打开这个文件进行readwrite操作,实际上是在读写内核通道,进程只要能够访问到该路径,则能实现进程间通信。
有名管道创建
//成功返回0,失败返回-1
int mkfifo(const char *pathname, mode_t mode);

一旦使用mkfifo创建了一个FIFO管道,常见的文件I/O函数都可用于FIFO

有名管道打开规则
  • 如果当前打开操作是读
    1.若已经有相应进程为而打开该FIFO,则当前打开操作成功
    2.反之,则可能会阻塞直到有相应进程为而打开该FIFO(这取决于是否设置了阻塞标志,没有设置阻塞则返回成功)
  • 如果当前打开操作是写
    1.若已经有相应进程为而打开该FIFO,则当前打开操作成功
    2.反之,则可能会阻塞直到有相应进程为而打开该FIFO(这取决于是否设置了阻塞标志,没有设置阻塞则返回ENXIO错误)

一旦设置了阻塞标志,那么管道的两端读写必须都打开,有任何一方未打开,调用open时就会被阻塞

FIFO中读取数据
  • 设置了阻塞标志的读操作
    对于各个读进程来说,这个有名管道是临界资源,读打开阻塞标志保证了只允许第一个读操作,而其他的读操作将被阻塞,直到第一个读操作完成后才被唤醒。对于设置了阻塞标志的读操作而言,造成阻塞的原因有两个
    1.FIFO内有数据,但是有其他进程在读。
    2.FIFO内没有数据。(会在FIFO中有新数据写入时被唤醒)

  • 没有设置阻塞标志的读操作
    如果读取失败会返回-1,当前error值为EAGAIN

FIFO中写入数据
  • 设置了阻塞标志的写操作
    1.当请求写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳请求写入的字节数,则阻塞,直到能够容纳后解阻塞。
    2.当请求写入数据量大于PIPE_BUF时,linux将保证写入的原子性,如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;否则返回EAGIN错误,提醒以后再写。

  • 没有设置阻塞标志的写操作
    1.当请求写入的数据量不大于PIPE_BUG时,linux将保证写入原子性。如果此时管道空闲缓冲区足以容纳请求写入的字节数,写完后成功返回;否则将返回EAGAIN错误,提醒以后再写。
    2.当请求写入的数据量大于PIPE_BUG时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后返回。

(保证写入原子性:写入操作完成之后才能进行进程切换,保证写入的正确性)

fifo_write.c

创建FIFO并且向其中写入数据。

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

void sys_err(char *str)
{
        perror(str);
        exit(-1);
}

int main()
{
        int fd, i;
        char buf[4096];
        //创建有名管道
        if(mkfifo("./myfifo", 0777) != 0) {
                perror("fifo create err!!");
        }
        //打开有名管道
        fd = open("./myfifo", O_WRONLY);
        if (fd < 0){
                sys_err("open");
                exit(1);
        }

        i = 0;
        while (1) {
                sprintf(buf, "hello itcast %d\n", i++);
                //写数据
                write(fd, buf, strlen(buf));
        }
        close(fd);

        return 0;
}

fifo_read.c

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

void sys_err(char *str)
{
        perror(str);
        exit(1);
}

int main()
{
        int fd, len;
        char buf[4096];
        //打开有名管道
        fd = open("./myfifo", O_RDONLY);
        if (fd < 0) {
                sys_err("open");
        }
        while (1) {
                //读取数据
                len = read(fd, buf, sizeof(buf));
                write(STDOUT_FILENO, buf, len);
                sleep(3);           //多個读端时应增加睡眠秒数,放大效果.
        }
        close(fd);

        return 0;
}

总结

有名管道相比较于管道pipe(匿名管道)来说,解决了非血缘关系进程之间的通信。有名管道和匿名管道(pipe)的区别如下:

  • 匿名管道必须由进程进行创建然后进行亲缘进程之间的通信,但是有名管道可以是事先创建好,进程可以之间访问其path进行操作(前提是有足够的权限访问该path)。
  • 匿名管道在创建的时候就已经被打开了,只需要自行关闭,而有名管道需要自行进行openclose操作。
  • 匿名管道只能用于有血缘关系的进程之间通信,而有名管道不是。

接下来我们将介绍消息队列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值