进程间通信(3)——命名管道(FIFO)

0. 前言

命名管道也被称为FIFO(first in first out)文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。

由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。

特点:

  • FIFO可以在无关的进程之间交换数据,与无名管道不同。
  • FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

1. 原型

1.1 创建fifo

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
                                                       返回值:若成功,返回0;若出错,返回-1

mkfifo函数中的mode 参数的规格说明与open 函数中的mode 相同。创建的fifo 文件的权限组和用户组,下面会介绍。

1.2 open()

open(const char *path, O_RDONLY);
open(const char *path, O_RDONLY | O_NONBLOCK);
open(const char *path, O_WRONLY);
open(const char *path, O_WRONLY | O_NONBLOCK);

FIFO文件使用open 来打开,非阻塞标志O_NONBLOCK会产生下列影响:

  • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。

  • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

2. 实例

test_fifo.h

#ifndef __TEST_FIFO_INCLUDE__
#define __TEST_FIFO_INCLUDE__

#define fifo_path "test_fifo"

#endif

test_read.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

//include 3 header files for fifo
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "test_fifo.h"


int main()
{
    printf("main for reading fifo, pid is %d\n", getpid());

    if (access(fifo_path, F_OK) == -1) {
        printf("create fifo %s.\n", fifo_path);

        if (mkfifo(fifo_path, 0644) < 0) {
            printf("create fifo failed, %d.\n", errno);
            exit(0);
        }
    }

    int fd = -1;
    char buff[128] = {0};

    printf("open fifo for reading messages.\n");
    fd = open(fifo_path, O_RDONLY); //open fifo
    if (fd < 0) {
        printf("open fifo failed.\n");
        exit(0);
    }
    printf("open fifo for reading messages successfully, fd = %d\n", fd);

    int buff_len = 0;
    do {
        buff_len = read(fd, buff, 128); //read fifo
        if (buff_len > 0)
            printf("%d read messages: %s\n", getpid(), buff);
    } while(buff_len > 0);

    close(fd); //close fifo
    //unlink(fifo_path); //delete fifo file

    return 0;
}

test_write.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <string.h>

//include 3 header files for fifo
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "test_fifo.h"


int main()
{
    printf("main for writing fifo, pid is %d\n", getpid());

    /*if (access(fifo_path, F_OK) == -1) {
        printf("create fifo %s.\n", fifo_path);

        if (mkfifo(fifo_path, 0644) < 0) {
            printf("create fifo failed, %d.\n", errno);
            exit(0);
        }
    }*/

    int fd = -1;
    char buff[128] = {0};

    printf("open fifo for writing messages.\n");
    fd = open(fifo_path, O_WRONLY); //open fifo
    if (fd < 0) {
        printf("open fifo failed.\n");
        exit(0);
    }
    printf("open fifo for writing messages successfully, fd = %d\n",fd);

    int buff_len = 0;
    for (int i=0; i<10; i++) {
        buff_len = sprintf(buff, "send message %d from %d", i, getpid());

        if (write(fd, buff, buff_len+1) < 0) { //write fifo
            printf("write fifo failed.\n");
            close(fd);
            exit(0);
        }
        sleep(1);
    }

    close(fd); //close fifo

    return 0;
}

例子比较简单,通过test_write 写入数据,在test_read中读出。

在test_read中创建了fifo 文件:

prw-r--r--  1 shift shift    0 4月  17 10:20 test_fifo|

文件属性第一个字母为“p”,代表是管道文件。

另外,创建FIFO之后,可能出现多个client 通过已经的FIFO给服务器发送消息,这就涉及到了PIPE_BUF,如果不希望多个进程所写的数据交叉,则必须考虑原子写操作。

在阻塞的情况下:

  • 如果write的字节数小于等于PIPE_BUF,那么write会阻塞到写入所有数据,并且 写入操作是原子的。
  • 如果write的字节数大于PIPE_BUF,那么write会阻塞到写入所有数据,但写入操作不是原子的,即write会根据当前缓冲区剩余的大小,写入相应的字节数,然后等待下一次有空余的缓冲区,这中间可能会有其他进程进行write操作。

在非阻塞的情况下:

  • 如果write的字节数小于等于PIPE_BUF,且管道或FIFO有足以存放要写入数据大小的空间,那么就写入所有数据;
  • 如果write的字节数小于等于PIPE_BUF,且管道或FIFO没有足够存放要写入数据大小的空间,那么就会立即返回EAGAIN错误。
  • 如果write的字节数大于PIPE_BUF,且管道或FIFO有至少1B的空间,那么就内核就会写入相应的字节数,然后返回已写入的字节数;
  • 如果write的字节数大于PIPE_BUF,且管道或FIFO无任何的空间,那么就会立即返回EAGAIN错误。

详细的PIPE_BUF,可以通过ulimit -a  或者 man 7 pipe 来查看。

相关博文:

进程间通信(0)——序 

进程间通信(1)——信号(Signal)

进程间通信(2)——管道(PIPE)

进程间通信(3)——命名管道(FIFO)

进程间通信(4)——消息队列

进程间通信(5)——共享内存

进程间通信(6)——信号量(semaphore​​​​​​)

进程间通信(7)——套接字(socket)

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值