[Linux内核驱动]自旋锁
自旋锁(spinlock)
更多内容可以查看我的github
概念
Spinlock是linux内核中常用的一种互斥锁机制,和mutex不同,当无法持锁进入临界区的时候,当前执行线索不会阻塞,而是不断的自旋等待该锁释放。 正因为如此,自旋锁也是可以用在中断上下文的。 也正是因为自旋,临界区的代码要求尽量的精简,否则在高竞争场景下会浪费宝贵的CPU资源。
使用
#include <linux/spinlock.h>
// 定义自选锁
spinlock_t lock;
// 初始化自旋锁
spin_lock_init(&lock);
// 获取自旋锁
spin_lock(&lock);
// 释放自旋锁
spin_unlock(&lock);
尽管使用自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰。但是得到锁的代码路径在执行临界区的时候,可能受到 中断 的影响。因此,需要使用自旋锁的衍生函数。
// 锁 + 开/关中断
spin_lock_irq();
spin_unlock_irq();
// 锁 + 关中断并保存中断状态/开中断并恢复中断状态
spin_lock_irqsave();
spin_unlock_irqrestore();
测试
make
dmesg -C
insmod spinlock.ko
mknod /dev/spinlock c 232 0
cat /dev/spinlock
dmesg
echo "1" > /dev/spinlock
dmesg
cat /dev/spinlock
dmesg
代码
/*
* @Date: 2024-04-30 15:35:19
* @author: lidonghang-02 [email protected]
* @LastEditTime: 2024-05-19 20:02:50
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#define DEVICE_NAME "spinlock_dev"
#define SPINLOCK_MAJOR 0
static int spinlock_major = SPINLOCK_MAJOR;
struct spinlock_dev
{
struct cdev cdev;
int shared_resource;
struct device *class_dev;
};
struct spinlock_dev *spinlock_devp;
struct class *my_class = NULL;
static dev_t dev_no;
// 自旋锁
spinlock_t my_lock;
static int spinlock_open_func(struct inode *inode, struct file *file)
{
file->private_data = spinlock_devp;
printk(KERN_INFO "Spinlock Device Opened\n");
return 0;
}
static int spinlock_release_func(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Spinlock Device Closed\n");
return 0;
}
static ssize_t spinlock_read_func(struct file *filp, char __user *buf, size_t size,
loff_t *ppos)
{
struct spinlock_dev *dev = filp->private_data;
// 从用户空间读取数据
if (copy_to_user(buf, &dev->shared_resource, sizeof(int)))
{
printk(KERN_ERR "Failed to copy data to user space\n");
return -EFAULT;
}
else
printk(KERN_INFO "shared_resource: %d\n", dev->shared_resource);
ppos += sizeof(int);
return 0;
}
static ssize_t spinlock_write_func(struct file *filp, const char __user *buf, size_t size,
loff_t *ppos)
{
unsigned long flags;
unsigned int count = size;
struct spinlock_dev *dev = filp->private_data;
// 加锁
spin_lock_irqsave(&my_lock, flags);
// 假设的共享资源操作
dev->shared_resource += 1;
// 解锁
spin_unlock_irqrestore(&my_lock, flags);
printk(KERN_INFO "shared_resource: %d\n", dev->shared_resource);
return count;
}
// 设备操作结构体
static const struct file_operations fops = {
.open = spinlock_open_func,
.release = spinlock_release_func,
.read = spinlock_read_func,
.write = spinlock_write_func,
};
// 驱动初始化函数
static int __init my_driver_init_module(void)
{
int ret;
dev_no = MKDEV(spinlock_major, 0);
spin_lock_init(&my_lock);
// 分配设备号
if (spinlock_major)
ret = register_chrdev_region(dev_no, 1, DEVICE_NAME);
else
{
ret = alloc_chrdev_region(&dev_no, 0, 1, DEVICE_NAME);
spinlock_major = MAJOR(dev_no);
}
if (ret < 0)
{
printk(KERN_ERR "Failed to allocate device number\n");
return ret;
}
spinlock_devp = kzalloc(sizeof(struct spinlock_dev), GFP_KERNEL);
if (!spinlock_devp)
{
ret = -ENOMEM;
goto free_chrdev_region;
}
my_class = class_create(THIS_MODULE, "spinlock");
if (IS_ERR(my_class))
{
ret = PTR_ERR(my_class);
goto free_chrdev_region;
}
// 初始化字符设备
cdev_init(&spinlock_devp->cdev, &fops);
spinlock_devp->cdev.owner = THIS_MODULE;
ret = cdev_add(&spinlock_devp->cdev, dev_no, 1);
if (ret < 0)
{
printk(KERN_ERR "Failed to add cdev\n");
goto free_chrdev_class;
}
spinlock_devp->class_dev = device_create(my_class, NULL, dev_no, NULL, DEVICE_NAME);
if (IS_ERR(spinlock_devp->class_dev))
{
ret = PTR_ERR(spinlock_devp->class_dev);
goto free_chrdev_class;
}
printk(KERN_INFO "My Spinlock Driver Init\n");
return 0;
free_chrdev_class:
class_destroy(my_class);
free_chrdev_region:
unregister_chrdev_region(dev_no, 1);
return ret;
}
// 驱动退出函数
static void __exit my_driver_exit_module(void)
{
cdev_del(&spinlock_devp->cdev);
unregister_chrdev_region(dev_no, 1);
kfree(spinlock_devp);
printk(KERN_INFO "My Spinlock Driver Exited\n");
}
module_init(my_driver_init_module);
module_exit(my_driver_exit_module);
MODULE_AUTHOR("lidonghang-02");
MODULE_LICENSE("GPL");