)
进程、线程与 IPC 并发实战不加锁的共享内存为何丢数据srvB本篇基于 srvBphase3真实实操。覆盖fork与僵尸进程、无名管道、命名管道 FIFO、System V 消息队列、共享内存信号量、线程互斥锁与条件变量、阻塞/非阻塞 IO以及select/epoll多路复用。最精彩的是竞态对比实验同一段并发自增无锁时结果从 200000 变成 137328。1. 环境Linux ecs-bee4-c6af-0002 6.8.0-106-generic x86_64 gcc 13.3.0 (pthread 用 -pthread 链接)全部 demo 一次编译通过$ gcc-Wall-ofork_demo fork_demo.c...gcc-Wall-oepoll_echo epoll_echo.cecho编译全部通过 编译全部通过2. fork 与僵尸进程fork_demo演示了三件事基本fork、stdout 缓冲区在子进程里被复制、以及僵尸进程的产生与回收。我是子进程: pid9888, 我的父进程 ppid9887 ----- [3] 僵尸进程演示 ----- 子进程 9890 即将退出, 父进程暂时不回收 此时用 ps 查看子进程状态应为 Z (zombie) 父进程现在回收子进程, 僵尸消失僵尸进程的本质子进程退出后内核保留其 PCB 让父进程wait回收父进程不回收子进程就一直以Z状态存在占用 PID。这是面试高频题这里直接用ps看到了Z。3. 进程间通信三件套无名管道父子进程子进程(pid9958)收到: hello pipe ipc from embedded linux lab 父进程(pid9957)发送: hello pipe ipc from embedded linux lab 父进程收到子进程处理结果: HELLO PIPE IPC FROM EMBEDDED LINUX LAB命名管道 FIFO无亲缘关系进程写端open会阻塞到读端打开这是 FIFO 的典型语义。写端发送: 消息1: FIFO 可以跨进程跨终端通信 写端发送: 消息2: 写端 open 会阻塞直到读端打开 读端收到: 消息1: FIFO 可以跨进程跨终端通信 读端收到: 消息2: 写端 open 会阻塞直到读端打开System V 消息队列支持按类型接收发 2 条收 2 条后删除。消息队列已创建:key0x51016002msqid0父进程收到(type1): 来自子进程(pid9968)的问候 父进程收到(type1): 子进程第二条: 消息队列支持按类型接收 消息队列已删除4. 重头戏共享内存 信号量竞态对比父子进程各对共享内存计数器自增 100000 次期望 200000。不加任何锁[对比1] 不加锁直接写共享内存: 无锁竞态 : 期望 200000, 实际 137328 (发生数据丢失!)加 System V 信号量互斥[对比2] 使用 System V 信号量互斥: 信号量保护: 期望 200000, 实际 200000 (正确)为什么差 6 万多因为counter在汇编层是读-改-写三步两个进程可能同时读到旧值、各自加一写回一次自增就丢了。信号量把这段临界区变成原子操作结果立即正确。这是并发编程最该刻进肌肉记忆的一课。5. 线程互斥锁与条件变量线程共享地址空间竞态更直观。两个线程各自增 1000000 次-----[1]互斥锁实验(2线程各自增1000000次)----- 不加锁: 期望2000000, 实际1055065(丢失更新!)加互斥锁: 期望2000000, 实际2000000(正确)条件变量实现生产者-消费者缓冲区容量 8各生产/消费 15 件可以看到缓冲区满时生产者等待、空时消费者等待[生产者] 放入产品 13 (当前库存 8) [生产者] 缓冲区满, 等待... [消费者] 取出产品 7 (当前库存 7) [生产者] 放入产品 15 (当前库存 8) ... 全部完成, 最终库存 0条件变量 互斥锁正是 bounded-buffer 问题的标准解法。6. 阻塞 IO vs 非阻塞 IO写端 3 秒后才写读端表现截然不同-----[1]阻塞 IO -----[read 开始等待]16:31:00[read 返回]16:31:03 读到18字节: 数据终于来了 —— 中间约3秒进程在睡眠, CPU 可被调度给别的任务 -----[2]非阻塞 IO ----- 第1次 read: 返回 -1,errnoResource temporarily unavailable 立即返回, 进程没有被阻塞 第2次 read: 返回 -1,errnoResource temporarily unavailable 立即返回 结论: 阻塞IO逻辑简单但会挂起进程;非阻塞IO需要配合轮询或IO多路复用, 才能在单进程/单线程里同时服务多个IO来源7. IO 多路复用select / epoll非阻塞 IO 必须配多路复用才能一边等一边干活。本实验实现了select聊天广播服务器和epoll回显服务器——单进程同时看护多个客户端连接这正是 Nginx、Redis 等高并发服务的基础模型。实操记录中select_server/epoll_echo均编译运行通过epoll在大量连接下比select的 O(n) 轮询更高效内核回调机制O(1) 就绪通知是生产环境的首选。8. 小结机制适用场景本实验关键结论管道/FIFO/消息队列进程间数据传递FIFO 的open阻塞语义、消息队列按类型收共享内存进程间大数据共享必须配信号量否则竞态丢数据线程互斥锁/条件变量线程间协作不加锁自增从 2000000 掉到 1055065阻塞/非阻塞 多路复用高并发 IO非阻塞要配select/epoll才实用下一篇跳到 srvC交叉编译、BusyBox 根文件系统与 QEMU 启动——以及一个折磨人的32 位内核跑 64 位程序启动陷阱。