
信号量1.1信号量的描述信号量是一个特殊变量一般取正数值用于控制对共享资源的访问。概念说明信号量的值代表允许访问的资源数目P操作sem_wait获取资源信号量值原子减1若值为0则阻塞等待V操作sem_post释放资源信号量值原子加1唤醒等待的线程/进程二值信号量值只取0 或 1相当于互斥锁计数信号量值 1控制多个资源如连接池信号量本质是一个计数器0→ 有可用资源拿一个走值减10→ 没资源了要排队等阻塞用完归还 → 值加1唤醒排队的人临界资源同一时刻只允许被一个进程或线程访问的资源(如共享变量、打印机临界区访问临界资源的代码段需要用信号量/锁保护1.2信号量使用头文件sem.hunion semun{ int val; }; void sem_init(); // 初始化信号量 void sem_p(); // P操作申请资源 void sem_v(); // V操作释放资源 void sem_destory(); // 销毁信号量拼写应为 destroysem.c实现信号量核心函数与结构体1.semget—— 创建或获取信号量集#include sys/sem.h int semget(key_t key, int nsems, int semflg);参数说明key全局唯一标识符ftok生成或IPC_PRIVATEnsems信号量个数通常为 1semflg权限标志如IPC_CREAT | 0666返回值说明 0成功返回信号量集 IDsemid-1失败2.semctl—— 控制信号量集int semctl(int semid, int semnum, int cmd, ...);参数说明semidsemget返回的信号量集 IDsemnum操作第几个信号量从 0 开始cmd控制命令常用cmd作用SETVAL设置信号量的值通过第四个参数GETVAL获取信号量的值返回值IPC_RMID删除信号量集// 设置信号量值为 1 semctl(semid, 0, SETVAL, 1); // 获取信号量当前值 int val semctl(semid, 0, GETVAL); // 删除信号量集 semctl(semid, 0, IPC_RMID);3.结构体sembuf—— 定义信号量操作struct sembuf { unsigned short sem_num; // 操作第几个信号量从0开始 short sem_op; // 操作数正/负/零 short sem_flg; // 操作标志(阻塞/非阻塞 };sem_flg常用值说明0默认阻塞等待IPC_NOWAIT非阻塞不等待立即返回错误SEM_UNDO进程退出时自动撤销对该信号量的操作防止死锁sem_op值效果负数-1P操作获取资源信号量值减少若值不够则阻塞正数1V操作释放资源信号量值增加唤醒等待者0等待信号量值为 0执行过程通过semid在内核中找到对应的信号量对象从联合体a中取出val的值1把这个值赋给找到的信号量信号量的状态从原来的值可能是0变成了1P 获取资源把信号量从未占用改为占用V 释放资源把信号量从占用改为未占用struct sembuf完整结构,用于描述对信号量的具体操作面试题例题进程a和进程b模拟访问打印机进程a输出第一个字符‘a’表示开始使用打印机输出第二个字符‘a’表示结束使用b进程操作与进程相同。由于打印机同一时刻只能被一个进程使用所以输出结果不应该出现abab)c#a.cC#b.c代码实现:生产者-消费者 vs 读者-写者一、核心关系对比维度生产者-消费者读者-写者核心冲突速率不匹配生产太快/消费太慢读写冲突数据一致性角色生产数据 vs 消费数据读数据 vs 写数据并发允许生产和消费不能同时操作同一个槽位读-读可并发读-写互斥写-写互斥二、同步条件生产者-消费者读者-写者缓冲区不满 → 才能生产无写者 → 读者可读缓冲区不空 → 才能消费无读者/写者 → 写者可写互斥访问缓冲池写者优先级通常高于读者防止写饿死三、信号量设计生产者-消费者读者-写者empty空位数生产者关心wrt写互斥读写/写写互斥full数据数消费者关心mutex保护读者计数mutex保护缓冲池readcount当前读者数四、典型应用生产者-消费者读者-写者任务队列、日志缓冲、管道数据库读写锁、文件系统缓存总结生产者-消费者管的是速率匹配队列空/满用empty/full两个信号量读者-写者管的是访问权限读读共享读写互斥用wrt保证写独占。Reactor 模型生产者-消费者角色Reactor 中的对应组件说明生产者主线程Reactor 线程通过epoll_wait监听事件一旦有 I/O 就绪就生产一个“任务”产品/任务就绪事件fd 可读/可写一个“连接可读”或“连接可写”的事件缓冲队列请求队列主线程将事件放入队列工作线程从队列取消费者工作线程Worker 线程从队列中取事件执行 Read、Process、Write映射关系主线程生产者 │ ├── epoll_wait() → 监听事件原材料 ├── 插入就绪的事件 → 生产任务放入队列 └── 请求队列 → 缓冲区任务队列 │ ▼ 工作线程消费者 ├── Read → 读数据 ├── Process → 处理业务 └── Write → 写回客户端Reactor 的变体分类变体说明生产者-消费者体现单 Reactor 单线程一个线程既负责监听又负责处理生产者和消费者是同一个线程串行单 Reactor 多线程主线程负责监听工作线程负责处理主线程是生产者工作线程是消费者多 Reactor 多线程多个 Reactor 线程各自监听和处理多个生产者 多个消费者Reactor 模式 主线程生产者用 epoll 生产事件 请求队列缓冲区 工作线程消费者处理事件。这就是生产者-消费者模型在网络编程中的具体应用。