不相关进程间通信--命名管道(FIFO文件)

命名管道是一种特殊类型的文件(FIFO),可以完成两个不相关之间的通信。它在文件系统中以文件名的方式存在,但行为却和一般的管道相似。

一.创建FIFO

在程序中,可以使用两个不同的函数调用创建:

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *filename, mode_t mode);

int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0);

二.打开FIFO

可以直接用open函数打开:

int open( const char * pathname, int flags);

参数flags有四种组合方式:

O_RDONLY:open调用一直阻塞,直到有一个进程以写方式打开一个FIFO

O_RDONLY | O_NONBLOCK:立即调用并返回,如果没有其他进程以写方式打开一个FIFO,就返回0

O_WRONLY:open调用一直阻塞,直到有一个进程读写方式打开一个FIFO

O_WRONLY | O_NONBLOCK:立即调用并返回,如果没有其他进程以读方式打开一个FIFO,就返回0

在这里没有O_RDWR模式,困为管道是单向传输数据的,如果需要双向传输,常用的方法是打开两个管道;还有一种是在一个方向的传输结束后关闭该管道,然后再打开进行另一个方向的传输,这种方法用得少。

三.读写操作

打开管道时使用O_NONBLOCK全影响到对它的write,read调用

对一个空的、阻塞的FIFO的read调用将待,直到有数据可以读时才继续执行,与此相反,对一个空的,非阻塞的FIFO进行read调用会立即返回0。

对一个阻塞的FIFO进行write调用将待,直到数据可以被写入才继续执行。如果FIFO不能接收所有数据(通常是在非阻塞模式),它将以下规则执行:

 如果请求写入的数据小于等于FIFO_BUF字节,调用失败,数据不能写入。

 如果请求写入的数据大于FIFO_BUF,将写入部分数据,返回实际写入的字节数,返回值也可能是0

    (为什么要这么设置)

FIFO_BUF是命名管道的长度,这个常量在limits.h中定义,linux和多数unix系统中是4096字节,某些系统中是512字节。

系统规定:一个以阻塞方式打开的命名管道中,如果写入的数据小于FIFO_BUF字节,那么或者全部写入,或者一个字节都不写。这种设置保证了每人个写操作都是原子化的,在多个进程写同一个FIFO文件时可以确保来自各进程的数据不会交错。

四.例子:

两个本来不相关的程序creater.c和consumer.c,它们打开同一个命名管道,creater.c向管道中写入数据,后者从中读出。

creater.c:

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

#define FIFO_NAME "fifo"
#define BUFFER_SIZE PIPE_BUF

int main()
{
    int pipe_fd;
    int res;
    
    int open_mode = O_WRONLY;
    int bytes_send;
    char buffer[BUFFER_SIZE + 1] = "access(判断是否具有存取文件的权限)相关函数 stat,open,chmod,chown,setuid,setgid";

    if( -1 == access(FIFO_NAME, F_OK))        //判断FIFO_NAME文件是否存在
    {
        res = mkfifo(FIFO_NAME, 0777);        //不存在则创建
        if(0 != res)
        {
            fprintf(stderr, "Could not create fifo %s \n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }

    pipe_fd = open(FIFO_NAME, open_mode);        //打开FIFO文件
    printf("Process %d result %d \n", getpid(), pipe_fd);
    
    if( -1 != pipe_fd)
    {
        res = write(pipe_fd, buffer, BUFFER_SIZE);    //向FIFO文件写入数据
        printf("res: %d \n", res);
        bytes_send += res;
        close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }

    printf("Process %d finished\n\n", getpid());
    exit(EXIT_SUCCESS);
}


consumer.c:

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

#define FIFO_NAME "fifo"
#define BUFFER_SIZE PIPE_BUF

int main()
{
    int pipe_fd;
    int res;
    
    int open_mode = O_RDONLY;
    int bytes_read;
    char buffer[BUFFER_SIZE + 1];
    
    memset(buffer, 0, sizeof(buffer));
    
    printf("Process %d opening FIFO_NAME O_RDONLY", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);                //打开FIFO文件
    printf("Process %d result %d \n", getpid(), pipe_fd);
    
    if(-1 != pipe_fd)
    {
        do
        {
            res = read(pipe_fd, buffer, BUFFER_SIZE);    //从管道中读取数据
            printf("%s", buffer);
            memset(buffer, 0, sizeof(buffer));
            bytes_read += res;
        }while(res > 0);
        
        printf("\n");
        close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finished, %d bytes read\n\n", getpid(), bytes_read);
    exit(EXIT_SUCCESS);
}

你可能感兴趣的:(不相关进程间通信--命名管道(FIFO文件))