linux 自旋锁
·
首先是基本概念
自旋锁,是不会休眠的。
在ARM平台下,自旋锁的实现使用了ldrex、strex、以及内存屏障指令dmb、dsb、wfe、sev等
自旋锁, 进程上下文中,调用spin_lock_irqsave()、spin_unlock_irqrestore(),在中断上下文中调用spin_lock()、spin_unlock()

然后是为什么spin_lock 中 不能调用 引起睡眠的函数呢?比如 sleep()函数。
这个是重点。
网上的截图:

这是驱动程序与 应用程序。
这实际上是一个例子了。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#define NEWCHRDEV_CNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "hello" /* 名字 */
static spinlock_t lock; //定义
static int flage = 1;
/* newchrdev设备结构体 */
struct newchr_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchr_dev chr_hello; /* 设备hello */
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
#define DEAD 1
static int hello_open (struct inode *inode, struct file *filep)
{
printk("hello_open()\n");
spin_lock(&lock); //锁
printk("hello_open() spin_lock \n");
if(flage != 1){
spin_unlock(&lock);
return -EBUSY;
}
flage =0;
#if DEAD
#elif
spin_unlock(&lock);
printk("hello_open() spin_unlock \n");
#endif
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int hello_release (struct inode *inode, struct file *filep)
{
printk("hello_release()\n");
flage = 1;
#if DEAD
spin_unlock(&lock);
printk("hello_release() spin_unlock \n");
#endif
return 0;
}
/* 设备操作函数 */
static struct file_operations chr_hello_ops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
};
/*
* @description : 驱动入口函数
* @param : 无
* @return : 0 成功;其他 失败
*/
static int hello_init(void)
{
int result = 0;
printk("chrdev_hello init!\r \n");
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (chr_hello.major) { /* 定义了设备号 */
chr_hello.devid = MKDEV(chr_hello.major, 0);
/* 据定义设备号申请注册 */
result = register_chrdev_region(chr_hello.devid, NEWCHRDEV_CNT, NEWCHRDEV_NAME);
if(result < 0){
printk("register_chrdev fail \n");
goto out_err_1;
}
} else { /* 没有定义设备号,自动分配*/
result = alloc_chrdev_region(&chr_hello.devid, 0, NEWCHRDEV_CNT, NEWCHRDEV_NAME); /* 申请设备号 */
if(result < 0){
printk("alloc_chrdev_region fail \n"); //自动分配设备号错误
goto out_err_1;
}
chr_hello.major = MAJOR(chr_hello.devid); /* MAJOR宏获取分配号的主设备号 */
chr_hello.minor = MINOR(chr_hello.devid); /* MINOR宏获取分配号的次设备号 */
}
printk("chr_hello major=%d,minor=%d\r\n",chr_hello.major, chr_hello.minor);
/* 2、初始化cdev */
chr_hello.cdev.owner = THIS_MODULE;
cdev_init(&chr_hello.cdev, &chr_hello_ops);
/* 3、添加一个cdev */
cdev_add(&chr_hello.cdev, chr_hello.devid, NEWCHRDEV_CNT);
/* 4、创建类 */
chr_hello.class = class_create(THIS_MODULE, NEWCHRDEV_NAME);
if (IS_ERR(chr_hello.class)) {
printk(KERN_ERR "class_create() failed\n");
result = PTR_ERR(chr_hello.class);
goto out_err_2;
}
/* 5、创建设备 */
chr_hello.device = device_create(chr_hello.class, NULL, chr_hello.devid, NULL, NEWCHRDEV_NAME);
if (IS_ERR(chr_hello.device)) {
printk(KERN_ERR "device_create() failed\n");
result = PTR_ERR(chr_hello.device);
goto out_err_3;
}
spin_lock_init(&lock); //自旋锁初始化
return result;
//释放已申请的资源返回
out_err_3:
device_destroy(chr_hello.class, chr_hello.devid); /* 删除device */
out_err_2:
class_destroy(chr_hello.class); /* 删除class */
unregister_chrdev_region(chr_hello.devid, NEWCHRDEV_CNT); /* 注销设备号 */
cdev_del(&chr_hello.cdev);/* 删除cdev */
out_err_1:
return result;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void hello_exit(void)
{
printk("chrdev_hello exit!\r \n");
/* 注销字符设备驱动 */
device_destroy(chr_hello.class, chr_hello.devid); /* 删除device */
class_destroy(chr_hello.class); /* 删除class */
unregister_chrdev_region(chr_hello.devid, NEWCHRDEV_CNT); /* 注销设备号 */
cdev_del(&chr_hello.cdev);/* 删除cdev */
return;
}
//modinfo name.ko
MODULE_LICENSE("GPL"); //遵循GPL协议
MODULE_AUTHOR("CJX");
MODULE_DESCRIPTION("Just for Demon");
module_init(hello_init);
module_exit(hello_exit);
//cat proc/devices
应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char *filename = "/dev/hello";
int fd = open(filename,O_RDWR);
if(fd<0){
perror("open fail \n");
return -1;
}
printf("open file %s success!\r\n", filename);
sleep(20);
close(fd);
return 0;
}
这里有两个进程。

死锁的关键在于 应用中有一个 sleep 函数。
那么原因到底是为什么呢?
我也没看明白,大致了解了一下。
主要就在于这个 关闭了 抢占。
怎么就 类似 SMP的并发了?

这个 preempt_cout 很重要,但是先不看。

调度器 P1与P2的 解释要重点看一下。

总结: 调用 spin_lock 函数, 会关闭抢占,但是由于之前的锁没释放, 所以它只能自旋,又不能把它调度出去,所以它站住了CPU,导致CPU无法执行别的任务。
我的疑问:
1 又不是只有一个CPU核,
2 如果说 一个进程首先获得了锁,但是执行的任务有点小复杂,那么另一个任务在调用这个锁的时候,多多少少都会有点小自旋, 这时候,不也是 占住了CPU吗?难道说短时间的占住CPU没问题吗?
然后是基本的函数:
spin_lock_irq() = spin_lock() + local_irq_disable() // 获取自旋锁并关中断
spin_unlock_irq() = spin_unlock() + local_irq_enable() // 释放自旋锁并开中断
spin_lock_irqsave() = spin_lock() + local_irq_save() // 获取自旋锁并关中断,保存中断状态
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()//释放自旋锁,开中断并恢复中断状态
spin_lock_bh() = spin_lock() + local_bh_disable() // 获取自旋锁并关底半部中断
spin_unlock_bh() = spin_unlock() + local_bh_enable() // 释放自旋锁并发开底半部中断
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)