首先是基本概念
自旋锁,是不会休眠的。
在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#elifspin_unlock(&lock);printk("hello_open() spin_unlock \n");#endifreturn 0;
}
/** @description : 关闭/释放设备* @param - filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int hello_release (struct inode *inode, struct file *filep)
{printk("hello_release()\n");flage = 1;#if DEADspin_unlock(&lock);printk("hello_release() spin_unlock \n");#endifreturn 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() // 释放自旋锁并发开底半部中断