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

版权声明:本文为博主原创文章,转载请务必注明作者与原文链接。 https://blog.csdn.net/jingerppp/article/details/89383201

前言

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

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

特点:

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

原型

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 文件的权限组和用户组,下面会介绍。

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。

实例

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 来查看。

猜你喜欢

转载自blog.csdn.net/jingerppp/article/details/89383201