尧图精选

Linux软件编程学习笔记(七)——进程间通信(一)

🕒 发布时间:2026/9/1 16:21:38 📁 来源:尧图网络
一、什么是进程间通信进程间通信简称IPC Inter-Process Communication就是不同进程之间进行数据交换和同步的一种机制。例如进程A │ │ 发送数据 ↓ IPC机制 │ │ 接收数据 ↓ 进程B常见的进程间通信方式进程间通信 │ ├── 管道 Pipe │ ├── 有名管道 FIFO │ ├── 消息队列 │ ├── 共享内存 │ ├── 信号 │ └── Socket本章重点掌握管道、FIFO、消息队列、共享内存、信号。二、为什么进程之间不能直接通信假设有两个进程进程A 进程B ┌──────────┐ ┌──────────┐ │ int num; │ │ int num; │ │ │ │ │ │ 地址空间 │ │ 地址空间 │ └──────────┘ └──────────┘即使两个进程中都有int num;它们也不是同一个变量。例如进程A num 100 进程B num 200两个num分别属于不同的进程地址空间。因此需要进程A ↓ IPC ↓ 进程B三、进程间通信的主要方式可以先记住这一张表IPC方式特点常见用途管道单向、亲缘进程父子进程通信FIFO有名管道、无亲缘关系也可以不相关进程通信消息队列按消息进行通信结构化消息传递共享内存速度快大量数据交换信号传递事件/通知异步通知Socket网络/本机通信网络程序四、管道 Pipe管道是 Linux 中最经典的 IPC 方式之一。基本特点管道是一种半双工通信机制。所谓半双工A ─────────→ B数据主要沿一个方向流动。五、管道的基本结构写端 读端 │ │ ↓ ↓ 进程A 进程B │ │ └──────→ 管道 ←─────────┘例如进程A │ write() │ ↓ ┌──────────┐ │ 管道 │ └──────────┘ │ read() ↓ 进程B六、pipe() 创建管道函数pipe()头文件#include unistd.h函数原型int pipe(int pipefd[2]);调用int pipefd[2]; pipe(pipefd);创建成功以后pipefd[0] ↓ 读端 pipefd[1] ↓ 写端一定要记住pipefd[0] → read pipefd[1] → write七、pipe() 的工作原理执行int pipefd[2]; pipe(pipefd);系统创建一个管道并返回两个文件描述符pipefd[0] ───────→ 读端 pipefd[1] ───────→ 写端因此read(pipefd[0], ...);表示从管道读取。write(pipefd[1], ...);表示向管道写入。八、父子进程通过管道通信管道最典型的应用父进程 │ │ fork() ↓ 子进程因为fork()后子进程会继承父进程打开的文件描述符。所以父进程 ─────┐ │ ↓ 管道 ↑ │ 子进程 ─────┘双方都可以拥有管道的读写端。九、单向通信的典型结构例如父进程 子进程 │ │ │ write │ ├──────────────→ │ │ read │ │父进程写数据 ↓ pipe ↓ 子进程读取十、父子进程管道通信示例#include stdio.h #include unistd.h #include sys/types.h #include sys/wait.h #include string.h int main(void) { int pipefd[2] {0}; pid_t pid 0; char buf[1024] {0}; pipe(pipefd); pid fork(); if(pid 0) { perror(fork); return -1; } if(pid 0) { // 子进程关闭写端 close(pipefd[1]); read(pipefd[0], buf, sizeof(buf)); printf(子进程收到%s\n, buf); close(pipefd[0]); } else { // 父进程关闭读端 close(pipefd[0]); strcpy(buf, hello child); write(pipefd[1], buf, strlen(buf) 1); close(pipefd[1]); wait(NULL); } return 0; }运行子进程收到hello child十一、为什么要关闭不用的管道端例如父进程只负责写父进程 write那么应该close(pipefd[0]);关闭读端。子进程只负责读子进程 read应该close(pipefd[1]);关闭写端。结构父进程 ├── 关闭读端 └── 使用写端 子进程 ├── 关闭写端 └── 使用读端这样做可以避免文件描述符泄漏明确通信方向正确判断管道 EOF让程序逻辑更加清晰十二、管道为什么通常用于父子进程因为pipe()创建的是内核中的一个管道对象。随后fork()子进程继承父进程的文件描述符。所以pipe() ↓ fork() ↓ 父子进程共同拥有管道描述符 ↓ 可以通信因此匿名管道特别适合具有亲缘关系的进程之间通信。十三、管道的读写特性管道本质上可以理解为内核中的一个缓冲区写入write() ↓ 管道缓冲区读取管道缓冲区 ↓ read()因此数据通常具有先进先出FIFO的特点。十四、管道的阻塞如果管道中没有数据read() ↓ 没有数据 ↓ 阻塞等待如果管道已经没有空间write() ↓ 管道满 ↓ 阻塞等待这也是管道能够天然用于进程同步的原因之一。十五、管道的局限性匿名管道存在一些限制1. 通常用于具有亲缘关系的进程例如父子进程 兄弟进程2. 通常是半双工通信如果需要双向通信可以创建两个管道管道1 A → B 管道2 B → A形成管道1 A ─────────→ B 管道2 A ←───────── B十六、有名管道 FIFO如果两个进程没有亲缘关系怎么办例如进程A 进程B它们不是父子关系这时候可以使用有名管道 FIFONamed Pipe十七、FIFO 和匿名管道的区别匿名管道pipe()通常亲缘进程FIFOmkfifo()可以无亲缘关系的进程通信。对比项目匿名管道FIFO创建pipe()mkfifo()文件系统中是否有名字没有有是否需要亲缘关系通常需要不需要通信方式文件描述符文件典型应用父子进程独立程序之间十八、mkfifo()创建 FIFOmkfifo()头文件#include sys/types.h #include sys/stat.h函数int mkfifo( const char *pathname, mode_t mode );例如mkfifo(/tmp/Myfifo, 0664);执行以后/tmp/Myfifo会成为一个 FIFO 特殊文件。可以使用ls -l /tmp/Myfifo查看。十九、FIFO 通信结构进程A │ open(/tmp/Myfifo, O_WRONLY) │ write() ↓ ┌─────────────────┐ │ /tmp/Myfifo │ │ FIFO │ └─────────────────┘ ↑ read() │ open(/tmp/Myfifo, O_RDONLY) │ 进程B两个程序甚至可以完全独立运行。二十、FIFO 的典型应用例如writer.c负责写入数据另一个reader.c负责读取数据它们之间通过/tmp/Myfifo通信。这也是 Linux 中非常典型的无亲缘关系进程通信。二十、信号 Signal信号也是 Linux 中一种重要的 IPC 机制。但是信号与管道 消息队列 共享内存不同。信号主要用于通知某个进程发生了某个事件。例如进程A ↓ 发送信号 ↓ 进程B ↓ 执行信号处理函数四十、常见信号Linux 中常见SIGINT SIGTERM SIGKILL SIGSTOP SIGCHLD SIGUSR1 SIGUSR2二十一、CtrlC 和 SIGINT在终端运行./a.out按Ctrl C通常会产生SIGINT发送给前台进程。可以理解成键盘 ↓ CtrlC ↓ SIGINT ↓ 进程四十二、kill()发送信号可以使用kill()函数原型int kill(pid_t pid, int sig);例如kill(pid, SIGUSR1);表示向指定 PID 的进程发送SIGUSR1信号。注意kill()不一定意味着“杀死进程”。它本质上是向指定进程发送一个信号。二十三、signal()可以使用signal()设置简单的信号处理方式。例如signal(SIGINT, handler);当进程收到SIGINT就执行handler()二十四、信号处理函数例如#include stdio.h #include signal.h void handler(int sig) { printf(收到信号%d\n, sig); } int main(void) { signal(SIGINT, handler); while(1) { } return 0; }运行./a.out按CtrlC可能输出收到信号2之后程序不会因为默认处理动作而立即退出因为我们设置了自己的处理函数。二十五、SIGKILL和SIGSTOP特殊在哪里有两个信号不能被进程捕获、阻塞或忽略SIGKILL SIGSTOP例如kill -9 PID其中9 ↓ SIGKILL表示强制终止进程。二十六、进程间通信方式对比把本章内容放在一起进程间通信 IPC │ ┌──────────────┼──────────────┐ ↓ ↓ ↓ 管道 FIFO 消息队列 │ │ │ 字节流 有名管道 消息单位 │ │ │ └──────────────┼──────────────┘ ↓ 共享内存 │ 速度快 │ ↓ 信号 │ 事件通知二十七、各种 IPC 的特点1. 管道pipe()特点简单常用于父子进程半双工字节流2. FIFOmkfifo()特点有名字存在于文件系统无亲缘关系进程也能通信常用于两个独立程序3. 信号kill() signal()特点数据量小主要用于事件通知异步可以通知进程发生某个事件二十八、进程间通信和线程间通信对比项目线程间进程间地址空间共享独立数据共享方便需要 IPC共享变量可以直接使用不能直接共享普通变量互斥锁常用也可以使用进程共享属性的同步机制条件变量常用可以配置为进程共享信号量可以可以管道一般不是首选常用FIFO一般不是首选常用消息队列不常作为首选常用共享内存不需要常用核心区别线程 共享地址空间 ↓ 直接共享数据 ↓ 重点解决同步问题 进程 地址空间独立 ↓ 需要IPC交换数据 ↓ 重点解决通信 同步
上一篇/下一篇内容由系统自动关联 返回资讯列表 →