Linux进程通信之管道

目录

1、无名管道

1.无名管道的特点

2.pipe函数创建管道

3.图例

2、命名管道(FIFO)

1.命名管道的特点

[2.mkfifo 函数-创建命名管道](#2.mkfifo 函数-创建命名管道)

3.示例

1.循环读取数据

2.循环写入数据


1、无名管道

管道通常指的就是无名管道,

1.无名管道的特点

  1. 管道是半双工的,只支持数据的单向传输,有固定的读端和写端。
  2. 无名管道用于有亲缘关系的进程间通信
  3. 无名管道可以看作一种特殊的文件,对它的读写我们可以直接使用普通的read、write等函数进行操作,但是注意无名管道并不是普通的文件,它不存在任何的文件系统中,只存在于内存中
  4. 管道里的数据被读走了就会从管道中消失

2.pipe函数创建管道

pipe 函数是用于创建管道的系统调用,通常用于在父子进程之间进行单向通信

函数原型

int pipe(int pipefd2);

参数:

  • pipefd: 一个包含两个文件描述符的整数数组,pipefd[0] 用于读取数据,pipefd[1] 用于写入数据。

返回值:

  • 如果成功,返回 0。
  • 如果失败,返回 -1,并设置 errno 指示错误类型。

pipe函数会创建两个文件描述符,pipefd0是读端、pipefd1是写端

需要注意的是,pipe 函数创建的管道是阻塞的,如果管道已满(写端写入速度过快),写操作将会被阻塞,直到有空间可用

3.图例

如果要使数据从父进程流向子进程就需要关闭父进程的读端和子进程的写端,反之就使数据从子进程流向父进程

复制代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {
    int fd[2];            // 用于存储管道的文件描述符,fd[0] 为读取端,fd[1] 为写入端
    int pid;              // 用于存储 fork 函数的返回值,表示当前是父进程还是子进程
    char buf[128] = {0};  // 用于存储从管道中读取的数据

    // 创建管道
    if (pipe(fd) == -1) {
        printf("pipe 创建失败\n");
        perror("pipe");
        exit(-1);  // 如果 pipe 创建失败,退出程序,返回 -1
    }

    // 创建子进程
    pid = fork();
    if (pid < 0) {
        printf("fork 创建失败\n");
        perror("fork");
        exit(-1);  // 如果 fork 创建失败,退出程序,返回 -1
    } else if (pid > 0) {  // 父进程
        printf("parent process\n");

        close(fd[0]);  // 关闭读取端,因为父进程只负责写入数据
        write(fd[1], "holle wjh happy", strlen("holle wjh happy"));  // 向管道写入数据
        wait(NULL);  // 等待子进程结束

    } else {  // 子进程
        printf("child process\n");

        close(fd[1]);  // 关闭写入端,因为子进程只负责读取数据
        read(fd[0], buf, 128);  // 从管道读取数据到 buf 中
        printf("read: %s\n", buf);

        exit(0);  // 子进程正常结束,返回 EXIT_SUCCESS
    }

    return 0;
}

2、命名管道(FIFO)

命名管道(Named Pipe)是一种特殊类型的管道,与无名管道(通过 pipe 创建的)不同,它具有文件系统中的名字,并且可以通过文件系统路径来访问。命名管道在进程间通信中提供了一种有趣而强大的方式。

1.命名管道的特点

  1. 命名管道是通过在文件系统中创建一个具有名字的特殊文件来实现的。这个文件的路径就是命名管道的名字
  2. 命名管道的使用方式与文件类似,可以使用标准的文件读写操作函数来进行进程间通信。打开命名管道后,可以使用 readwrite 等函数进行数据传输。
  3. 命名管道常用于独立的进程之间通信。在这种情况下,各个进程可以通过打开相同路径名的命名管道文件来进行通信,也就是说命名管道可以在无关进程之间进行通信,这里和无名管道不同
  4. 像无名管道一样,命名管道的读写操作也是阻塞的。如果没有数据可读,读取操作会被阻塞,直到有数据为止;如果管道已满,写入操作会被阻塞,直到有空间可用。

2.mkfifo 函数-创建命名管道

函数原型

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

mkfifo是 "make FIFO" 的缩写,用于创建一个新的命名管道

参数:

  • pathname:要创建的命名管道的路径名。
  • mode:创建的文件权限。通常使用八进制表示,例如 0666

返回值:

  • 如果成功创建命名管道,返回 0。
  • 如果失败,返回 -1,并设置 errno 表示具体的错误类型。

注意:

  • 在成功创建命名管道后,可以使用标准的文件操作函数(如 openreadwrite 等)来进行读写操作。
  • 如果已经存在同名的普通文件或目录,mkfifo 会失败。

3.示例

1.循环读取数据
复制代码
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>

int main() {
    char buf[30] = {0};

    // 尝试创建命名管道
    if (mkfifo("./file", 0600) == -1 && errno != EEXIST) {
        printf("mkfifo 失败!!!\n");
        perror("mkfifo");
        exit(EXIT_FAILURE);
    } else if (errno == EEXIST) {
        perror("mkfifo");
        // 如果管道已经存在,继续执行
    }

    // 打开命名管道进行读取
    int fd = open("./file", O_RDONLY);
    if (fd == -1) {
        printf("open 失败!!!\n");
        perror("open");
        exit(EXIT_FAILURE);
    } else {
        // 循环读取数据
        while (1) {
            int read_count = read(fd, buf, 20);
            if (read_count > 0) {
                printf("read_count = %d, reads = %s\n", read_count, buf);
            } else if (read_count == 0) {
                // 如果 read 返回 0,表示已经读到文件末尾,可以采取适当的处理方式
                printf("End of file reached. Exiting...\n");
                break;
            } else {
                // 处理 read 函数返回的错误
                perror("read");
                break;
            }
        }
        // 关闭文件描述符
        close(fd);
    }

    return 0;
}
2.循环写入数据
复制代码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    // 创建一个名为 "file" 的命名管道
    if (mkfifo("./file", 0600) == -1 && errno != EEXIST) {
        // 如果创建失败且不是因为文件已存在,则输出错误信息并退出程序
        printf("mkfifo 失败!!!\n");
        perror("mkfifo");
        exit(EXIT_FAILURE);
    } else if (errno == EEXIST) {
        // 如果管道已经存在,则输出相应的错误信息
        perror("mkfifo");
    }

    // 打开命名管道进行写入
    int fd = open("./file", O_WRONLY);
    if (fd == -1) {
        // 如果打开失败,则输出错误信息
        perror("open");
    } else {
        // 在循环中每秒写入一次数据到管道中
        while (1) {
            sleep(1);
            int write_count = write(fd, "happy bay wjh", strlen("happy bay wjh"));
            printf("write_count = %d\n", write_count);
        }

        // 关闭文件描述符
        close(fd);
    }

    return 0;
}
相关推荐
RisunJan43 分钟前
Linux命令-slabtop(实时显示内核 slab 缓存信息)
linux
蚰蜒螟3 小时前
从内核源码看Linux启动:chroot、execve与MS_MOVE的协奏曲
linux·服务器·网络
酷可达拉斯3 小时前
自动化运维-ansible配置文件与主机清单
linux·运维·自动化·ansible
影视飓风TIM4 小时前
Linux下C语言缓冲区原理 + Git版本控制
linux·c语言·git
来者皆善4 小时前
ZYNQ linux上使用 USB CDC ACM
linux·运维·服务器
Dawn-bit6 小时前
Linux磁盘分区与Swap和磁盘故障查询
linux·运维·服务器·网络·云计算
无足鸟ICT7 小时前
【RHCA+】$[]
linux·运维·服务器
运维技术小记8 小时前
国产化环境配置 VNC 远程桌面:麒麟 V10 实战
linux·运维·服务器
xiaoye-duck8 小时前
《Linux系统编程》Linux 系统多线程(八): C++ 高并发线程池全链路深度解析与从零手撕实现
linux·c++·线程池
寒水馨9 小时前
Linux下载、安装protobuf-v35.1(附安装包protoc-35.1-linux-x86_64.zip)
linux·运维·服务器·google·序列化·protobuf·protoc