linux编程之pipe()函数详解

Linux编程之pipe()函数详解

在Linux编程中,pipe()是一个重要的函数,用于在两个进程之间创建一个管道,从而实现进程间通信。本文将详细讲解pipe()函数的使用方法、注意事项及示例说明。

管道的创建

调用pipe()函数可以创建一个管道,该函数的原型如下:

#include <unistd.h>

int pipe(int pipefd[2]);

其中,pipefd是一个整型数组,用于存储管道两端的文件描述符。pipefd[0]代表读端,pipefd[1]代表写端。

需要注意的是,pipe()函数必须在创建进程之前调用,这是因为管道只能在父进程和子进程之间进行通信,而且在fork函数之后,父进程和子进程拥有相同的文件描述符表,因此在子进程调用pipe()函数创建管道已经来不及了。

管道的通信

管道的通信是通过read()write()函数完成的,其中读操作和写操作分别在不同的进程中进行。如下图所示,假设有两个进程P1和P2,通过pipe()函数创建一个管道,并且P1写入数据到管道中,P2从管道中读出数据。

+------+         +------+
|  P1  |         |  P2  |
+------+         +------+
   |  ------------->  |
   |                 |
   |                 |
   |  <-------------  |
   |                  |
+--------+         +--------+
| Write  |         |  Read  |
|  end   |         |  end   |
+--------+         +--------+

写端

在写端进程中,我们需要取得管道的写文件描述符,并通过write()函数将数据写入管道中,示例如下:

char *buffer = "Hello pipe!";

// 创建管道
int pipefd[2];
if (pipe(pipefd) == -1) {
    perror("pipe failed");
    exit(EXIT_FAILURE);
}

// 子进程中写入数据
if (fork() == 0) {
    close(pipefd[0]); // 关闭读端
    write(pipefd[1], buffer, strlen(buffer));
    close(pipefd[1]);
    exit(EXIT_SUCCESS);
}

上述代码中,先创建了一个包含字符串数据的缓冲区buffer,随后通过pipe()函数创建了一个管道,并在子进程中进行写入操作。管道的写文件描述符为pipefd[1],所以需要先关闭读端文件描述符pipefd[0],然后使用write()函数将数据写入管道,最后关闭写文件描述符pipefd[1]

读端

在读端进程中,我们需要取得管道的读文件描述符,并通过read()函数从管道中读取数据,示例如下:

char buffer[BUFSIZ];

// 创建管道
int pipefd[2];
if (pipe(pipefd) == -1) {
    perror("pipe failed");
    exit(EXIT_FAILURE);
}

// 父进程中读取数据
if (fork() > 0) {
    close(pipefd[1]); // 关闭写端
    int len = read(pipefd[0], buffer, BUFSIZ - 1);
    buffer[len] = '\0';
    printf("Received message: %s\n", buffer);
    close(pipefd[0]);
    exit(EXIT_SUCCESS);
}

上述代码中,先创建了一个缓冲区buffer,随后通过pipe()函数创建了一个管道,并在父进程中进行读取操作。管道的读文件描述符为pipefd[0],所以需要先关闭写端文件描述符pipefd[1],然后使用read()函数从管道中读取数据,并将其保存到缓冲区中,最后打印出接收到的数据并关闭读文件描述符pipefd[0]

注意事项

在使用pipe()函数时,需要注意以下几点:

  • 管道是一个单向通信机制,一端写入后只能在另一端读取,不能同时读写。
  • 管道的容量是有限的,写入数据超过管道容量时会阻塞,除非使用特殊标志。
  • 管道的文件描述符不是永久性的,程序退出时会自动关闭。

示例说明

示例一:基本使用

上述代码中的示例即为管道的基本使用示例,实现了进程间的单向通信。

示例二:多进程通信

可使用多个子进程向同一管道中写入数据,父进程从中读取,并统计接收到的数据总和。

具体实现代码及解释如下:

// 定义进程数
#define PROCESS_NUM 3

// 主函数
int main(int argc, char *argv[]) {
    char buffer[BUFSIZ];

    // 创建管道
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        perror("pipe failed");
        exit(EXIT_FAILURE);
    }

    // 创建多个子进程并向管道中写入数据
    for (int i = 0; i < PROCESS_NUM; i++) {
        if (fork() == 0) { // 子进程中
            close(pipefd[0]); // 关闭读端
            sprintf(buffer, "Message from child %d\n", i);
            write(pipefd[1], buffer, strlen(buffer));
            close(pipefd[1]);
            exit(EXIT_SUCCESS);
        }
    }

    // 父进程中读取并统计数据
    close(pipefd[1]); // 关闭写端
    int sum = 0;
    for (int i = 0; i < PROCESS_NUM; i++) {
        int len = read(pipefd[0], buffer, BUFSIZ - 1);
        buffer[len] = '\0';
        printf("Received message: %s", buffer);
        sum += len;
    }
    printf("Received total %d bytes.\n", sum);
    close(pipefd[0]);
    wait(NULL);

    return 0;
}

上述代码中,先定义了一个常量PROCESS_NUM,表示创建的子进程数。随后创建了一个包含读写文件描述符的管道,并使用fork()函数创建子进程,子进程中向管道中写入一条数据,并在写入完成后关闭写文件描述符。

在父进程中,读取管道中的数据并将其累加,最终打印出接收到的总数据量,并关闭读文件描述符。

通过以上代码,我们可以实现多个子进程同时向同一管道中写入数据,而父进程从中读取,并统计接收到的数据总和,从而实现了多进程之间的通信。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:linux编程之pipe()函数详解 - Python技术站

(0)
上一篇 2023年5月25日
下一篇 2023年5月25日

相关文章

  • 树莓派64位系统安装libjasper-dev显示无法定位软件包问题

    以下是针对“树莓派64位系统安装libjasper-dev显示无法定位软件包问题”的完整攻略。 问题背景 在安装树莓派64位系统时,可能会遇到需要安装libjasper-dev软件包的情况,但是在执行安装命令时会提示“无法定位软件包”的错误信息。 解决方案 方案一:添加软件源后更新 可以尝试先添加armhf架构软件源,并更新软件包列表,再尝试安装libjas…

    人工智能概览 2023年5月25日
    00
  • django 实现手动存储文件到model的FileField

    当我们在使用Django开发Web应用时,常常需要让用户上传文件,比如头像、照片等,我们可以通过使用Django的FileField字段将这些文件存储到数据库中。但是,有时候我们可能需要手动将文件保存到FileField字段所关联的文件中。本文将详细讲解如何在Django中手动保存文件到FileField字段所关联的文件中。 1. 准备工作: 首先,我们需要…

    人工智能概论 2023年5月25日
    00
  • tensorboard 可视化之localhost:6006不显示的解决方案

    当我们使用Tensorboard时,有时候会遇到localhost:6006不显示的问题。这可能是由许多不同的原因所导致的。本攻略将提供一些可能的解决方案。 确认Tensorboard已正确安装 在使用Tensorboard之前,我们需要先确认Tensorboard是否已经正确安装。我们可以在命令行中输入以下命令来检查: tensorboard –vers…

    人工智能概论 2023年5月25日
    00
  • python中redis的安装和使用

    下面是“python中redis的安装和使用”的完整攻略: 一、安装redis 在使用redis之前,我们需要先安装redis。以下提供两种安装redis的方法。 1.1 在Ubuntu上安装redis 在Ubuntu上安装redis非常简单,只需要使用apt-get命令即可: sudo apt-get install redis-server 1.2 在W…

    人工智能概览 2023年5月25日
    00
  • MongoDB设计方法以及技巧示例详解

    MongoDB设计方法以及技巧示例详解 在使用 MongoDB 设计数据库时,需要考虑如何设置数据结构和索引,以及如何查询和优化查询。下面将介绍一些 MongoDB 的设计方法和技巧,并且提供两个示例帮助理解。 MongoDB 数据结构设计 MongoDB 是一种文档型数据库,数据以 BSON 格式存储。设计数据结构时,需要考虑如何组织数据和关联数据。 设计…

    人工智能概览 2023年5月25日
    00
  • pytorch随机采样操作SubsetRandomSampler()

    PyTorch 中的 SubsetRandomSampler 类是一种用于随机采样数据集的方法。它可以用于生成一个索引列表,该列表可以被 DataLoader 类(或其他任何需要索引列表的类)用于加载数据集子集。 使用方法示例 下面是使用 SubsetRandomSampler 的基本方法: import torch from torch.utils.dat…

    人工智能概论 2023年5月25日
    00
  • ASP.NET session.timeout设置案例详解

    ASP.NET Session.Timeout 设置案例详解 什么是 ASP.NET Session.Timeout ASP.NET Session.Timeout 是指在一定的时间段内,如果客户端没有向服务器发送任何请求,那么服务器就会自动销毁客户端的会话信息。 如何设置 ASP.NET Session.Timeout 在 ASP.NET 中,我们可以通过…

    人工智能概论 2023年5月25日
    00
  • C++ OpenCV中几种基本的图像处理方式

    C++ OpenCV是一种广泛使用的图像处理库,它提供了多种基本的图像处理方式,主要包括以下几种: 基本的图像处理方式 图像读取 在OpenCV中,使用cv::imread函数可以读取图像,该函数接受两个参数:文件名和读取标志。例如,以下代码读取名为“lena.jpg”的图像并将其显示在窗口中: #include <opencv2/opencv.hpp…

    人工智能概览 2023年5月25日
    00
合作推广
合作推广
分享本页
返回顶部