简介
生产者 - 消费者模型是一种经典的多线程同步模型,用于处理多个线程或进程对共享资源的访问。它主要解决生产者线程和消费者线程之间的数据同步问题。
1.生产者线程
负责生成数据并将其放入共享缓冲区中。
一般来说,生产者线程会不断地生产数据,当缓冲区满时,生产者线程需要等待消费者线程消费数据,释放缓冲区空间。
2.消费者线程
负责从共享缓冲区中取出数据并进行处理。
消费者线程会不断地从缓冲区中取走数据,当缓冲区为空时,消费者线程需要等待生产者线程生产数据。
🧩 模块结构概览
- 字符设备接口:用于用户态读写数据
- 环形缓冲区:共享数据区,带锁保护
- 生产者线程:周期性写入数据
- 消费者线程:周期性读取数据
- 同步机制 :使用
wait_queue
+spinlock
实现阻塞与唤醒
🧪 内核模块代码(简化版)
c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#define BUF_SIZE 128
#define DEVICE_NAME "pcdev"
static dev_t dev_num;
static struct cdev pc_cdev;
static char buffer[BUF_SIZE];
static int head = 0, tail = 0;
static spinlock_t buf_lock;
static wait_queue_head_t read_queue, write_queue;
static struct task_struct *producer_thread;
static struct task_struct *consumer_thread;
static bool data_available = false;
static int buffer_is_empty(void) { return head == tail; }
static int buffer_is_full(void) { return ((head + 1) % BUF_SIZE) == tail; }
static void buffer_write(char val) {
buffer[head] = val;
head = (head + 1) % BUF_SIZE;
}
static char buffer_read(void) {
char val = buffer[tail];
tail = (tail + 1) % BUF_SIZE;
return val;
}
// 用户态读
static ssize_t pc_read(struct file *filp, char __user *buf, size_t count, loff_t *off) {
char val;
if (wait_event_interruptible(read_queue, !buffer_is_empty()))
return -ERESTARTSYS;
spin_lock(&buf_lock);
val = buffer_read();
spin_unlock(&buf_lock);
if (copy_to_user(buf, &val, 1))
return -EFAULT;
wake_up_interruptible(&write_queue);
return 1;
}
// 用户态写
static ssize_t pc_write(struct file *filp, const char __user *buf, size_t count, loff_t *off) {
char val;
if (copy_from_user(&val, buf, 1))
return -EFAULT;
if (wait_event_interruptible(write_queue, !buffer_is_full()))
return -ERESTARTSYS;
spin_lock(&buf_lock);
buffer_write(val);
spin_unlock(&buf_lock);
wake_up_interruptible(&read_queue);
return 1;
}
static struct file_operations pc_fops = {
.owner = THIS_MODULE,
.read = pc_read,
.write = pc_write,
};
// 生产者线程
static int producer_fn(void *data) {
char val = 'A';
while (!kthread_should_stop()) {
if (!buffer_is_full()) {
spin_lock(&buf_lock);
buffer_write(val++);
spin_unlock(&buf_lock);
wake_up_interruptible(&read_queue);
}
msleep(100);
}
return 0;
}
// 消费者线程
static int consumer_fn(void *data) {
while (!kthread_should_stop()) {
if (!buffer_is_empty()) {
spin_lock(&buf_lock);
char val = buffer_read();
spin_unlock(&buf_lock);
pr_info("Consumed: %c\n", val);
wake_up_interruptible(&write_queue);
}
msleep(150);
}
return 0;
}
static int __init pc_init(void) {
alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
cdev_init(&pc_cdev, &pc_fops);
cdev_add(&pc_cdev, dev_num, 1);
spin_lock_init(&buf_lock);
init_waitqueue_head(&read_queue);
init_waitqueue_head(&write_queue);
producer_thread = kthread_run(producer_fn, NULL, "producer");
consumer_thread = kthread_run(consumer_fn, NULL, "consumer");
pr_info("Producer-Consumer module loaded\n");
return 0;
}
static void __exit pc_exit(void) {
kthread_stop(producer_thread);
kthread_stop(consumer_thread);
cdev_del(&pc_cdev);
unregister_chrdev_region(dev_num, 1);
pr_info("Producer-Consumer module unloaded\n");
}
module_init(pc_init);
module_exit(pc_exit);
MODULE_LICENSE("GPL");
🧪 使用方式
-
编译并加载模块:
bashinsmod pcdev.ko
-
创建设备节点:
bashmknod /dev/pcdev c <major> 0
-
用户态读写:
bashcat /dev/pcdev # 读取消费者消费的数据 echo "X" > /dev/pcdev # 写入生产者数据
✅ 总结
这个模块展示了如何在内核中通过字符设备实现一个完整的生产者-消费者模型,支持用户态交互、内核态线程协作、同步机制和缓冲区管理。你可以扩展它支持多生产者/消费者、环形队列大小动态调整、或添加阻塞/非阻塞模式切换。
一组"生产者"线程负责生成数据并放入共享缓冲区,另一组"消费者"线程从缓冲区中取出数据进行处理。两者通过同步机制协调访问,避免竞争、空转或资源浪费。