11.并发:自旋锁

目录

原子操作和自旋锁的区别

自旋锁

加锁步骤

存在问题

解决方案

CAS函数

PAUSE指令

自旋锁原理

自旋锁相关API

定义自旋锁

初始化自旋锁

获取自旋锁函数

尝试获取自旋锁函数

释放自旋锁

实验环节

dts_led.c文件

App.c文件

Makefile文件

执行过程


原子操作和自旋锁的区别

相同点都是保护共享资源。

不同点在于:

原子操作简单易用,但只能做计数操作,保护的东西太少。

自旋锁主要用于多核处理器。短时期的轻量级加锁,加锁失败时原地打转、忙等待。避免了上下文调度和系统开销较小。

自旋锁

加锁步骤

查看锁的状态

如果锁是空闲的,将锁设置为当前线程持有

存在问题

在没有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文件

照旧

执行过程

虚拟机:

执行makemake 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值