5.2 Linux 应用开发 进程(二)

一、管道通信(Pipe、FIFO)

1. 无名管道(Pipe)

(1)核心特性:

  • 本质:一种特殊的文件,由队列实现,只能用pipe()函数创建,不能用open()打开;在文件系统中无文件名、无文件节点,因此叫“无名”管道。
  • 适用场景只能用于有亲缘关系的进程间通信(如父子进程),因为它没有文件系统节点,无法被其他进程打开。
  • 半双工通信:同一时间只能单向传输数据,一端读、一端写。

(2)核心函数:

int pipe(int fd[2]);

// fd[2] 用于保存文件描述符:
// fd[0]:读管道(从管道读取数据)
// fd[1]:写管道(向管道写入数据)
// 返回值:创建成功返回0,创建失败返回-1。

(3)读写特性:

1) 读管道(read()

  • 管道中无数据时:

若管道写端被全部关闭:read()返回0(读到 EOF);

若管道写端未全部关闭:read()阻塞等待,直到有数据写入。

  • 管道中有数据时:read()返回实际读到的字节数。

2) 写管道(write()

  • 管道读端全部关闭:进程会异常终止(收到SIGPIPE信号);
  • 管道读端未全部关闭

管道已满(默认大小64KB = 65536字节):write()阻塞等待

管道未满:write()将数据写入,返回实际写入的字节数。

(4)补充说明:

  • 无名管道本质是一段内存缓冲区(队列形式),数据读完后就会从管道中清除,无法重复读取。
  • fd是两个文件描述符,子进程会继承父进程的文件描述符表;虽然父子进程的fd变量是分离的,但背后指向的文件(管道)是同一个。

(5)使用示例:

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

int main() {
    int fd[2];  // fd[0]读端,fd[1]写端
    pid_t pid;
    char buf[1024];
    const char *msg = "Hello from parent process!";

    // 1. 创建无名管道
    if (pipe(fd) == -1) {
        perror("pipe create failed");
        exit(EXIT_FAILURE);
    }

    // 2. 创建子进程
    pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }

    // 3. 子进程:从管道读数据
    if (pid == 0) {
        close(fd[1]);  // 子进程不需要写,关闭写端(必须!避免阻塞)
        
        // 读取管道数据
        ssize_t len = read(fd[0], buf, sizeof(buf)-1);
        if (len == -1) {
            perror("child read failed");
            exit(EXIT_FAILURE);
        } else if (len == 0) {
            printf("Child: pipe write end closed, no data\n");
        } else {
            buf[len] = '\0';
            printf("Child received: %s\n", buf);
        }

        close(fd[0]);  // 读完关闭读端
        exit(EXIT_SUCCESS);
    }
    // 4. 父进程:向管道写数据
    else {
        close(fd[0]);  // 父进程不需要读,关闭读端(必须!)
        
        // 向管道写入数据
        ssize_t len = write(fd[1], msg, strlen(msg));
        if (len == -1) {
            perror("parent write failed");
            exit(EXIT_FAILURE);
        }
        printf("Parent sent: %s\n", msg);

        close(fd[1]);  // 写完关闭写端,让子进程read不再阻塞
        wait(NULL);    // 等待子进程退出,避免僵尸进程
    }

    return 0;
}

2. 有名管道(FIFO)

(1)核心特性:

  • 适用场景:支持非亲缘关系的进程间通信,因为创建的有名管道在文件系统中可见(是一个特殊的管道文件),可被任意进程通过文件名打开。
  • 本质:是一个存在于文件系统中的特殊管道文件,数据实际存放在内存中,不占用磁盘空间。
  • 半双工通信:同一时间只能单向传输数据。
  • 其他特性:遵循先进先出(FIFO)原则;不支持lseek()文件定位(只能顺序读写);支持标准文件 I/O 操作。

(2)核心函数:

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

// filename:要创建的有名管道文件的路径 / 名称
// mode:文件权限(如0666,需结合umask生效)
// 返回值:创建成功返回0,失败返回-1
// 作用:创建一个先进先出的特殊管道文件,只需在一个进程中创建,其他进程直接通过文件名正常读写即可。

(3)读写特性:

  • 读端、写端的阻塞情况与无名管道(Pipe)完全一致:

读空且写端未关:读阻塞;读空且写端全关:read( ) 返回 0;

写满且读端未关:写阻塞;读端全关:写进程异常终止。

(4)使用示例:

// 写进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include 

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++/嵌入式开发 秋招面经 文章被收录于专栏

一名985硕,在25年秋招中斩获多个C++/嵌入式开发Offer。本专栏将分享我的面经,涵盖C/C++、操作系统、计算机网络、ARM体系与架构、Linux应用/驱动开发、Qt、通信协议及开发工具链等核心内容。

全部评论

相关推荐

点赞 评论 收藏
分享
评论
5
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务