有名管道(FIFO)

分享到:
           

    本文关键字: linux 管道通信,linux 进程通信方式,有名管道,FIFO

    有名管道的创建可以使用函数mkfifo(),该函数类似于文件中的open()操作,可以指定管道的路径和打开的模式。用户还可以在命令行使用“mknod 管道名 p”来创建有名管道。

    在创建管道成功后,就可以使用open()、read()和write()这些函数了。与普通文件的开发设置一样,对于为读而打开的管道可在open()中设置O_RDONLY,对于为写而打开的管道可在open()中设置O_WRONLY,在这里与普通文件不同的是阻塞问题。由于普通文件在读写时不会出现阻塞问题,而在管道的读写中却有阻塞的可能,这里的非阻塞标志可以在open()函数中设定为O_NONBLOCK。下面分别对阻塞打开和非阻塞打开的读写进行讨论。

    对于读进程:

    ● 若该管道是阻塞打开,且当前FIFO内没有数据,则对读进程而言将一直阻塞到有数据写入。

    ● 若该管道是非阻塞打开,则不论FIFO内是否有数据,读进程都会立即执行读操作。即如果FIFO内没有数据,则读函数将立刻返回0。

    对于写进程:

    ● 若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入。

    ● 若该管道是非阻塞打开而不能写入全部数据,则读操作进行部分写入或者调用失败。

    表1列出了mkfifo()函数的语法要点。

表1 mkfifo()函数语法要点

所需头文件 #include <sys/types.h> #include <sys/state.h>
函数原型 int mkfifo(const char *filename,mode_t mode)
函数传入值 filename:要创建的管道
mode O_RDONLY:读管道
O_WRONLY:写管道
O_RDWR:读写管道
O_NONBLOCK:非阻塞
O_CREAT:如果该文件不存在,那么就创建一个新的文件,并用第3个参数为其设置权限
O_EXCL:如果使用O_CREAT时文件存在,那么可返回错误消息。这个参数可测试文件是否存在
函数返回值 成功:0
出错:-1

    表2再对FIFO相关的出错信息进行归纳,以方便用户查错。

表2 FIFO相关的出错信息

EACCESS 参数filename所指定的目录路径无可执行的权限
EEXIST 参数filename所指定的文件已存在
ENAMETOOLONG 参数filename的路径名称太长
ENOENT 参数filename包含的目录不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数filename路径中的目录存在但却非真正的目录
EROFS 参数filename指定的文件存在于只读文件系统内

    下面的实例包含两个程序,一个用于读管道,另一个用于写管道。其中在读管道的程序中创建管道,并且作为main()函数里的参数由用户输入要写入的内容;读管道的程序会读出用户写入到管道的内容。这两个程序采用的是阻塞式读写管道模式。

    写管道的程序如下:

    /* fifo_write.c */
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <limits.h>
    #define MYFIFO "/tmp/myfifo" /* 有名管道文件名 */
    #define MAX_BUFFER_SIZE PIPE_BUF /* 定义在limits.h中 */

    int main(int argc, char * argv[]) /* 参数为即将写入的字符串 */
    {
        int fd;
        char buff[MAX_BUFFER_SIZE];
        int nwrite;

        if(argc <= 1)
        {
            printf("Usage: ./fifo_write string\n");
            exit(1);
        }
        sscanf(argv[1], "%s", buff);

        /* 以只写阻塞方式打开FIFO管道 */
        fd = open(MYFIFO, O_WRONLY);
        if (fd == -1)
        {
            printf("Open fifo file error\n");
            exit(1);
        }

        /* 向管道中写入字符串 */
        if ((nwrite = write(fd, buff, MAX_BUFFER_SIZE)) > 0)
        {
            printf("Write '%s' to FIFO\n", buff);
        }
        close(fd);
        exit(0);
    }

    读管道程序如下:

    /* fifo_read.c */
    (头文件和宏定义同fifo_write.c)
    int main()
    {
        char buff[MAX_BUFFER_SIZE];
        int fd;
        int nread;

        /* 判断有名管道是否已存在,若尚未创建,则以相应的权限创建 */
        if (access(MYFIFO, F_OK) == -1)
        {
            if ((mkfifo(MYFIFO, 0666) < 0) && (errno != EEXIST))
                {
                    printf("Cannot create fifo file\n");
                    exit(1);
                }
            }
            /* 以只读阻塞方式打开有名管道 */
            fd = open(MYFIFO, O_RDONLY);
            if (fd == -1)
            {
                printf("Open fifo file error\n");
                exit(1);
            }

            while (1)
            {
                memset(buff, 0, sizeof(buff));
                if ((nread = read(fd, buff, MAX_BUFFER_SIZE)) > 0)
            {
                printf("Read '%s' from FIFO\n", buff);
            }
        }
        close(fd);
        exit(0);
    }

    为了能够较好地观察运行结果,需要把这两个程序分别在两个终端里运行,在这里首先启动读管道程序。读管道进程在建立管道后就开始循环地从管道里读出内容,如果没有数据可读,则一直阻塞到写管道进程向管道写入数据。在启动了写管道程序后,读进程能够从管道里读出用户的输入内容,程序运行结果如下。

    终端一:

    $ ./fifo_read
    Read 'FIFO' from FIFO
    Read 'Test' from FIFO
    Read 'Program' from FIFO
    …

    终端二:

    $ ./fifo_write FIFO
    Write 'FIFO' to FIFO
    $ ./fifo_write Test
    Write 'Test' to FIFO
    $ ./fifo_write Program
    Write 'Program' to FIFO
    …

    本文选自华清远见嵌入式培训教材《从实践中学嵌入式Linux应用程序开发》

   热点链接:

   1、标准流管道
   2、无名管道系统调用
   3、Linux下进程间通信方式-管道
   4、Linux下进程间通信
   5、实验:编写守护进程

更多新闻>>