当前位置: 首页 > news >正文

【管道 】

【管道】

  • 目录:
      • 进程间通信(IPC)概览
      • 匿名管道(PIPE)
        • 基本逻辑
        • 函数接口
        • 管道的读写特性
        • 管道的阻塞特性
        • 注意事项
        • 代码示例
        • 运行示例
      • 相关知识点
      • 管道的父进程 子进程
      • 管道的创建
      • 父进程和子进程的使用模式
        • 单向通信
        • 双向通信
      • 资源管理

目录:

在这里插入图片描述

进程间通信(IPC)概览

进程间通信(Inter - Process Communication,IPC)是指在不同进程之间传播或交换信息的机制。在 Linux 系统中,常见的 IPC 方式有匿名管道(PIPE)、命名管道(FIFO)、消息队列、共享内存、信号量等。不同的 IPC 方式适用于不同的场景,例如,匿名管道适用于具有亲缘关系的进程间通信,而命名管道可以用于无亲缘关系的进程间通信

匿名管道(PIPE)

基本逻辑

匿名管道是一种半双工的通信方式,数据只能在一个方向上流动,即一端用于写入数据,另一端用于读取数据。匿名管道只能用于具有亲缘关系的进程(如父子进程)之间的通信。在创建管道时,系统会为管道分配两个文件描述符,一个用于读操作(fd[0]),另一个用于写操作(fd[1])。

函数接口

在 Linux 中,使用 pipe 函数来创建匿名管道,其原型如下:

#include <unistd.h>
int pipe(int fd[2]);
  • 参数fd 是一个包含两个整数的数组,fd[0] 用于读取管道中的数据,fd[1] 用于向管道中写入数据。
  • 返回值:成功时返回 0,失败时返回 -1,并设置相应的错误码。
管道的读写特性
  • 写入操作:向管道中写入数据时,数据会被追加到管道的末尾。如果管道已满,写入操作会被阻塞,直到管道中有足够的空间。
  • 读取操作:从管道中读取数据时,数据会从管道的头部开始被读取。如果管道为空,读取操作会被阻塞,直到有数据被写入管道。
    在这里插入图片描述
管道的阻塞特性
  • 写入阻塞当管道已满(默认情况下管道的缓冲区大小为 65536 字节),写入进程会被阻塞,直到有其他进程从管道中读取数据,释放出足够的空间
  • 读取阻塞:当管道为空时,读取进程会被阻塞,直到有其他进程向管道中写入数据。如果所有的写端文件描述符都已关闭,读取操作将不会阻塞,而是返回 0,表示已经读取到文件末尾。
注意事项
  • 半双工通信:匿名管道是半双工的,数据只能在一个方向上流动。如果需要双向通信,需要创建两个管道。
  • 亲缘关系匿名管道只能用于具有亲缘关系的进程之间的通信,通常是父子进程
  • 文件描述符关闭:在使用完管道后,需要及时关闭不再使用的文件描述符,避免资源泄漏。
代码示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define BUFFER_SIZE 1024

int main() {
    int fd[2];
    pid_t pid;
    char buffer[BUFFER_SIZE];

    // 创建管道
    if (pipe(fd) == -1) {
        perror("pipe");
        return 1;
    }

    // 创建子进程
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程
        close(fd[0]); // 关闭读端

        const char *message = "Hello, parent process!";
        // 向管道中写入数据
        if (write(fd[1], message, strlen(message)) == -1) {
            perror("write");
            return 1;
        }
        close(fd[1]); // 关闭写端
        exit(0);
    } else {
        // 父进程
        close(fd[1]); // 关闭写端

        // 从管道中读取数据
        ssize_t bytes_read = read(fd[0], buffer, BUFFER_SIZE);
        if (bytes_read == -1) {
            perror("read");
            return 1;
        }
        buffer[bytes_read] = '\0';
        printf("父进程收到消息: %s\n", buffer);

        close(fd[0]); // 关闭读端
    }

    return 0;
}
运行示例
shaseng@ubuntu:~$ gcc -o pipe_example pipe_example.c
shaseng@ubuntu:~$ ./pipe_example
父进程收到消息: Hello, parent process!

相关知识点

  • 文件描述符:在 Linux 系统中,文件描述符是一个非负整数,用于标识打开的文件、管道、套接字等。pipe 函数返回的两个文件描述符分别用于管道的读和写操作。
  • fork 函数fork 函数用于创建一个新的进程,新进程是原进程的子进程。在使用匿名管道进行通信时,通常先创建管道,然后使用 fork
    函数创建子进程,父子进程通过管道进行通信。
  • 读写操作write 函数用于向文件描述符中写入数据,read 函数用于从文件描述符中读取数据。在管道通信中,通过 write 函数向管道的写端写入数据,通过 read 函数从管道的读端读取数据。

管道的父进程 子进程

在使用匿名管道进行进程间通信时,父进程和子进程扮演着不同的角色,下面从创建、使用和资源管理等方面详细介绍管道中父进程和子进程的相关内容。

管道的创建

在使用匿名管道进行进程间通信时,通常是由父进程先创建管道,再创建子进程。这是因为匿名管道只能在具有亲缘关系的进程(如父子进程)间使用,且管道的文件描述符是通过 pipe 函数创建的。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
    int fd[2];
    if (pipe(fd) == -1) {
        perror("pipe");
        return 1;
    }
    // 后续可创建子进程
    return 0;
}

在上述代码中,父进程调用 pipe 函数创建了一个管道,fd[0] 用于读,fd[1] 用于写。

在这里插入图片描述

父进程和子进程的使用模式

单向通信
  • 父进程写,子进程读
    父进程关闭读端(fd[0]),向写端(fd[1])写入数据;子进程关闭写端(fd[1]),从读端(fd[0])读取数据。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 1024

int main() {
    int fd[2];
    pid_t pid;
    char buffer[BUFFER_SIZE];

    if (pipe(fd) == -1) {
        perror("pipe");
        return 1;
    }

    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程
        close(fd[1]);  // 关闭写端
        ssize_t bytes_read = read(fd[0], buffer, BUFFER_SIZE);
        if (bytes_read == -1) {
            perror("read");
            return 1;
        }
        buffer[bytes_read] = '\0';
        printf("子进程收到消息: %s\n", buffer);
        close(fd[0]);
    } else {
        // 父进程
        close(fd[0]);  // 关闭读端
        const char *message = "Hello, child process!";
        if (write(fd[1], message, strlen(message)) == -1) {
            perror("write");
            return 1;
        }
        close(fd[1]);
    }

    return 0;
}
  • 子进程写,父进程读
    子进程关闭读端(fd[0]),向写端(fd[1])写入数据;父进程关闭写端(fd[1]),从读端(fd[0])读取数据。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 1024

int main() {
    int fd[2];
    pid_t pid;
    char buffer[BUFFER_SIZE];

    if (pipe(fd) == -1) {
        perror("pipe");
        return 1;
    }

    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程
        close(fd[0]);  // 关闭读端
        const char *message = "Hello, parent process!";
        if (write(fd[1], message, strlen(message)) == -1) {
            perror("write");
            return 1;
        }
        close(fd[1]);
    } else {
        // 父进程
        close(fd[1]);  // 关闭写端
        ssize_t bytes_read = read(fd[0], buffer, BUFFER_SIZE);
        if (bytes_read == -1) {
            perror("read");
            return 1;
        }
        buffer[bytes_read] = '\0';
        printf("父进程收到消息: %s\n", buffer);
        close(fd[0]);
    }

    return 0;
}
双向通信

若要实现父子进程的双向通信,需要创建两个管道,一个用于父进程向子进程发送数据,另一个用于子进程向父进程发送数据。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 1024

int main() {
    int fd1[2], fd2[2];
    pid_t pid;
    char buffer[BUFFER_SIZE];

    if (pipe(fd1) == -1 || pipe(fd2) == -1) {
        perror("pipe");
        return 1;
    }

    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程
        close(fd1[1]);  // 关闭父->子管道的写端
        close(fd2[0]);  // 关闭子->父管道的读端

        // 从父进程读取数据
        ssize_t bytes_read = read(fd1[0], buffer, BUFFER_SIZE);
        if (bytes_read == -1) {
            perror("read");
            return 1;
        }
        buffer[bytes_read] = '\0';
        printf("子进程收到父进程消息: %s\n", buffer);

        // 向父进程发送数据
        const char *message = "Hello back, parent!";
        if (write(fd2[1], message, strlen(message)) == -1) {
            perror("write");
            return 1;
        }

        close(fd1[0]);
        close(fd2[1]);
    } else {
        // 父进程
        close(fd1[0]);  // 关闭父->子管道的读端
        close(fd2[1]);  // 关闭子->父管道的写端

        // 向子进程发送数据
        const char *message = "Hello, child!";
        if (write(fd1[1], message, strlen(message)) == -1) {
            perror("write");
            return 1;
        }

        // 从子进程读取数据
        ssize_t bytes_read = read(fd2[0], buffer, BUFFER_SIZE);
        if (bytes_read == -1) {
            perror("read");
            return 1;
        }
        buffer[bytes_read] = '\0';
        printf("父进程收到子进程消息: %s\n", buffer);

        close(fd1[1]);
        close(fd2[0]);
    }

    return 0;
}

资源管理

在使用完管道后,父子进程都需要及时关闭不再使用的文件描述符,以避免资源泄漏。例如,在上述代码中,子进程和父进程在完成读写操作后,都会调用 close 函数关闭相应的文件描述符

此外,父进程还需要负责回收子进程的资源,避免产生僵尸进程。可以通过调用 waitwaitpid 函数来实现。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main() {
    int fd[2];
    pid_t pid;

    if (pipe(fd) == -1) {
        perror("pipe");
        return 1;
    }

    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程操作
        // ...
        exit(0);
    } else {
        // 父进程操作
        // ...
        int status;
        wait(&status);
    }

    return 0;
}

综上所述,在管道通信中,父进程和子进程需要根据通信需求合理使用管道的读写端,并做好资源管理工作。

相关文章:

  • STM32的HAL库开发---ADC采集内部温度传感器
  • 大疆激光雷达录制的bag包无法解析出topic怎么办?
  • 【Blender】二、建模篇--07,置换修改器
  • 第14篇:Vue Router 高级用法与路由守卫
  • 2025教育与科研领域实战全解析:DeepSeek赋能细分场景深度指南(附全流程案例与资源)
  • Android 实现 RTMP 推流:快速集成指南
  • PyTorch 是如何进行机器学习的
  • stm32108键C-B全调性_动态可视化乐谱钢琴
  • conda 基本命令
  • HttpWatch 9.4.17 Pro网页调试与性能优化 资源工具分享
  • Leetcode-42. Trapping Rain Water [C++][Java]
  • 能否在delete一个指针后继续使用它以及原因
  • MQTT实现智能家居------3、源码分析(超详细)
  • JavaScript中的call方法相关知识点
  • ZLG嵌入式笔记 | 为什么你的网卡工作会不正常?(上
  • 鸿蒙学习-
  • ctf网络安全题库 ctf网络安全大赛答案
  • JSP学习
  • MybatisPlus-注解
  • 【Python基础】Python 环境安装 Win10
  • 炫酷的电商网站设计/seo博客写作
  • 编辑网页的工具有哪些/江门seo外包公司
  • 前几年做那些网站能致富/网络推广渠道排名
  • 招应届培训网页设计/云南seo网站关键词优化软件
  • 新手如何入侵一个网站/企业管理软件
  • 如何在百度做网站/广州seo优化