工学1号馆

home

« | 返回首页 | »

初识 FIFO

By Wu Yudong on December 09, 2016

FIFO 指代先进先出,unix 中 FIFO 与管道类似,是一个单向(半双工)的数据流;区别在于每个FIFO都有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO,而管道没有名字,只能用于有一个共同祖先进程的各个进程之间。所有FIFO有时也称为有名管道。

FIFO由mkfifo函数创建,函数原型如下:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char* pathname,  mode_t mode);

其中pathname是一个普通的Unix路径名,它是该FIFO的名字。mode参数指定文件权限位,类似于open的第二个参数

mkfifo 已隐含指定 O_CREAT|O_EXCL。即它要么创建一个新的 FIFO,要么返回一个 EEXIST 错误。

要打开一个已存在的 FIFO 或创建一个新的 FIFO ,应先调用 mkfifo,检查它是否返回EEXIST错误(说明已经存在,这样可以直接打开),若返回 EEXIST 则改为调用open。

接下来使用两个 FIFO 代替两个pipe来重写《初识管道》中的例子,实现客户-服务器程序,其中的server与client函数不变,只修改main函数中的代码。

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

#define    MAXLINE    4096
#define FIFO1    "/tmp/fifo.1"
#define FIFO2    "/tmp/fifo.2"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void client(int readfd, int writefd)
{
    size_t len;
    ssize_t n;
    char buf[MAXLINE];

    fgets(buf, MAXLINE, stdin);
    len = strlen(buf);

    if(buf[len-1] == '\n')
      len--;

    write(writefd, buf, len);

    while((n = read(readfd, buf, MAXLINE)) > 0)
      write(STDOUT_FILENO, buf, n);
}

void server(int readfd, int writefd)
{
    int fd;
    ssize_t n;
    char buf[MAXLINE + 1];

    if((n = read(readfd, buf, MAXLINE)) == 0) {
        printf("read end of file!");
        exit(0);
    }
    buf[n] = '\0';

    if((fd = open(buf, 'r'))< 0) {
        snprintf(buf+n, sizeof(buf)-n,": can't open, %s\n", strerror(errno));
        n = strlen(buf);
        write(writefd, buf, n);
    } else {
        while((n = read(fd, buf, MAXLINE)) > 0)
          write(writefd, buf, n);
        close(fd);
    }
}

int main(int argc, char **argv)
{    
    int readfd, writefd;
    pid_t childpid;

    if((mkfifo(FIFO1, FILE_MODE) < 0) && errno != EEXIST) {
        printf("can't create %s\n", FIFO1);
        exit(1);
    }
    if((mkfifo(FIFO2, FILE_MODE) < 0) && errno != EEXIST) {
        unlink(FIFO1);    //删除创建的FIFO并释放
        printf("can't create %s", FIFO2);
        exit(2);
    }
    if((childpid = fork()) == 0) {    //子进程
        readfd = open(FIFO1, O_RDONLY, 0);
        writefd = open(FIFO2, O_WRONLY, 0);

        server(readfd, writefd);
        exit(0);
    }
    
    writefd = open(FIFO1, O_WRONLY, 0);
    readfd = open(FIFO2, O_RDONLY, 0);

    client(readfd, writefd);

    waitpid(childpid, NULL, 0);

    close(readfd);
    close(writefd);

    unlink(FIFO1);
    unlink(FIFO2);
    exit(0);
}

编译运行:

wu@ubuntu:~/myapue$ ./usefifo
fifotest
hello wuyudong
FIFO test

open的顺序不对的话,程序有可能不能执行,如果互换父进程的open调用顺序,这时没有进程打开FIFO来写,于是父子进程打开FIFO来读都会产生阻塞,这种现象称为死锁

上面的程序客户和服务器仍然有亲缘关系,下面重写代码实现无亲缘关系

server_fifo.c

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

#define    MAXLINE    4096
#define FIFO1    "/tmp/fifo.1"
#define FIFO2    "/tmp/fifo.2"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void server(int readfd, int writefd)
{
    int fd;
    ssize_t n;
    char buf[MAXLINE + 1];

    if((n = read(readfd, buf, MAXLINE)) == 0) {
        printf("read end of file!");
        exit(0);
    }
    buf[n] = '\0';

    if((fd = open(buf, 'r'))< 0) {
        snprintf(buf+n, sizeof(buf)-n,": can't open, %s\n", strerror(errno));
        n = strlen(buf);
        write(writefd, buf, n);
    } else {
        while((n = read(fd, buf, MAXLINE)) > 0)
          write(writefd, buf, n);
        close(fd);
    }
}


int main(int argc, char **argv)
{
    int readfd, writefd;

    if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST)) {
        printf("can't create %s", FIFO1);
        exit(1);
    }
    if((mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST)) {
        unlink(FIFO1);
        printf("can't create %s", FIFO2);
        exit(1);
    }

    readfd = open(FIFO1, O_RDONLY, 0);
    writefd = open(FIFO2, O_WRONLY, 0);

    server(readfd, writefd);

    exit(0);
}

client_fifo.c

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

#define    MAXLINE    4096
#define FIFO1    "/tmp/fifo.1"
#define FIFO2    "/tmp/fifo.2"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void client(int readfd, int writefd)
{
    size_t len;
    ssize_t n;
    char buf[MAXLINE];

    fgets(buf, MAXLINE, stdin);
    len = strlen(buf);

    if(buf[len-1] == '\n')
      len--;

    write(writefd, buf, len);

    while((n = read(readfd, buf, MAXLINE)) > 0)
      write(STDOUT_FILENO, buf, n);
}

int main(int argc, char **argv)
{    
    int readfd, writefd;
    
    writefd = open(FIFO1, O_WRONLY, 0);
    readfd = open(FIFO2, O_RDONLY, 0);

    client(readfd, writefd);

    close(readfd);
    close(writefd);

    unlink(FIFO1);
    unlink(FIFO2);
    exit(0);
}

首先在后台运行服务器,接着运行客户端:

wu@ubuntu:~/myapue$ gcc server_fifo.c -o server_fifo
wu@ubuntu:~/myapue$ gcc client_fifo.c -o client_fifo
wu@ubuntu:~/myapue$ ./server_fifo &
[1] 10471
wu@ubuntu:~/myapue$ ./client_fifo
fifotest
hello wuyudong
FIFO test

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)