当前位置: 首页 > news >正文

【操作系统实验】Linux 下多线程同步与互斥实战——生产者 - 消费者模型

摘要:
本文记录了在 Ubuntu Linux 环境下,使用 GCC 和 POSIX 线程库(pthread)解决经典并发编程问题——“生产者 - 消费者问题”的全过程。文章详细分析了信号量(Semaphore)与互斥锁(Mutex)的配合机制,并提供了完整的可运行代码及编译调试步骤。对于正在学习操作系统课程的同学,这是一份实用的参考指南。


一、前言

最近在进行操作系统的课程设计,核心任务是理解进程 / 线程间的同步与互斥机制。经典的“生产者 - 消费者问题”是理解这一概念的基石。

我的开发环境是在 Windows 宿主机上通过VMware Workstation运行的Ubuntu 64 位虚拟机。这种环境既能保证系统的安全性,又能提供原汁原味的 Linux 命令行体验,非常适合进行底层系统编程的学习。

二、问题分析

在这个实验中,我们需要模拟多个生产者和多个消费者共享一个固定大小的缓冲区:

  • 生产者(Producer):负责生产数据并放入缓冲区。如果缓冲区满了,必须等待。
  • 消费者(Consumer):负责从缓冲区取出数据。如果缓冲区空了,必须等待。
  • 互斥访问:无论生产还是消费,同一时刻只能有一个线程操作缓冲区,以防止数据错乱。

为了实现上述逻辑,我们需要三个关键的同步工具:

  1. mutex(互斥锁):保护缓冲区的互斥访问。
  2. empty(信号量):记录空闲缓冲区的数量,初始值为缓冲区总大小 $M$。
  3. full(信号量):记录已填充数据的缓冲区数量,初始值为 $0$。
三、核心代码实现

以下是我在 Ubuntu 终端中编写并验证通过的完整代码(producer_consumer.c)。代码使用了<semaphore.h><pthread.h>库。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 5 // 缓冲区大小 M #define PRODUCER_NUM 2 // 生产者数量 #define CONSUMER_NUM 2 // 消费者数量 int buffer[BUFFER_SIZE]; int in = 0; // 生产者写入位置 int out = 0; // 消费者读取位置 // 定义信号量和互斥锁 sem_t empty_sem; sem_t full_sem; pthread_mutex_t mutex; // 生产者函数 void *producer(void *arg) { int id = *(int *)arg; while (1) { sleep(1); // 模拟生产耗时 sem_wait(&empty_sem); // P(empty): 申请空位 pthread_mutex_lock(&mutex); // Lock: 进入临界区 // --- 写入数据 --- buffer[in] = 1; printf("[生产者 %d] 放入数据到位置 %d\n", id, in); in = (in + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); // Unlock: 离开临界区 sem_post(&full_sem); // V(full): 增加满位计数 } return NULL; } // 消费者函数 void *consumer(void *arg) { int id = *(int *)arg; while (1) { sleep(2); // 模拟消费耗时 sem_wait(&full_sem); // P(full): 申请数据 pthread_mutex_lock(&mutex); // Lock: 进入临界区 // --- 读取数据 --- printf("[消费者 %d] 从位置 %d 取出数据\n", id, out); buffer[out] = 0; out = (out + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); // Unlock: 离开临界区 sem_post(&empty_sem); // V(empty): 增加空位计数 } return NULL; } int main() { pthread_t prod_threads[PRODUCER_NUM], cons_threads[CONSUMER_NUM]; int prod_ids[PRODUCER_NUM], cons_ids[CONSUMER_NUM]; // 初始化 sem_init(&empty_sem, 0, BUFFER_SIZE); sem_init(&full_sem, 0, 0); pthread_mutex_init(&mutex, NULL); // 创建线程 for (int i = 0; i < PRODUCER_NUM; i++) { prod_ids[i] = i; pthread_create(&prod_threads[i], NULL, producer, &prod_ids[i]); } for (int i = 0; i < CONSUMER_NUM; i++) { cons_ids[i] = i; pthread_create(&cons_threads[i], NULL, consumer, &cons_ids[i]); } // 等待线程结束(此处为死循环演示,实际需配合退出逻辑) for (int i = 0; i < PRODUCER_NUM; i++) pthread_join(prod_threads[i], NULL); for (int i = 0; i < CONSUMER_NUM; i++) pthread_join(cons_threads[i], NULL); // 销毁资源 sem_destroy(&empty_sem); sem_destroy(&full_sem); pthread_mutex_destroy(&mutex); return 0; }
四、编译与运行指南

在 Ubuntu 虚拟机中,请按照以下步骤操作:

  1. 保存文件:将上述代码保存为producer_consumer.c

  2. 编译命令:注意需要链接pthread库。

    gcc producer_consumer.c -o pc_demo -lpthread
  3. 运行程序

    ./pc_demo
  4. 观察输出:你会看到生产者和消费者交替打印日志,且不会出现缓冲区溢出或读取空数据的情况。按Ctrl+C终止程序。

五、实验心得与注意事项
  1. P/V 操作顺序至关重要:在生产者中,必须先wait(empty)lock(mutex);在消费者中,必须先wait(full)lock(mutex)。如果顺序反了(先锁后等信号量),极易导致死锁
  2. 编译报错处理:如果在 Ubuntu 上编译提示找不到semaphore.h,请确保安装了libc6-dev包(通常默认已安装)。
  3. 虚拟机的优势:如果在实验中不小心写错了代码导致系统卡死,直接关闭 VMware 虚拟机重启即可,完全不会影响宿主机的 Windows 系统,这大大降低了试错成本。
http://www.gsyq.cn/news/1514071.html

相关文章:

  • 别再死记硬背了!用ASM图搞定VHDL状态机设计,从交通灯到FPGA实战
  • 终极指南:如何高效使用yuzu模拟器运行Switch游戏
  • 2026年当前市场烘焙设备销售厂家找哪家?专业选型与青岛杰麦深度解析 - 品牌鉴赏官2026
  • 2026企业协同办公工具全方位测评:适配不同团队的数字化办公工具深度解析
  • 2026年五金冲压件选购指南:从材质、工艺到供应商的全面分析 - 优质品牌商家
  • 告别WinForms默认丑界面:用Guna UI 2.0.4.4快速打造现代化桌面应用(附控件详解)
  • 3分钟掌握:高效实用的网易云音乐ncm转mp3完整指南
  • 2026甄选:常州高端婚纱品牌实力之选与行业深度分析 - 品牌发掘
  • 2026年,聊城异形钢管供应商:聊城市宏宝钢管有限公司 - 企业推荐官【官方】
  • Move Mouse:Windows防休眠与自动化鼠标操作的终极解决方案
  • 2026年温州商业展柜行业深度评测:谁才是品牌门店背后的“空间塑造者”? - 优质品牌商家
  • Java毕设项目:基于 SpringBoot 的数字化智慧物业综合运维系统的设计与实现 (源码+文档,讲解、调试运行,定制等)
  • RT-Thread Studio实战:手把手教你用SPI驱动BMP280传感器(附完整代码)
  • Mem Reduct:Windows系统内存优化的终极免费解决方案
  • 2026年绿化支撑杆品牌怎么选?青海、甘肃、西宁地区正规厂家与供应商深度分析 - 优质品牌商家
  • 比特币钱包密码恢复终极指南:如何用btcrecover找回遗忘的密码和助记词
  • PCB拼版三大细节及华秋PCB的硬核制程能力
  • FPGA接口桥接设计:从Motorola M-2适配器看高速通信接口转换
  • 2026年成都厂房防雷公司哪家实惠?六家主流企业服务能力与价格对比分析 - 优质品牌商家
  • MC9S08QE32低功耗设计实战:嵌入式系统性能与能耗平衡指南
  • 2026年专业车载逆变器直销厂商深度解析与选型指南 - 品牌鉴赏官2026
  • 智能体时代的产品经理如何转型
  • Transformer:现代大模型核心架构入门
  • PrivAct框架:多智能体协同的LLM隐私保护方案
  • 细说RocketMQ双网卡问题
  • 用Arduino UNO和ULN2003驱动28BYJ-48步进电机,手把手教你做个迷你云台
  • GPT-4参数量与稀疏激活真相:1.8万亿参数和2% per token的工程本质
  • PVZ Toolkit技术架构解析:内存注入与跨版本兼容性实现
  • 组件库版本管理与语义化发布:从手动发包到自动化交付链路
  • Kimi版超级玛丽效果“惊人”,配额不足5厘米!