26.linux内核(线程/进程)同步——自旋锁

1.linux内核(线程/进程)同步——内存屏障
2.linux内核(线程/进程)同步——原子操作
3.linux内核(线程/进程)同步——自旋锁
4.linux内核(线程/进程)同步——信号量
5.linux内核(线程/进程)同步——互斥锁
6.linux内核(线程/进程)同步——完成量

自旋锁

自旋锁(spin lock)是一种排他锁,采用死循环来竞争资源,竞争不到就无限尝试,所以又叫自旋锁。适合短生命周期的资源竞争,比较浪费CPU,实现原理就是上一篇当中的原子操作。当strex操作失败后会重新进入 ldrexstrex 的过程,无限尝试下去,直到成功。
相关api在 spinlock.h

//定义锁
spinlock_t data_lock;
//初始化锁
spin_lock_init(_lock)
//上锁,如果锁不上就会自旋
void spin_lock(spinlock_t *lock)
//上锁,锁上返回true,锁不上返回false,锁不上不会自旋
int spin_trylock(spinlock_t *lock)
//释放锁
void spin_unlock(spinlock_t *lock)

自旋锁适用于多核CPU或具有抢占的单核CPU,对于单核不支持抢占的CPU自旋锁无效。另外在资源临界区有可能被中断打断,结合中断操作衍生了如下api

//关中断并上锁
void spin_lock_irq(spinlock_t *lock)
//释放锁并开中断
void spin_unlock_irq(spinlock_t *lock)
//关中断底半部并上锁
void spin_lock_bh(spinlock_t *lock)
//释放锁并开中断底半部
void spin_unlock_bh(spinlock_t *lock)
//关中断并保存状态字并上锁
spin_lock_irqsave(lock, flags)
//释放锁并开中断恢复状态字
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
自旋锁字符设备驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

static int data = 1;
module_param(data, int, 0644);  //声明模块参数
dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 100;
char * char_data;
static spinlock_t data_lock;

static int open(struct inode * node, struct file * fl){
	return 0;
}

static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
	return 0;
}

static ssize_t read(struct file * fl, char __user * buf, size_t len, loff_t * offset){
	int ret = 0,copy_len,data_len;
	spin_lock(&data_lock);

	data_len = strlen(char_data)+1;
	if(fl->f_pos + len > data_len)
		copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
	else
		copy_len = len;					 //没超过

	ret = copy_to_user(buf,char_data+fl->f_pos,copy_len);
	ret = copy_len - ret;
	*offset += ret;						 //移动文件指针
	spin_unlock(&data_lock);
	return ret;
}

static ssize_t write(struct file * fl, const char __user * buf, size_t len, loff_t * offset){
	int ret = 0,copy_len,data_len = buffer_size;
	spin_lock(&data_lock);

	if(fl->f_pos + len > data_len)
		copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
	else
		copy_len = len;					 //没超过

	ret = copy_from_user(char_data+fl->f_pos,buf,copy_len);
	ret = copy_len - ret;
	*offset += ret;						 //移动文件指针
	spin_unlock(&data_lock);
	return ret;
}

struct file_operations my_opts = {
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl
};

static int __init char_init(void){
	int ret = 0;

    devid = MKDEV(241, 1);								//换算设备号
    ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到
    if (ret < 0)
        goto err0;

    cdev_init(&char_dev,&my_opts);						//绑定opt结构体
    char_dev.owner = THIS_MODULE;
    ret = cdev_add(&char_dev,devid,1);					//注册字符设备驱动
    if (ret < 0)
    	goto err1;

    char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹
    device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1

    char_data = kzalloc(buffer_size,GFP_KERNEL);
    spin_lock_init(&data_lock);
	printk("char init\n");
    return 0;

	err1:
	    unregister_chrdev_region(devid, 1);
    err0:
        return ret;
}

static void __exit char_exit(void){
	kfree(char_data);
	unregister_chrdev_region(devid, 1);
	cdev_del(&char_dev);
	device_destroy(char_class,devid);
	class_destroy(char_class);
	printk("char exit\n");
}

module_init(char_init);
module_exit(char_exit);
读写自旋锁

读写自旋锁允许多个读操作同时进行,写操作只能独自进行。读写锁有更好的性能。相关api在rwlock.h中

初始化锁
//定义锁
spinlock_t data_lock;
rwlock_init(lock)
读操作
read_lock(lock);
read_lock_irq(lock)
read_lock_irqsave(lock, flags)
read_lock_bh(lock)
读解锁
read_unlock(lock);
read_unlock_irq(lock)
read_unlock_irqsave(lock, flags)
read_unlock_bh(lock)
写操作
write_lock(lock);
write_lock_irq(lock)
write_lock_irqsave(lock, flags)
write_lock_bh(lock)
写解锁
write_unlock(lock);
write_unlock_irq(lock)
write_unlock_irqsave(lock, flags)
write_unlock_bh(lock)
读写锁字符设备驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

static int data = 1;
module_param(data, int, 0644);  //声明模块参数
dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 100;
char * char_data;
static rwlock_t data_lock;

static int open(struct inode * node, struct file * fl){
	return 0;
}

static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
	return 0;
}

static ssize_t read(struct file * fl, char __user * buf, size_t len, loff_t * offset){
	int ret = 0,copy_len,data_len;
	read_lock(&data_lock);

	data_len = strlen(char_data)+1;
	if(fl->f_pos + len > data_len)
		copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
	else
		copy_len = len;					 //没超过

	ret = copy_to_user(buf,char_data+fl->f_pos,copy_len);
	ret = copy_len - ret;
	*offset += ret;						 //移动文件指针
	read_unlock(&data_lock);
	return ret;
}

static ssize_t write(struct file * fl, const char __user * buf, size_t len, loff_t * offset){
	int ret = 0,copy_len,data_len = buffer_size;
	write_lock(&data_lock);

	if(fl->f_pos + len > data_len)
		copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
	else
		copy_len = len;					 //没超过

	ret = copy_from_user(char_data+fl->f_pos,buf,copy_len);
	ret = copy_len - ret;
	*offset += ret;						 //移动文件指针
	write_unlock(&data_lock);
	return ret;
}

struct file_operations my_opts = {
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl
};

static int __init char_init(void){
	int ret = 0;

    devid = MKDEV(241, 1);								//换算设备号
    ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到
    if (ret < 0)
        goto err0;

    cdev_init(&char_dev,&my_opts);						//绑定opt结构体
    char_dev.owner = THIS_MODULE;
    ret = cdev_add(&char_dev,devid,1);					//注册字符设备驱动
    if (ret < 0)
    	goto err1;

    char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹
    device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1

    char_data = kzalloc(buffer_size,GFP_KERNEL);
    rwlock_init(&data_lock);
	printk("char init\n");
    return 0;

	err1:
	    unregister_chrdev_region(devid, 1);
    err0:
        return ret;
}

static void __exit char_exit(void){
	kfree(char_data);
	unregister_chrdev_region(devid, 1);
	cdev_del(&char_dev);
	device_destroy(char_class,devid);
	class_destroy(char_class);
	printk("char exit\n");
}

module_init(char_init);
module_exit(char_exit);
顺序自旋锁

顺序自旋锁是对读写锁的一种优化,顺序锁只有写跟写具有排他性,也就是读操作永远不会被阻塞。读完需要检查一下读期间有没有数据写入,读到脏数据了要重新读取。感觉顺序锁的优点在于提高了写入的效率,写入不必等待读取都退出。在读操作密集的场景有比较好的性能。
相关api在seqlock.h中

//初始化读锁
seqlock_init(x)
//写锁
void write_seqlock(seqlock_t *sl)
int write_tryseqlock(seqlock_t *sl)
//关中断写锁
write_seqlock_irq(lock)
//关中断保存状态字写锁
write_seqlock_irqsave(lock, flags)
//关中断底半部写
write_seqlock_bh(lock)
//释放写锁
void write_sequnlock(seqlock_t *sl)
write_sequnlock_irqrestore(lock, flags)
write_sequnlock_irq(lock)
write_sequnlock_bh(lock)
//读锁
unsigned read_seqbegin(const seqlock_t *sl)
read_seqbegin_irqsave(lock, flags)
read_seqretry_irqrestore(lock, iv, flags)
//读期间是否有数据写入
int read_seqretry(const seqlock_t *sl, unsigned start)

读取操作一般在do{ }while()当中进行

do{
seq_no = read_seqbegin(&lock);
//读操作
}while(read_seqretry(&lock,seq_no));
顺序锁字符设备驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

static int data = 1;
module_param(data, int, 0644);  //声明模块参数
dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 100;
char * char_data;
static seqlock_t data_lock;

static int open(struct inode * node, struct file * fl){
	return 0;
}

static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
	return 0;
}

static ssize_t read(struct file * fl, char __user * buf, size_t len, loff_t * offset){
	int ret = 0,copy_len,data_len;
	unsigned int seq_no;
	do{
		seq_no = read_seqbegin(&data_lock);

		data_len = strlen(char_data)+1;
		if(fl->f_pos + len > data_len)
			copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
		else
			copy_len = len;					 //没超过

		ret = copy_to_user(buf,char_data+fl->f_pos,copy_len);
		ret = copy_len - ret;
	}while(read_seqretry(&data_lock,seq_no));

	*offset += ret;						 //移动文件指针
	return ret;
}

static ssize_t write(struct file * fl, const char __user * buf, size_t len, loff_t * offset){
	int ret = 0,copy_len,data_len = buffer_size;
	write_seqlock(&data_lock);

	if(fl->f_pos + len > data_len)
		copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
	else
		copy_len = len;					 //没超过

	ret = copy_from_user(char_data+fl->f_pos,buf,copy_len);
	ret = copy_len - ret;
	*offset += ret;						 //移动文件指针
	write_sequnlock(&data_lock);
	return ret;
}

struct file_operations my_opts = {
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl
};

static int __init char_init(void){
	int ret = 0;

    devid = MKDEV(241, 1);								//换算设备号
    ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到
    if (ret < 0)
        goto err0;

    cdev_init(&char_dev,&my_opts);						//绑定opt结构体
    char_dev.owner = THIS_MODULE;
    ret = cdev_add(&char_dev,devid,1);					//注册字符设备驱动
    if (ret < 0)
    	goto err1;

    char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹
    device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1

    char_data = kzalloc(buffer_size,GFP_KERNEL);
    seqlock_init(&data_lock);
	printk("char init\n");
    return 0;

	err1:
	    unregister_chrdev_region(devid, 1);
    err0:
        return ret;
}

static void __exit char_exit(void){
	kfree(char_data);
	unregister_chrdev_region(devid, 1);
	cdev_del(&char_dev);
	device_destroy(char_class,devid);
	class_destroy(char_class);
	printk("char exit\n");
}

module_init(char_init);
module_exit(char_exit);

猜你喜欢

转载自blog.csdn.net/qq_16054639/article/details/107525940