进程间通信(二)有名管道

进程间通信(二)有名管道

有名管道 / 命名管道

  匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO),也叫命名管道、 FIFO文件。
  有名管道(FIFO)不同于匿名管道之处在于它提供了一个路径名与之关联,以 FIFO的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与 FIFO 创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过FIFO 不相关的进程也能交换数据。
  一旦打开了 FIFO,就能在它上面使用与操作匿名管道和其他文件的系统调用一样的I/O系统调用了(如read()、 write()和close())。与匿名管道一样, FIFO 也有一个写入端和读取端,并且从管道中读取数据的顺序与写入的顺序是一样的。 FIFO 的名称也由此而来:先入先出

匿名管道与有名管道区别:

  1. FIFO 在文件系统中作为一个特殊文件存在,但 FIFO 中的内容却存放在内存中。

  2. 当使用 FIFO 的进程退出后, FIFO 文件将继续保存在文件系统中以便以后使用。

  3. FIFO 有名字,不相关的进程可以通过打开有名管道进行通信

创建命名管道的两种方式

命令创建(mkfifo)

使用 mkfifo 命令创建命名管道:

mkfifo [fifo_name]

进程间通信(二)有名管道

系统调用创建(mkfifo())

  1. 函数原型
 #include <sys/types.h>
 #include <sys/stat.h>

 int mkfifo(const char *pathname, mode_t mode);
  1. 参数
  • pathname: 管道名称路径
  • mode :文件权限,与open 的mode 相同
  1. 返回值
  • 成功返回 0
  • 失败返回-1,设置 errorno

示例1:通过系统调用创建有名管道

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>


int main(){
    int ret = mkfifo("fifo_syscall", 0664);

    if (ret == -1){
        perror("mkfifo");
    }

    return 0;
}

编译执行,可以得到有名管道fifo_syscall
进程间通信(二)有名管道

使用有名管道在两个进程之间进行通信

示例2:两个进程使用有名管道进行通信
写端程序

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


// 向有名管道中写数据
int main(){
     // 1. 判断文件是否存在
    int ret = access("test", F_OK);
    if (ret == -1){
        printf("test not existed, create test fifo\n");

        // 2. 创建管道文件
        ret = mkfifo("test", 0664);
        if (ret == -1){
            perror("mkfifo");
        }
    }
    printf("test existed");

    // 3. 以只写的方式打开管道
    int fd = open("test", O_WRONLY);
    if (fd == -1){
        perror("open");
        exit(0);
    }

    for (int i = 0; i < 100; i++){
        char buf[1024];
        sprintf(buf, "hello, %d\n", i);
        printf("write data: %s\n", buf);
        write(fd, buf, strlen(buf));
        sleep(1);
    }

    close(fd);

    return 0;
}


读端程序:

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


// 从管道读数据
int main(){
    // 1. 打开管道文件
    int fd = open("test", O_RDONLY);
    if (fd == -1){
        perror("open");
        exit(0);
    }

    // 读数据
    while (1){
        char buf[1024];
        int len = read(fd, buf, sizeof(buf)); 
        if (len == 0){
            printf("write client close...\n");
            break;
        }
        printf("recv buf: %s\n", buf);
    }

    close(fd);
    return 0;
}

编译执行,可以看到,读写进程可以通过管道发送数据进行通信
进程间通信(二)有名管道

小结:

  • 一个为只读而打开一个管道的进程会阻塞,直到另一个进程为只写打开管道。
  • 一个为只写而打开一个管道的进程会阻塞,直到另一个进程为只读打开管道。

    与匿名管道相同,命名管道的readwrite 阻塞原理相同,总结如下。

对于 读管道:
  管道中有数据:read 返回实际读到的字节数
  管道中无数据:
    - 管道写端被全部关闭,read 返回 0
    - 管道写端没有被全部关闭,read 阻塞等待

对于 写管道:
  管道读端被全部关闭,进行异常终止(SIGPIPE信号)
  管道读端没有被全部关闭:
    管道已满,write 阻塞
    管道未满,write 将数据写入,并返回实际写入的字节数

一键三连是对我最大的鼓励与支持。欢迎关注编程小镇,每天涨一点新姿势

上一篇:【STM32】STM32 SDIO SD卡读写测试(二)-- SD_Init之Power On阶段


下一篇:先进先出(FIFO)页面置换算法 C语言实现