Linux系统编程 --管道(二)

写在前面

Linux系统编程 --管道主要介绍了匿名管道,本文主要介绍FIFO有名管道

FIFO有名管道概述

  无名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道、FIFO 文件。

  命名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据。

FIFOLinux 基础文件类型中的一种。但 FIFO 文件在磁盘上没有数据块,仅仅用来标识一条通道。 各进程可以打开这个文件进行 read/write,实际上是在读写内核通道,这样就实现了进程间通信。

命名管道(FIFO)和无名管道(pipe)有一些特点是相同的,不一样的地方在于

  1. FIFO 在文件系统中作为一个特殊的文件存在而已, 但是 FIFO 的内用却存放在内存中
  2. 当使用 FIFO 的进程退出后, FIFO 文件将继续保存在文件系统中以便以后使用
  3. FIFO 有名字,不相关的进程可以通过打开命名管道进行进程间通信

命名管道的创建

通过函数创建

函数原型:int mkfifo(const char *pathname, mode_t mode);
作用:创建有名管道

头文件:

  • #include <sys/types.h>
  • #include <sys/stat.h>

参数:

  • pathname: 普通的路径名,也就是创建后 FIFO 的名字。
  • mode: 文件的权限,与打开普通文件的 open() 函数中的 mode 参数相同。

返回值:

  • 成功:返回 0
  • 失败:返回 -1

创建demo

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[]){
	int ret;
	ret = mkfifo("my_fifo", 0644); // 创建命名管道
	if(ret != 0){	// 出错
		perror("mkfifo");
	}
	return 0;
}

通过命令创建

mkfifo + 管道名

我是红色字体

有名管道操作

 后期的操作,把这个命名管道当做普通文件一样进行操作:open()、write()、read()、close()。但是,和无名管道一样,操作命名管道肯定要考虑默认情况下其阻塞特性

下面验证的是默认情况下的特点,即 open() 的时候没有指定非阻塞标志( O_NONBLOCK )。测试如下:

  • open() 以只读方式打开 FIFO 时,要阻塞到某个进程为写而打开此 FIFO
  • open() 以只写方式打开 FIFO 时,要阻塞到某个进程为读而打开此 FIFO。

简单一句话,只读等着只写,只写等着只读,只有两个都执行到,才会往下执行

//写端
#include<stdio.h>
#include <unistd.h>
#include <error.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
    int ret = mkfifo("myfifo",0644);
    if(ret == 0){ 
        perror("mkfifo error");
        exit(1);
    }   
    printf("before open\n");
    int fd = open("myfifo", O_WRONLY);
    if(fd < 0){ 
        perror("open fifo");
        exit(1);
    }   
    printf("after open\n");
    return 0;
}
//读端
#include<stdio.h>
#include <unistd.h>
#include <error.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
    int ret = mkfifo("myfifo",0644);
    if(ret == 0){
        perror("mkfifo error");
        exit(1);
    }
    printf("before open\n");
    int fd = open("myfifo", O_RDONLY);
    if(fd < 0){
        perror("open fifo");
        exit(1);
    }
    printf("after open\n");
    return 0;
}

上述代码证明,只有当读写都在执行时,程序才能执行。

如果大家不想在 open() 的时候阻塞,我们可以以可读可写方式打开 FIFO 文件,这样 open() 函数就不会阻塞。

假如 FIFO 里没有数据,调用 read() 函数从 FIFO 里读数据时 read() 也会阻塞。这个特点和无名管道是一样的

//写端
#include<stdio.h>
#include <unistd.h>
#include <error.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
    int ret = mkfifo("myfifo",0644);
    if(ret == 0){ 
        perror("mkfifo error");
        exit(1);
    }   
    printf("before open\n");
    int fd = open("myfifo", O_WRONLY);
    if(fd < 0){ 
        perror("open fifo");
        exit(1);
    }   
    sleep(5); //让写端睡眠,看读端的状态
    char *str = "hello fifo\n";;
    write(fd, str, strlen(str));
    return 0;
}
// 写端
#include<stdio.h>
#include <unistd.h>
#include <error.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
    int ret = mkfifo("myfifo",0644);
    if(ret == 0){
        perror("mkfifo error");
        exit(1);
    }
    printf("before open\n");
    int fd = open("myfifo", O_RDONLY);
    if(fd < 0){
        perror("open fifo");
        exit(1);
    }
    char buf[1024];
    int len;
    len = read(fd, buf, sizeof(buf));
    write(STDOUT_FILENO, buf, len);
    return 0;
}

上述代码自行编译运行,才能明白其中奥妙

无聊的测试

pipe 能实现的 fifo 都可以,一对一,一对多,多对一,多对多,都可以,但是数据单向流通这个是前提