实现一个字符设备,模拟系统信号量,支持 up/down 操作
在本章节中,我们将创建一个Linux内核模块,该模块实现一个字符设备,用于模拟系统信号量的行为。信号量是操作系统中用于同步和互斥的重要机制。通过这个项目,你将学习如何在Linux内核中实现一个简单的信号量,并支持标准的 up 和 down 操作。
信号量的 down 操作(也称为 wait 或 P 操作)会减少信号量的值,如果值变为负数,则进程会被阻塞。up 操作(也称为 signal 或 V 操作)会增加信号量的值,并唤醒可能被阻塞的进程。
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "semaphore_device"
#define CLASS_NAME "semaphore_class"
static int major_number;
static struct class* semaphore_class = NULL;
static struct device* semaphore_device = NULL;
static struct cdev semaphore_cdev;
struct semaphore_state {
struct semaphore sem;
int value;
};
static struct semaphore_state dev_state;
static int device_open(struct inode *inode, struct file *file) {
return 0;
}
static int device_release(struct inode *inode, struct file *file) {
return 0;
}
static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset) {
char value_str[10];
int len = snprintf(value_str, sizeof(value_str), "%d\n", dev_state.value);
return simple_read_from_buffer(buffer, length, offset, value_str, len);
}
static ssize_t device_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset) {
char op;
if (copy_from_user(&op, buffer, 1))
return -EFAULT;
if (op == 'U' || op == 'u') {
up(&dev_state.sem);
dev_state.value++;
} else if (op == 'D' || op == 'd') {
down(&dev_state.sem);
dev_state.value--;
} else {
return -EINVAL;
}
return length;
}
static struct file_operations fops = {
.open = device_open,
.release = device_release,
.read = device_read,
.write = device_write,
};
static int __init semaphore_init(void) {
// 分配主设备号
major_number = register_chrdev(0, DEVICE_NAME, &fops);
if (major_number < 0) {
printk(KERN_ALERT "Failed to register a major number\n");
return major_number;
}
// 创建设备类
semaphore_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(semaphore_class)) {
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(semaphore_class);
}
// 创建设备
semaphore_device = device_create(semaphore_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME);
if (IS_ERR(semaphore_device)) {
class_destroy(semaphore_class);
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(semaphore_device);
}
// 初始化信号量
sema_init(&dev_state.sem, 1);
dev_state.value = 1;
printk(KERN_INFO "Semaphore device module loaded\n");
return 0;
}
static void __exit semaphore_exit(void) {
device_destroy(semaphore_class, MKDEV(major_number, 0));
class_unregister(semaphore_class);
class_destroy(semaphore_class);
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_INFO "Semaphore device module unloaded\n");
}
module_init(semaphore_init);
module_exit(semaphore_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple semaphore device driver");
编译并加载模块后,可以通过以下命令测试信号量设备:
# 编译模块
make
# 加载模块
sudo insmod semaphore.ko
# 查看设备号
cat /proc/devices | grep semaphore_device
# 创建设备节点
sudo mknod /dev/semaphore_device c [major_number] 0
# 测试 down 操作
echo "D" > /dev/semaphore_device
# 测试 up 操作
echo "U" > /dev/semaphore_device
# 读取当前值
cat /dev/semaphore_device
函数 | 描述 |
---|---|
sema_init | 初始化信号量结构 |
down | 执行 down 操作,减少信号量值 |
up | 执行 up 操作,增加信号量值 |
register_chrdev | 注册字符设备 |
device_create | 创建设备节点 |
🔧 确保在编译模块时使用正确版本的内核头文件。
⚠️ 信号量操作是原子性的,但在多处理器系统中可能需要额外的同步机制。
📝 在实际应用中,可能需要处理信号量的超时和中断。