进程间通信,mkfifo创建命名管道

Last Updated: 2023-11-05 08:21:23 Sunday

-- TOC --

所谓命名管道,就是这个管道在文件系统中,有一个pathname,还有对应的权限,它是一个特殊的文件。

mkfifo是一个shell命令,同时也是一个glibc提供的C编程接口,功能相同,参考man 3 mkfifo。fifo表示first in first out,先进先出,这就是pipe的属性。

// mkfifo - make a FIFO special file (a named pipe)
// man 3 mkfifo
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

mkfifo底层可能是系统调用mknodman 2 mknod。mknod调用的第3个参数填0,就是创建named fifo pipe(未测试),否则第3个参数就要设备文件的major和minor。

下面是一段测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>


#define _PEXIT \
    do {\
        char ebuf[64] = {0};\
        sprintf(ebuf, "%s: %d", __FILE__, __LINE__);\
        perror(ebuf);\
        exit(errno);\
    }while(0)


void ptime(void) {
    time_t now;
    time(&now);
    printf("%ld ", now);
}


int main(int argc, char *argv[]) {
    assert(argc == 2);

    /* 0777 & ~umask */
    if (mkfifo(argv[1], 0777) != 0)
        _PEXIT;
    ptime();
    printf("mkfifo ok\n");

    /* fork a child to read */
    pid_t child;
    child = fork();
    if (child == 0) {
        /* in child */
        sleep(3);
        int fd;
        if ((fd=open(argv[1], O_RDONLY)) < 0)
            _PEXIT;

        /* read */
        char rb[64] = {0};
        if (read(fd,rb,64) < 0)
            _PEXIT;
        close(fd);
        ptime();
        printf("%s (child)\n", rb);

        exit(0);
    }

    /* open in write only mode, block if no open in read */
    int fd;
    if ((fd=open(argv[1], O_WRONLY)) < 0)
        _PEXIT;
    ptime();
    printf("open ok\n");

    /* in parent, write immediately */
    char *wb = "hello mkfifo";
    if (write(fd,wb,strlen(wb)) < 0)
        _PEXIT;
    close(fd);
    ptime();
    printf("write on\n");

    wait(NULL);

    /* delete */
    if (remove(argv[1]) != 0)
        _PEXIT;

    return 0;
}

刻意在子进程中先等3秒钟,这是为了测试一个效果:如果以写的方式打开named pipe,此时还没有以读方式打开的fd,写方式的open会block,直到有以read方式open的代码执行。

匿名管道在创建的时候,读写fd都有了,因此不存在这个block的问题。

测试如下:

$ ./test_mkfifo np1
1658484053 mkfifo ok
1658484056 open ok
1658484056 write on
1658484056 hello mkfifo (child)

重复创建相同名称的fifo文件时,会出错,因此代码最后调用了remove。

本文链接:https://cs.pynote.net/sf/linux/prog/202202105/

-- EOF --

-- MORE --