目录
原子操作和自旋锁的区别
相同点都是保护共享资源。
不同点在于:
原子操作简单易用,但只能做计数操作,保护的东西太少。
自旋锁主要用于多核处理器。短时期的轻量级加锁,加锁失败时原地打转、忙等待。避免了上下文调度和系统开销较小。
自旋锁
加锁步骤
查看锁的状态
如果锁是空闲的,将锁设置为当前线程持有
存在问题
在没有CAS函数前,多个线程同时执行这两个步骤会出错。
解决方案
CAS函数把这两个步骤合并为一条硬件级指令。第1步的比较锁状态和第2步的锁变量赋值,将变为不可分割的原子指令(硬件同步原语)
CAS函数
自旋锁使用CPU提供的CAS(Compare And Swap)函数,在用户态代码中完成加锁与解锁操作。
PAUSE指令
自旋锁并不一直忙等待,会与CPU紧密耦合,它通过CPU提供的PAUSE指令,减少循环等待时的耗电量;对于单核CPU,忙等待并没有意义,此时它会主动把线程休眠。
自旋锁原理
设自旋锁为变量lock,整数0表示锁是空闲状态,整数pid表示线程ID。
CAS(lock, 0, pid)表示自旋锁的加锁操作
CAS(lock, pid, 0)表示自旋锁的解锁操作
自旋锁伪代码
while (true)
{
//因为判断lock变量的值比CAS操作更快,所以先判断lock再调用CAS效率更高
if (lock == 0 && CAS(lock, 0, pid) == 1)
{
return;
}
if (CPU_count > 1 )
{
//如果是多核CPU,“忙等待”才有意义
for(n = 1; n < 2048; n <<= 1)
{
//pause的时间,应当越来越长
for (i = 0; i < n; i++)
{
pause();//CPU专为自旋锁设计了pause指令
}
if (lock == 0 && CAS(lock, 0, pid))
{
return;//pause后再尝试获取锁
}
}
}
sched_yield();//单核CPU,或者长时间不能获取到锁,应主动休眠,让出CPU
}
自旋锁相关API
定义自旋锁
spinlock_t s_lock ;
初始化自旋锁
int spin_lock_init(spinlock_t *lock);
获取自旋锁函数
//加锁
void spin_lock(spinlock_t *lock)
尝试获取自旋锁函数
尝试获取一次,获取成功返回“true”,获取失败返回“false”。程序继续往下执行
与上面的区别就是非阻塞
int spin_trylock(spinlock_t *lock)
释放自旋锁
void spin_unlock(spinlock_t *lock);
实验环节
dts_led.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define DEV_NAME "rgb_led"
#define DEV_CNT (1)
int rgb_led_red;
int rgb_led_green;
int rgb_led_blue;
/* 保护共享资源,此处保护flag */
spinlock_t s_lock;
char flag;
static dev_t led_devno;
static struct cdev led_chrdev;
struct class *class_led;
struct device *device;
struct device_node *rgb_led_device_node;
static int led_chrdev_open(struct inode *inode, struct file *filp)
{
spin_lock(&s_lock);
if(flag){
spin_unlock(&s_lock);
printk("driver on using! open failed!!!\n");
return -EBUSY;
}else{
flag++;
}
spin_unlock(&s_lock);
printk("open form driver\n");
return 0;
}
static ssize_t led_chrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret, error;
unsigned char receive_data[10]; //用于保存接收到的数据
unsigned int write_data = 0;
if(cnt > 10) cnt = 10;
error = copy_from_user(receive_data, buf, cnt);
if(error < 0) return -1;
ret = kstrtoint(receive_data, 16, &write_data);
if(ret) return -1;
/* 设置GPIO1_04输出电平 */
if(write_data & 0x04){
gpio_set_value(rgb_led_red, 0);
}else{
gpio_set_value(rgb_led_red, 1);
}
/* 设置GPIO4_20输出电平 */
if(write_data & 0x02){
gpio_set_value(rgb_led_green, 0);
}else{
gpio_set_value(rgb_led_green, 1);
}
/* 设置GPIO4_19输出电平 */
if(write_data & 0x01){
gpio_set_value(rgb_led_blue, 0);
}else{
gpio_set_value(rgb_led_blue, 1);
}
return cnt;
}
static int led_chrdev_release(struct inode *inode, struct file *filp)
{
spin_lock(&s_lock);
if(flag)
flag--;
spin_unlock(&s_lock);
printk(KERN_ALERT "finished!!!\n");
return 0;
}
static struct file_operations led_chrdev_fops = {
.owner = THIS_MODULE,
.open = led_chrdev_open,
.write = led_chrdev_write,
.release = led_chrdev_release,
};
static int led_probe(struct platform_device *pdv)
{
int ret = -1; //保存错误状态码
unsigned int register_data = 0;
printk(KERN_ALERT "match successed!\n");
/* 获取rgb_led的设备树节点 */
rgb_led_device_node = of_find_node_by_path("/rgb_led");
if(rgb_led_device_node == NULL){
printk(KERN_ERR "get rgb_led failed!\n");
return -1;
}
/* 获取red led GPIO 引脚号 */
rgb_led_red = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
if(rgb_led_red < 0){
printk(KERN_ERR "rgb_led_red failed!\n");
return -1;
}
/* 获取green led GPIO 引脚号 */
rgb_led_green = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
if(rgb_led_green < 0){
printk(KERN_ERR "rgb_led_green failed!\n");
return -1;
}
/* 获取blue led GPIO 引脚号 */
rgb_led_blue = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);
if(rgb_led_blue < 0){
printk(KERN_ERR "rgb_led_blue failed!\n");
return -1;
}
/* 设置GPIO为输出模式,并默认高电平 */
gpio_direction_output(rgb_led_red, 1);
gpio_direction_output(rgb_led_green, 1);
gpio_direction_output(rgb_led_blue, 1);
/* 第一步
* 采用动态分配的方式获取设备编号,次设备号为0
* 设备名称为rgb-leds,可通过命令cat /proc/devices查看
* DEV_CNT为1,当前只申请一个设备编号
*/
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk("fail to alloc led_devno\n");
goto alloc_err;
}
/* 第二步
* 关联字符设备结构体cdev与文件操作结构体file_operations
*/
led_chrdev.owner = THIS_MODULE;
cdev_init(&led_chrdev, &led_chrdev_fops);
/* 第三步
* 添加设备到cdev_map哈希表中
*/
ret = cdev_add(&led_chrdev, led_devno, DEV_CNT);
if(ret < 0){
printk("fail to add cdev\n");
goto add_err;
}
/* 第四步:创建类 */
class_led = class_create(THIS_MODULE, DEV_NAME);
/* 第五步:创建设备 */
device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);
return 0;
alloc_err:
return -1;
add_err:
//添加设备失败时,需要注销设备号
unregister_chrdev_region(led_devno, DEV_CNT);
printk("error!\n");
}
static const struct of_device_id rgb_led[] = {
{.compatible = "fire,rgb_led"},
{/* sentinel */}
};
/* 定义平台设备结构体 */
struct platform_driver led_platform_driver = {
.probe = led_probe,
.driver = {
.name = "rgb-leds-platform",
.owner = THIS_MODULE,
.of_match_table = rgb_led,
}
};
static int __init led_platform_driver_init(void)
{
int DriverState;
spin_lock_init(&s_lock);
DriverState = platform_driver_register(&led_platform_driver);
printk(KERN_ALERT "DriverState is %d\n", DriverState);
return 0;
}
static void __exit led_platform_driver_exit(void){
/* 销毁设备 */
device_destroy(class_led, led_devno);
/* 删除设备号 */
cdev_del(&led_chrdev);
/* 取消注册字符设备 */
unregister_chrdev_region(led_devno, DEV_CNT);
/* 销毁类 */
class_destroy(class_led);
platform_driver_unregister(&led_platform_driver);
printk(KERN_ALERT "led_platform_driver exit\n");
}
module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");
App.c文件
照旧
Makefile文件
照旧
执行过程
虚拟机:
执行make和make copy。生成.ko文件。
开发板(在挂载目录下执行):
sudo insmod dts_led.ko
ls /dev/rgb_led
sudo /mnt/App 1 &
sudo /mnt/App 2
sudo /mnt/App 1 &
sudo /mnt/App 2
sudo rmmod dts_led.ko