首先是基本概念

自旋锁,是不会休眠的。

ARM平台下,自旋锁的实现使用了ldrexstrex、以及内存屏障指令dmbdsbwfesev

自旋锁, 进程上下文中,调用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()		//	释放自旋锁并发开底半部中断

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐