资讯详情

Linux 学习笔记(二十八):进程间通信(三)—— 信号量与进程池

📅 2026/10/26 18:18:59 | 华诺云谱 👁 阅读
Linux 学习笔记(二十八):进程间通信(三)—— 信号量与进程池
一、 并发编程的核心概念由于多个进程/线程可能同时访问公共资源为了防止数据混乱操作系统引入了同步与互斥的概念临界资源同一时刻只允许一个进程访问的资源如共享内存。临界区代码中访问临界资源的那段代码。互斥任何时刻只允许一个进程进入临界区。同步多个进程访问资源时具有顺序性。二、 信号量Semaphore信号量本质上是一个计数器用于实现对临界资源的预订和保护机制。P 操作申请资源计数器减一。若为负则阻塞等待。V 操作释放资源计数器加一。若有人排队则唤醒。通过信号量我们可以完美解决上一节共享内存没有保护机制的问题。三、 高阶架构进程池Process Pool频繁地fork和exit进程极其消耗系统资源。进程池技术通过“池化”思想提前创建好固定数量的子进程。当任务到来时父进程通过管道或共享内存将任务派发给空闲的子进程实现任务的高效处理。优势以空间换时间避免了进程频繁创建和销毁的开销极大提升并发性能。实现方式常结合命名管道做同步控制共享内存做数据传输。四、 踩坑记录IPC 资源清理无论是共享内存还是消息队列它们的生命周期都随内核。如果代码异常退出如 CtrlC 或段错误资源不会被自动回收。必须使用ipcs -m(或-s,-q) 查看并使用ipcrm命令手动清理避免造成内存泄漏。自我实战操作实战 1管理你的 IPC 资源刚才咱们玩了共享内存可能有的同学没删干净今天咱们清理一下系统。# 1. 查看系统里所有的共享内存 ipcs -m # 2. 查看系统里的信号量 ipcs -s # 3. 如果看到之前 test 遗留的或者是 root 用户建的你没权限删的 # 记下对应的 ID用 ipcrm 删除 ipcrm -m shmid ipcrm -s semid实战 2借助命名管道控制共享内存因为共享内存没锁我们干脆用命名管道FIFO当锁用Client 往共享内存写完数据。Client 往命名管道里写 1 个字节相当于敲了一下铃铛。Server 阻塞在命名管道的read上。一读到铃铛说明 Client 写完了Server 才去共享内存读数据。(这就完美实现了没有数据的时候 Server 去睡觉不占 CPU有数据了才干活。文档里Comm.hpp的代码就是干这个的)。当前在blog用户下且进入了ipc_test目录su - blog cd ~/linux_study/ipc_test1. 公共头文件Comm.hpp这个文件负责定义通信的“规则”和“工具”。vim Comm.hpp#pragma once #include iostream #include string #include cstring #include cassert #include unistd.h #include sys/ipc.h #include sys/shm.h #include sys/stat.h #include fcntl.h #include cstdio using namespace std; #define PATH_NAME /home/blog // 确保你当前在 /home/blog 目录下或者改成你真实的绝对路径 #define PROJ_ID 0x66 #define SHM_SIZE 4096 // 共享内存大小最好为4096的整数倍 #define FIFO_NAME ./myfifo // 命名管道文件路径 // 辅助类用来在程序启动时创建管道退出时销毁管道 class Init { public: Init() { umask(0); // mkfifo 如果文件已存在会返回-1为了防止程序报错先 unlink 删除旧的 unlink(FIFO_NAME); int n mkfifo(FIFO_NAME, 0666); assert(n 0); // 确保创建成功 cout [Init] 创建命名管道成功: FIFO_NAME endl; } ~Init() { unlink(FIFO_NAME); cout [Init] 销毁命名管道: FIFO_NAME endl; } }; // 打开管道的辅助函数 int OpenFIFO(string pathname, int flags) { int fd open(pathname.c_str(), flags); assert(fd 0); return fd; } // 阻塞等待相当于听门铃 void Wait(int fd) { cout 等待中... endl; uint32_t temp 0; ssize_t s read(fd, temp, sizeof(uint32_t)); // 卡在这里直到有人写 assert(s sizeof(uint32_t)); } // 发送信号按门铃 void Signal(int fd) { uint32_t temp 1; ssize_t s write(fd, temp, sizeof(uint32_t)); assert(s sizeof(uint32_t)); cout 唤醒中... endl; }2. 服务端ShmServer.cc负责创建共享内存并在管道上等门铃。vim ShmServer.cc#include Comm.hpp // 声明全局对象main 运行前创建管道结束后自动销毁 Init init; int main() { // 1. 生成 Key key_t k ftok(PATH_NAME, PROJ_ID); assert(k ! -1); // 2. 创建共享内存 (IPC_CREAT | IPC_EXCL: 必须是全新的) int shmid shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666); if (shmid -1) { perror(shmget (是不是上次没删干净?)); exit(1); } // 3. 挂接共享内存 char* shmaddr (char*)shmat(shmid, nullptr, 0); assert(shmaddr ! (char*)-1); // 4. 以只读方式打开命名管道 int fd OpenFIFO(FIFO_NAME, O_RDONLY); // 5. 死循环等门铃 - 读黑板 - 继续等 while (true) { Wait(fd); // 阻塞在这里直到 Client 发信号 // 门铃响了说明 Client 写完了共享内存赶紧读 printf(Server 收到: %s\n, shmaddr); if (strcmp(shmaddr, quit) 0) { break; } } close(fd); // 6. 脱离共享内存 int n shmdt(shmaddr); assert(n ! -1); // 7. 删除共享内存 (非常关键) n shmctl(shmid, IPC_RMID, nullptr); assert(n ! -1); return 0; }3. 客户端ShmClient.cc负责获取共享内存写完后按门铃。vim ShmClient.cc#include Comm.hpp int main() { // 1. 生成相同的 Key key_t k ftok(PATH_NAME, PROJ_ID); assert(k ! -1); // 2. 获取共享内存 (没有 IPC_EXCL如果不存在就报错通常要求 Server 先启动) int shmid shmget(k, SHM_SIZE, IPC_CREAT); if (shmid 0) { perror(shmget (Server 启动了吗?)); exit(1); } // 3. 挂接共享内存 char* shmaddr (char*)shmat(shmid, nullptr, 0); assert(shmaddr ! (char*)-1); // 4. 打开命名管道只写方式 int fd OpenFIFO(FIFO_NAME, O_WRONLY); // 5. 循环写数据 while (true) { // 从键盘读入写进共享内存 ssize_t s read(0, shmaddr, SHM_SIZE - 1); if (s 0) { shmaddr[s - 1] \0; // 去掉回车换行 Signal(fd); // 敲完回车按门铃通知 Server if (strcmp(shmaddr, quit) 0) { break; } } } close(fd); // 6. 去关联 int n shmdt(shmaddr); assert(n ! -1); return 0; } 运行因为这是 C 代码编译要用g。sudo apt install g -y第一步编译g ShmServer.cc -o server g ShmClient.cc -o client第二步运行老规矩开两个终端终端 A先启动运行./server。它会提示“等待中...”然后卡住。终端 B后启动运行./client。输入Hello World回车。现象终端 A 会瞬间打印出Server 收到: Hello World然后又进入等待。退出在终端 B 输入quit两个程序都会安全退出并且自动把共享内存和管道清理得干干净净。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑