news 2026/5/5 13:52:53

Linux学习笔记(十四)--进程间通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux学习笔记(十四)--进程间通信

进程间通信目的

数据传输:一个进程需要将它的数据发送给另一个进程

资源共享:多个进程之间共享同样的资源。

通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。

本质

让不同进程看到同一份资源。

“资源”是一种特定形式的内存空间。
这个资源一般是由操作系统提供,而不是两个进程的其中一个,因为假设由一个进程提供,那么这个资源属于谁,是这个进程独有,那么就会破坏进程的独立性。
我们进程访问这个空间进行通信,本质就是访问操作系统,进程代表的就是用户,资源从创建到使用(一般),再到释放,系统会提供一个系统调用接口来实现(从底层设计,从接口设计都要由操作系统独立设计)所以一般操作系统会有一个独立的通信模块,这个模块隶属于文件系统,这个模块叫做IPC通信模块。

IPC的作用:提供一种受控的机制,允许数据跨越进程边界流动,同时不破坏操作系统的隔离保护。

进程间通信发展

管道(基于文件级别的通信)-->System V进程间通信(本机内部通信)-->POSIX进程间通信(网络通信)

进程间通信分类

管道

概念

管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

本质

具有“血缘关系”的进程(通常是父子进程或兄弟进程)间的单向字节流。

接口

pipe

基本语法
#include <unistd.h> int pipe(int pipefd[2]);
参数

pipefd[2]:一个包含2个整数的数组
pipefd[0]:用于读取管道的文件描述符
pipefd[1]:用于写入管道的文件描述符

若成功则返回0,不成功则返回-1

创建pipe
#include <unistd.h> #include <stdio.h> int main() { int pipefd[2]; if (pipe(pipefd) == -1) { perror("pipe failed"); return 1; } printf("Pipe created successfully!\n"); printf("Read end: fd = %d\n", pipefd[0]); printf("Write end: fd = %d\n", pipefd[1]); // 使用管道... // 记得关闭文件描述符 close(pipefd[0]); close(pipefd[1]); return 0; }

特点

匿名管道
概念

通过pipe()系统调用创建,存在于内核中,没有文件系统入口。只能用于有亲缘关系的进程。

关键特性

单向性:数据只能从一端写入,从另一端读取。这形成了经典的“生产者-消费者”模型。

亲缘关系限制:通常由父进程创建,然后通过fork()将管道的文件描述符复制给子进程,从而实现通信。

字节流导向:不维护消息边界。写入端多次写入的"Hello""World",在读取端可能被一次读取为"HelloWorld"。应用层需要自己定义消息分隔协议。

生命周期随进程:当所有引用该管道的进程都终止后,管道资源会被内核自动回收。

工作原理

(1)创建管道:父进程调用 int pipe(int fd[2])系统调用。内核会创建一个管道,并返回两个文件描述符:fd[0]:用于读取管道。fd[1]:用于写入管道。

(2)创建子进程:父进程调用 fork()。此时,子进程继承了父进程打开的文件描述符表,因此它也拥有指向同一个内核管道的fd[0]和fd[1]。

(3)关闭不需要的端口:由于管道是单向的,为了让数据从父流向子:父进程关闭它的读端 close(fd[0])。子进程关闭它的写端 close(fd[1])。反之,如果想让数据从子流向父,则关闭相反的描述符。

(4)进行通信:父进程用 write(fd[1], buf, size)向管道写数据。子进程用 read(fd[0], buf, size)从管道读数据。

(5)通信结束:进程关闭所有描述符,当没有进程再持有管道的写端描述符时,读端会收到EOF。

站在文件描述符角度理解管道

站在内核角度理解管道

内核与底层

缓冲区:管道在内核中有一个固定大小的缓冲区(通常为4KB或64KB,可通过fcntl设置)。写操作将数据复制到内核缓冲区,读操作从缓冲区复制数据到用户空间。
阻塞与非阻塞:
读空管道:如果管道为空,读操作默认阻塞,直到有数据写入。
写满管道:如果管道已满,写操作默认阻塞,直到有数据被读出腾出空间。
可以使用fcntl设置文件描述符为O_NONBLOCK来改为非阻塞模式。
同步与互斥:内核保证了管道读写的原子性。小于管道缓冲区大小(PIPE_BUF,通常是512字节或4KB)的写操作是原子的,即不会被其他写入操作的数据穿插。

优点

(1)简单高效:是系统调用,不涉及磁盘I/O,数据在内核和用户空间间复制一次。
(2)无需同步代码:内核自动处理读写同步(阻塞/唤醒)。
(3)资源自动管理。

缺点

(1)只能用于亲缘进程。
(2)单向通信。要实现双向通信,需要创建两个管道。
(3)传输的是字节流,无消息边界,对结构化数据不友好。
(4)生命周期短,随进程结束。

匿名管道代码

(1)父进程向子进程发送消息

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> int main() { int pipefd[2]; pid_t pid; char buffer[100]; if (pipe(pipefd) == -1) { perror("pipe创建失败"); exit(EXIT_FAILURE); } printf("管道创建成功: fd[0]=%d, fd[1]=%d\n", pipefd[0], pipefd[1]); pid = fork(); if (pid < 0) { perror("fork失败"); exit(EXIT_FAILURE); } if (pid > 0) { printf("=== 父进程 (PID=%d) ===\n", getpid()); close(pipefd[0]); char *message = "Hello from parent process!"; printf("父进程准备发送消息: %s\n", message); write(pipefd[1], message, strlen(message) + 1); // +1包含'\0' printf("父进程已发送消息\n"); close(pipefd[1]); printf("父进程关闭了写端 fd[1]\n"); wait(NULL); printf("子进程已结束,父进程退出\n"); } else { // 子进程 (pid == 0) printf("=== 子进程 (PID=%d) ===\n", getpid()); close(pipefd[1]); ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer)); printf("子进程接收到 %ld 字节数据\n", bytes_read); if (bytes_read > 0) { printf("子进程收到的消息: %s\n", buffer); } close(pipefd[0]); printf("子进程关闭了读端 fd[0]\n"); printf("子进程退出\n"); } return 0; }

(2)父子进程互相发送消息

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> int main() { int pipe1[2]; int pipe2[2]; pid_t pid; char buffer[100]; if (pipe(pipe1) == -1 || pipe(pipe2) == -1) { perror("管道创建失败"); exit(EXIT_FAILURE); } printf("pipe1: 父[%d]->子[%d]\n", pipe1[1], pipe1[0]); printf("pipe2: 子[%d]->父[%d]\n", pipe2[1], pipe2[0]); pid = fork(); if (pid < 0) { perror("fork失败"); exit(EXIT_FAILURE); } if (pid > 0) { printf("=== 父进程 (PID=%d) ===\n", getpid()); close(pipe1[0]); close(pipe2[1]); char *msg_to_child = "Hello child! This is your parent."; printf("父进程发送消息: %s\n", msg_to_child); write(pipe1[1], msg_to_child, strlen(msg_to_child) + 1); ssize_t bytes = read(pipe2[0], buffer, sizeof(buffer)); if (bytes > 0) { printf("父进程收到子进程消息: %s\n", buffer); } bytes = read(pipe2[0], buffer, sizeof(buffer)); if (bytes > 0) { printf("父进程收到子进程回复: %s\n", buffer); } char *end_msg = "Goodbye child!"; write(pipe1[1], end_msg, strlen(end_msg) + 1); close(pipe1[1]); close(pipe2[0]); wait(NULL); printf("父进程退出\n"); } else { printf("=== 子进程 (PID=%d) ===\n", getpid()); close(pipe1[1]); close(pipe2[0]); ssize_t bytes = read(pipe1[0], buffer, sizeof(buffer)); if (bytes > 0) { printf("子进程收到父进程消息: %s\n", buffer); } // 回复父进程 char *reply1 = "Hi parent! I got your message."; printf("子进程回复父进程: %s\n", reply1); write(pipe2[1], reply1, strlen(reply1) + 1); char *reply2 = "How are you today?"; write(pipe2[1], reply2, strlen(reply2) + 1); bytes = read(pipe1[0], buffer, sizeof(buffer)); if (bytes > 0) { printf("子进程收到父进程消息: %s\n", buffer); } close(pipe1[0]); close(pipe2[1]); printf("子进程退出\n"); } return 0; }

命名管道 (FIFO)

概念

通过mkfifo()创建,在文件系统中有一个路径名(如/tmp/myfifo)。任何知道该名字的进程都可以打开它进行通信,突破了亲缘关系限制。

关键特性

可以用于任意进程间通信,不限于亲缘关系

遵循先进先出(FIFO)原则

数据在内核中缓冲,不实际写入磁盘

创建命名管道

(1)命令行创建

使用mkfifo命令

$ mkfifo mypipe

(2)使用C语言创建

#include <sys/types.h> #include <sys/stat.h> // 方法1:使用 mkfifo 函数 int mkfifo(const char *pathname, mode_t mode); // 方法2:使用 mknod 函数(更通用) int mknod(const char *pathname, mode_t mode, dev_t dev);
两个独立进程完整通信

(1)写入者

#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #define FIFO_PATH "/tmp/myfifo" int main() { int fd; char message[100]; printf("Writer Process (PID=%d)\n", getpid()); if (mkfifo(FIFO_PATH, 0666) == -1) { if (errno != EEXIST) { perror("mkfifo"); exit(EXIT_FAILURE); } } printf("Opening FIFO for writing...\n"); fd = open(FIFO_PATH, O_WRONLY); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); } printf("FIFO opened successfully!\n"); for (int i = 1; i <= 5; i++) { snprintf(message, sizeof(message), "Message %d from writer (PID=%d)", i, getpid()); printf("Writing: %s\n", message); ssize_t bytes = write(fd, message, strlen(message) + 1); if (bytes == -1) { perror("write"); break; } sleep(1); } write(fd, "END", 4); close(fd); printf("Writer finished.\n"); return 0; }

(2)读取者

#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #define FIFO_PATH "/tmp/myfifo" int main() { int fd; char buffer[256]; printf("Reader Process (PID=%d)\n", getpid()); printf("Opening FIFO for reading...\n"); fd = open(FIFO_PATH, O_RDONLY); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); } printf("FIFO opened successfully!\n"); while (1) { memset(buffer, 0, sizeof(buffer)); ssize_t bytes = read(fd, buffer, sizeof(buffer) - 1); if (bytes <= 0) { printf("EOF reached or error\n"); break; } printf("Received: %s\n", buffer); if (strcmp(buffer, "END") == 0) { printf("Received END signal, exiting...\n"); break; } } close(fd); unlink(FIFO_PATH); printf("Reader finished.\n"); return 0; }
命名管道与匿名管道区别
特性匿名管道命名管道
文件系统可见
进程关系必须有亲缘关系任意进程
创建方式pipe()系统调用mkfifo()函数或 mknod()
生命周期随进程结束持久存在,直至被删除
打开方式通过继承的文件描述符通过路径名打开
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 13:51:10

内容访问辅助工具:突破付费限制的技术实现方案分析

内容访问辅助工具&#xff1a;突破付费限制的技术实现方案分析 揭示数字内容获取的现实挑战 在信息经济时代&#xff0c;内容付费已成为主流商业模式。据2024年数字内容消费报告显示&#xff0c;全球付费墙覆盖的媒体网站数量较2019年增长187%&#xff0c;学术期刊的单篇文章访…

作者头像 李华
网站建设 2026/5/5 13:51:44

Telia在收购Telness Operator后将部署Seamless OS

Telness Tech的母公司Nordic Communications Group AB今天宣布&#xff0c;该公司将剥离旗下的移动虚拟网络运营商&#xff08;MVNO&#xff09;Telness并出售给瑞典Telia。作为本次交易的一部分&#xff0c;Telness将与Telness Tech签署继续使用Seamless OS技术的长期协议&…

作者头像 李华
网站建设 2026/4/11 17:48:00

不内卷、不踩坑!Paperxie毕业论文功能,解锁毕业通关新姿势

paperxie-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/AIPPThttps://www.paperxie.cn/ai/dissertationhttps://www.paperxie.cn/ai/dissertation 毕业季的朋友圈&#xff0c;一半是奔赴山海的期待&#xff0c;一半是被毕业论文支配的焦虑。有人在文献堆里熬到凌…

作者头像 李华
网站建设 2026/4/12 5:18:56

Windows风扇控制终极方案:FanControl让你的电脑散热与静音兼得

Windows风扇控制终极方案&#xff1a;FanControl让你的电脑散热与静音兼得 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Tre…

作者头像 李华
网站建设 2026/4/11 19:18:35

如何快速提升网盘下载速度?8大平台直链下载助手终极指南

如何快速提升网盘下载速度&#xff1f;8大平台直链下载助手终极指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天…

作者头像 李华
网站建设 2026/4/12 7:24:51

Agent Client Protocol 全景解析缓

1. 核心概念 在 Antigravity 中&#xff0c;技能系统分为两层&#xff1a; Skills (全局库)&#xff1a;实际的代码、脚本和指南&#xff0c;存储在系统级目录&#xff08;如 ~/.gemini/antigravity/skills&#xff09;。它们是“能力”的本体。 Workflows (项目级)&#xff1a…

作者头像 李华