一文搞懂 Linux 驱动并发与竞争(学习笔记)
·
1. 基本概念:
对于linux的系统编程,博主做过linux AI项目后,比较了解了,所以就不过多赘述, 如果时间允许后续会将系统的笔记发出来;
-
竞争产生的原因:
-
多线程访问
-
中断访问
-
抢占访问
-
多核并发访问
-
-
共享资源:
-
全局变量
-
内核缓冲区
-
设备结构体
-
-
保护共享资源
-
原子操作
-
自旋锁
-
互斥锁
-
信号量
-
2. 原子操作:
2.1 概念:
-
原子就是借用化学的概念,不可再分
-
原子操作就是Linux下,该操作在执行完之前不会被任何事物打断(这个操作不可再分)。
-
原子操作分类:用于整形变量和位的保护
-
整型原子操作
-
位原子操作
-
2.2 API:
-
头文件:#include <linux/atomic.h>
-
atomic_t 结构体:
typedef struct {
int counter;
} atomic_t; // 用于32位
#ifdef CONFIG_64BIT
typedef struct {
long counter;
} atomic64_t; // 用于64位
#endif
- 32位的, 64位只在函数上加个64:(用到来查)


- 例子:

2.3 code:
- 因为多个设备打开这个驱动程序,存在竞争的情况,所以使用原子操作实现打开时互斥
/* 1- 定义一个原子变量: 原子变量初始化,用于实现设备单开(同一时间仅允许一个进程打开) */
static atomic64_t v = ATOMIC64_INIT(1);
/* 设备私有数据结构体 */
struct chrdev_test {
dev_t dev_num; // 设备号
int major, minor; // 主/次设备号
struct cdev cdev; // 字符设备结构体
struct class *class; // 设备类
};
static struct chrdev_test dev1;
static char kbuf[10] = {0}; // 内核数据缓冲区
/************************* 设备操作函数实现 *************************/
/* 2- 用原子变量实现互斥打开: 打开设备:原子变量判断,实现单开 */
static int open_test(struct inode *inode, struct file *file)
{
if (atomic64_read(&v) != 1) {
return -EBUSY; // 设备已被占用,返回忙
}
atomic64_set(&v, 0); // 标记设备已打开
return 0;
}
/* 读设备:内核 -> 用户 拷贝数据 */
static ssize_t read_test(struct file *file, char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
char data[] = "topeet";
printk(KERN_INFO "read_test: enter\n");
ret = copy_to_user(ubuf, data, strlen(data));
if (ret) {
printk(KERN_ERR "read_test: copy_to_user error\n");
} else {
printk(KERN_INFO "read_test: copy_to_user ok\n");
}
return 0;
}
/* 写设备:用户 -> 内核 拷贝数据,并根据内容延时 */
static ssize_t write_test(struct file *file, const char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
ret = copy_from_user(kbuf, ubuf, len);
if (ret) {
printk(KERN_ERR "write_test: copy_from_user error\n");
return -EFAULT;
}
printk(KERN_INFO "write_test: received data: %s\n", kbuf);
/* 根据写入内容执行不同延时 */
if (!strcmp(kbuf, "topeet")) {
ssleep(4);
} else if (!strcmp(kbuf, "itop")) {
ssleep(2);
}
return len; // 返回实际写入长度
}
/* 3- 关闭设备:释放原子变量 */
static int release_test(struct inode *inode, struct file *file)
{
atomic64_set(&v, 1); // 标记设备空闲
return 0;
}
3. 自旋锁:
3.1 概念:
-
死等的现象: 当一个线程尝试获取自旋锁而发现该锁已被其他线程持有时, 它不会进入睡眠状态等待, 而是会持续循环地尝试获取锁, 直到成功为止。这称为自旋。
-
适用于自旋锁锁持有时间短且线程不希望在重新调度上花费过多时间的情况
-
因为死等,特别浪费CPU时间
3.2 API:
-
头文件: #include <linux/spinlock.h>
-
用到再记:


3.3 code
- 仅在 open 和 release 中对共享标志 flag 使用自旋锁
// 1- 自旋锁 + 设备状态标志(实现设备同一时间仅能被一个进程打开)
static spinlock_t spinlock_test;
static int flag = 1; // 1=设备空闲 0=设备已被占用
// 设备私有数据结构体
struct chrdev_test {
dev_t dev_num; // 设备号
int major, minor; // 主/次设备号
struct cdev cdev_test; // 字符设备
struct class *class_test; // 设备类
};
static struct chrdev_test dev1;
static char kbuf[10] = {0}; // 内核缓冲区
/************************* 设备操作函数 *************************/
// 打开设备:自旋锁保护 flag,实现互斥单开
static int open_test(struct inode *inode, struct file *file)
{
spin_lock(&spinlock_test); // 2-加锁
if (flag != 1) {
spin_unlock(&spinlock_test);
return -EBUSY; // 设备忙
}
flag = 0; // 标记设备已占用
spin_unlock(&spinlock_test); // 解锁
return 0;
}
// 读设备:内核 -> 用户 拷贝 "topeet"
static ssize_t read_test(struct file *file, char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
char data_buf[] = "topeet";
printk(KERN_INFO "this is read_test\n");
ret = copy_to_user(ubuf, data_buf, strlen(data_buf));
if (ret)
printk(KERN_ERR "copy_to_user error\n");
else
printk(KERN_INFO "copy_to_user ok\n");
return 0;
}
// 写设备:用户 -> 内核 拷贝数据,并根据内容延时
static ssize_t write_test(struct file *file, const char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
ret = copy_from_user(kbuf, ubuf, len);
if (ret) {
printk(KERN_ERR "copy_from_user error\n");
return -EFAULT;
}
// 根据写入内容执行延时
if (!strcmp(kbuf, "topeet"))
ssleep(4);
else if (!strcmp(kbuf, "itop"))
ssleep(2);
printk(KERN_INFO "copy_from_user buf: %s\n", kbuf);
return len;
}
// 关闭设备:释放设备占用标志
static int release_test(struct inode *inode, struct file *file)
{
printk(KERN_INFO "this is release_test\n");
spin_lock(&spinlock_test);
flag = 1; // 标记设备空闲
spin_unlock(&spinlock_test);
return 0;
}
// 文件操作集合
static const struct file_operations fops_test = {
.owner = THIS_MODULE,
.open = open_test,
.read = read_test,
.write = write_test,
.release = release_test,
};
/************************* 模块加载/卸载 *************************/
static int __init spinlock_drv_init(void)
{
int ret;
// 初始化自旋锁
spin_lock_init(&spinlock_test);
// 动态申请设备号
ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "chrdev_name");
if (ret < 0) {
printk(KERN_ERR "alloc_chrdev_region error\n");
return ret;
}
printk(KERN_INFO "alloc_chrdev_region ok\n");
// 获取主/次设备号
dev1.major = MAJOR(dev1.dev_num);
dev1.minor = MINOR(dev1.dev_num);
printk(KERN_INFO "major=%d, minor=%d\n", dev1.major, dev1.minor);
// 初始化并添加字符设备
cdev_init(&dev1.cdev_test, &fops_test);
dev1.cdev_test.owner = THIS_MODULE;
cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
// 创建设备类 + 设备节点(自动生成 /dev/device_test)
dev1.class_test = class_create(THIS_MODULE, "class_test");
device_create(dev1.class_test, NULL, dev1.dev_num, NULL, "device_test");
printk(KERN_INFO "spinlock driver init success\n");
return 0;
}
4. 信号量
4.1 API:
- 头文件:#include <linux/semaphore.h>

4.2 code:
//1- 二值信号量:实现设备单开(同一时间仅允许一个进程打开)
struct semaphore semaphore_test;
// 设备私有数据结构体
struct chrdev_test {
dev_t dev_num; // 设备号
int major, minor; // 主/次设备号
struct cdev cdev_test; // 字符设备
struct class *class_test; // 设备类
};
static struct chrdev_test dev1;
static char kbuf[10] = {0}; // 内核数据缓冲区
/************************* 设备操作函数实现 *************************/
// 2- 打开设备:信号量 P 操作(申请资源)
static int open_test(struct inode *inode, struct file *file)
{
printk(KERN_INFO "this is open_test\n");
down(&semaphore_test); // 信号量-1,为0则阻塞等待
return 0;
}
// 读设备:内核 -> 用户 拷贝 "topeet"
static ssize_t read_test(struct file *file, char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
char data_buf[] = "topeet";
printk(KERN_INFO "this is read_test\n");
ret = copy_to_user(ubuf, data_buf, strlen(data_buf));
if (ret)
printk(KERN_ERR "copy_to_user error\n");
else
printk(KERN_INFO "copy_to_user ok\n");
return 0;
}
// 写设备:用户 -> 内核 拷贝数据,并根据内容延时
static ssize_t write_test(struct file *file, const char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
ret = copy_from_user(kbuf, ubuf, len);
if (ret) {
printk(KERN_ERR "copy_from_user error\n");
return -EFAULT;
}
// 根据写入内容执行不同延时
if (!strcmp(kbuf, "topeet"))
ssleep(4);
else if (!strcmp(kbuf, "itop"))
ssleep(2);
printk(KERN_INFO "copy_from_user buf: %s\n", kbuf);
return len;
}
// 3-关闭设备:信号量 V 操作(释放资源)
static int release_test(struct inode *inode, struct file *file)
{
up(&semaphore_test); // 信号量+1,唤醒等待的进程
printk(KERN_INFO "this is release_test\n");
return 0;
}
// 文件操作集合
static const struct file_operations fops_test = {
.owner = THIS_MODULE,
.open = open_test,
.read = read_test,
.write = write_test,
.release = release_test,
};
/************************* 模块加载/卸载 *************************/
static int __init sema_drv_init(void)
{
int ret;
// 初始化信号量,值为 1(二值信号量 = 互斥锁)
sema_init(&semaphore_test, 1);
// 动态申请设备号
ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "chrdev_name");
if (ret < 0) {
printk(KERN_ERR "alloc_chrdev_region error\n");
return ret;
}
printk(KERN_INFO "alloc_chrdev_region ok\n");
// 获取主/次设备号
dev1.major = MAJOR(dev1.dev_num);
dev1.minor = MINOR(dev1.dev_num);
printk(KERN_INFO "major=%d, minor=%d\n", dev1.major, dev1.minor);
// 初始化并添加字符设备
cdev_init(&dev1.cdev_test, &fops_test);
dev1.cdev_test.owner = THIS_MODULE;
cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
// 创建设备类 + 设备节点 /dev/device_test
dev1.class_test = class_create(THIS_MODULE, "class_test");
device_create(dev1.class_test, NULL, dev1.dev_num, NULL, "device_test");
printk(KERN_INFO "semaphore driver init success\n");
return 0;
}
5. 互斥量:
5.1 API:
- #include <linux/mutex.h>

5.2 code
// 1-互斥锁:实现设备单开(同一时间仅允许一个进程打开)
struct mutex mutex_test;
// 设备私有数据结构体
struct chrdev_test {
dev_t dev_num; // 设备号
int major, minor; // 主/次设备号
struct cdev cdev_test; // 字符设备
struct class *class_test; // 设备类
};
static struct chrdev_test dev1;
static char kbuf[10] = {0}; // 内核数据缓冲区
/************************* 设备操作函数实现 *************************/
// 2-打开设备:互斥锁上锁
static int open_test(struct inode *inode, struct file *file)
{
printk(KERN_INFO "this is open_test\n");
mutex_lock(&mutex_test); // 上锁,已上锁则阻塞等待
return 0;
}
// 读设备:内核 -> 用户 拷贝 "topeet"
static ssize_t read_test(struct file *file, char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
char data_buf[] = "topeet";
printk(KERN_INFO "this is read_test\n");
ret = copy_to_user(ubuf, data_buf, strlen(data_buf));
if (ret)
printk(KERN_ERR "copy_to_user error\n");
else
printk(KERN_INFO "copy_to_user ok\n");
return 0;
}
// 写设备:用户 -> 内核 拷贝数据,并根据内容延时
static ssize_t write_test(struct file *file, const char __user *ubuf,
size_t len, loff_t *off)
{
int ret;
ret = copy_from_user(kbuf, ubuf, len);
if (ret) {
printk(KERN_ERR "copy_from_user error\n");
return -EFAULT;
}
// 根据写入内容执行不同延时
if (!strcmp(kbuf, "topeet"))
ssleep(4);
else if (!strcmp(kbuf, "itop"))
ssleep(2);
printk(KERN_INFO "copy_from_user buf: %s\n", kbuf);
return len;
}
// 3-关闭设备:互斥锁解锁
static int release_test(struct inode *inode, struct file *file)
{
mutex_unlock(&mutex_test); // 解锁,唤醒等待的进程
printk(KERN_INFO "this is release_test\n");
return 0;
}
// 文件操作集合
static const struct file_operations fops_test = {
.owner = THIS_MODULE,
.open = open_test,
.read = read_test,
.write = write_test,
.release = release_test,
};
/************************* 模块加载/卸载 *************************/
static int __init mutex_drv_init(void)
{
int ret;
// 初始化互斥锁
mutex_init(&mutex_test);
// 动态申请设备号
ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "chrdev_name");
if (ret < 0) {
printk(KERN_ERR "alloc_chrdev_region error\n");
return ret;
}
printk(KERN_INFO "alloc_chrdev_region ok\n");
// 获取主/次设备号
dev1.major = MAJOR(dev1.dev_num);
dev1.minor = MINOR(dev1.dev_num);
printk(KERN_INFO "major=%d, minor=%d\n", dev1.major, dev1.minor);
// 初始化并添加字符设备
cdev_init(&dev1.cdev_test, &fops_test);
dev1.cdev_test.owner = THIS_MODULE;
cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
// 创建设备类 + 设备节点 /dev/device_test
dev1.class_test = class_create(THIS_MODULE, "class_test");
device_create(dev1.class_test, NULL, dev1.dev_num, NULL, "device_test");
printk(KERN_INFO "mutex driver init success\n");
return 0;
}
更多推荐




所有评论(0)