在 Linux 驱动开发领域,驱动程序的稳定运行是保障系统正常工作的关键。然而,随着硬件设备功能的日益复杂和多任务系统的广泛应用,并发与竞争问题逐渐成为影响驱动稳定性和可靠性的重要因素。这些问题若处理不当,可能导致数据错误、系统崩溃等严重后果。本文将深入探讨 Linux 驱动中并发与竞争产生的原因、具体场景,并详细介绍相应的解决方案,帮助开发者有效应对这些挑战。
一、并发与竞争的概念
1.1 并发
并发是指在同一时间段内,多个执行单元(如进程、线程、中断处理程序等)同时对系统资源进行访问或操作。在 Linux 驱动中,并发情况十分常见,例如多个用户空间进程同时访问设备驱动、进程与中断处理程序同时操作设备资源等。这些并发操作虽然提高了系统的效率,但也带来了潜在的风险。
1.2 竞争
竞争(Race Condition)则是并发操作带来的不良结果,当多个执行单元同时访问共享资源(如内存变量、硬件寄存器),并且这些访问的顺序和时机不确定,导致最终结果依赖于执行单元的执行顺序时,就会出现竞争条件。竞争条件可能导致数据不一致、程序逻辑错误等问题,严重影响驱动的正常运行。
二、并发与竞争产生的原因
2.1 多任务环境
Linux 作为多任务操作系统,支持多个进程和线程同时运行。当这些进程或线程同时访问驱动程序中的共享资源时,就可能引发并发与竞争问题。例如,两个进程同时对设备的计数器进行加 1 操作,如果没有适当的保护措施,可能会导致计数器的值出现错误。
2.2 中断的异步性
中断是硬件设备向 CPU 发送信号的一种机制,用于通知 CPU 发生了特定事件。中断处理程序的执行具有异步性,它可能在任何时刻打断进程的正常执行。当进程正在访问设备资源时,若此时中断发生,中断处理程序也对同一资源进行操作,就容易产生竞争。比如,进程正在读取设备的状态寄存器,中断处理程序却在此时修改了该寄存器的值,可能导致进程读取到错误的状态信息。
2.3 内核抢占
Linux 内核支持抢占机制,即当高优先级任务准备好运行时,正在执行的低优先级任务可以被暂停,以便高优先级任务优先执行。在驱动程序执行过程中,如果发生内核抢占,被抢占的任务可能正在访问共享资源,而新调度的任务也试图访问同一资源,从而引发竞争。
三、并发与竞争的具体场景及解决方案
3.1 进程间并发与竞争
场景描述 多个用户空间进程同时打开设备文件并进行读写操作,若驱动程序没有对共享资源进行保护,就会出现竞争。例如,一个字符设备驱动维护着一个缓冲区用于存储数据,多个进程同时向缓冲区写入数据,可能导致数据覆盖或混乱。 解决方案:信号量(Semaphore) 信号量是一种经典的同步机制,通过一个计数器来控制对共享资源的访问。在驱动中,可以使用struct semaphore定义信号量,并通过sem_init函数进行初始化。在访问共享资源前,调用down函数获取信号量,如果信号量的值为 0,则调用进程会被阻塞,直到信号量被释放;在访问完共享资源后,调用up函数释放信号量,唤醒等待的进程。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#define DEVICE_NAME "simple_char_dev"
#define DEVICE_SIZE 1024
struct semaphore my_sem;
struct simple_dev_t {
struct cdev cdev;
dev_t dev_num;
struct class *class;
struct device *device;
char buffer[DEVICE_SIZE]; // 数据缓冲区
} ;
static struct simple_dev_t simple_dev;
static int my_char_open(struct inode *inode, struct file *filp) {
struct simple_dev_t *dev = container_of(inode->i_cdev, struct simple_dev_t, cdev);
filp->private_data = dev;
return 0;
}
static ssize_t my_char_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
struct simple_dev_t *dev = filp->private_data;
// 获取信号量
down(&my_sem);
// 进行数据写入操作
if (copy_from_user(dev->buffer, buf, count)) {
// 释放信号量
up(&my_sem);
return -EFAULT;
}
printk(KERN_INFO "my_char_write buffer = %s\n", dev->buffer);
// 释放信号量
up(&my_sem);
return count;
}
static const struct file_operations my_char_fops = {
.owner = THIS_MODULE,
.open = my_char_open,
.write = my_char_write,
};
static int __init my_char_driver_init(void) {
// 初始化信号量
sema_init(&my_sem, 1);
int result;
// 动态分配设备号
result = alloc_chrdev_region(&simple_dev.dev_num, 0, 1, DEVICE_NAME);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to allocate major number\n");
return result;
}
printk(KERN_INFO "my_char_driver_init Allocated major number: %d\n", MAJOR(simple_dev.dev_num));
// 初始化cdev结构
cdev_init(&simple_dev.cdev, &my_char_fops);
simple_dev.cdev.owner = THIS_MODULE;
// 添加字符设备
result = cdev_add(&simple_dev.cdev, simple_dev.dev_num, 1);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to add cdev\n");
unregister_chrdev_region(simple_dev.dev_num, 1);
return result;
}
// 创建类
simple_dev.class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(simple_dev.class)) {
printk(KERN_ERR "my_char_driver_init Failed to create class\n");
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.class);
}
// 创建设备节点
simple_dev.device = device_create(simple_dev.class, NULL, simple_dev.dev_num, NULL, DEVICE_NAME);
if (IS_ERR(simple_dev.device)) {
printk(KERN_ERR "my_char_driver_init Failed to create device\n");
class_destroy(simple_dev.class);
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.device);
}
return 0;
}
static void __exit my_char_driver_exit(void) {
// 1. 销毁设备节点
if (simple_dev.device)
device_destroy(simple_dev.class, simple_dev.dev_num);
// 2. 销毁设备类
if (simple_dev.class)
class_destroy(simple_dev.class);
// 3. 注销字符设备
cdev_del(&simple_dev.cdev);
// 4. 释放设备号
unregister_chrdev_region(simple_dev.dev_num, 1);
printk(KERN_INFO "my_char_driver_exit\n");
}
module_init(my_char_driver_init);
module_exit(my_char_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple driver using concurrent and competition");
MODULE_AUTHOR("cmy");
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_testConcurrency(JNIEnv *env, jobject thiz) {
// 打开设备文件
int fd = open("/dev/simple_char_dev", O_WRONLY);
if (fd < 0) {
LOGD("open failed");
return 1;
}
write(fd, "testConcurrency", 15);
close(fd);
return 0;
}
应用层开启2个线程写入数据。
3.2 进程与中断处理程序间并发与竞争
场景描述 当进程正在访问设备资源时,中断发生,中断处理程序也需要访问同一资源。例如,一个定时器设备驱动,进程正在读取定时器的当前计数值,此时定时器超时触发中断,中断处理程序需要重置计数器,若不进行同步,可能导致进程读取到错误的计数值。 解决方案:自旋锁(Spin Lock)与中断屏蔽 自旋锁:自旋锁是一种忙等待的锁机制,获取锁的线程在未获得锁时会一直循环等待,直到锁被释放。在 Linux 驱动中,使用spin_lock和spin_unlock函数来操作自旋锁。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#define DEVICE_NAME "simple_char_dev"
#define BUFFER_SIZE 1024
struct simple_dev_t {
struct cdev cdev;
dev_t dev_num;
struct class *class;
struct device *device;
char buffer[BUFFER_SIZE]; // 数据缓冲区
spinlock_t lock; // 自旋锁
int count; // 缓冲区数据计数
} ;
static struct simple_dev_t simple_dev;
static int my_char_open(struct inode *inode, struct file *filp) {
struct simple_dev_t *dev = container_of(inode->i_cdev, struct simple_dev_t, cdev);
filp->private_data = dev;
return 0;
}
// 读操作
static ssize_t spinlock_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
struct simple_dev_t *dev = filp->private_data;
unsigned long flags;
ssize_t ret = 0;
// 加锁(中断上下文安全版本)
spin_lock_irqsave(&dev->lock, flags);
// 临界区:访问共享资源
if (dev->count > 0) {
count = min(count, (size_t)dev->count);
if (copy_to_user(buf, dev->buffer, count)) {
ret = -EFAULT;
} else {
ret = count;
dev->count -= count;
}
}
// 解锁并恢复中断状态
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
// 写操作
static ssize_t spinlock_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
struct simple_dev_t *dev = filp->private_data;
unsigned long flags;
ssize_t ret = 0;
// 加锁
spin_lock_irqsave(&dev->lock, flags);
// 临界区:访问共享资源
if (dev->count + count > BUFFER_SIZE) {
count = BUFFER_SIZE - dev->count;
}
if (count > 0) {
if (copy_from_user(dev->buffer + dev->count, buf, count)) {
ret = -EFAULT;
} else {
ret = count;
dev->count += count;
}
}
// 解锁
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static const struct file_operations my_char_fops = {
.owner = THIS_MODULE,
.open = my_char_open,
.read = spinlock_read,
.write = spinlock_write,
};
static int __init my_char_driver_init(void) {
int result;
// 动态分配设备号
result = alloc_chrdev_region(&simple_dev.dev_num, 0, 1, DEVICE_NAME);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to allocate major number\n");
return result;
}
printk(KERN_INFO "my_char_driver_init Allocated major number: %d\n", MAJOR(simple_dev.dev_num));
// 初始化自旋锁
spin_lock_init(&simple_dev.lock);
// 初始化cdev结构
cdev_init(&simple_dev.cdev, &my_char_fops);
simple_dev.cdev.owner = THIS_MODULE;
// 添加字符设备
result = cdev_add(&simple_dev.cdev, simple_dev.dev_num, 1);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to add cdev\n");
unregister_chrdev_region(simple_dev.dev_num, 1);
return result;
}
// 创建类
simple_dev.class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(simple_dev.class)) {
printk(KERN_ERR "my_char_driver_init Failed to create class\n");
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.class);
}
// 创建设备节点
simple_dev.device = device_create(simple_dev.class, NULL, simple_dev.dev_num, NULL, DEVICE_NAME);
if (IS_ERR(simple_dev.device)) {
printk(KERN_ERR "my_char_driver_init Failed to create device\n");
class_destroy(simple_dev.class);
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.device);
}
return 0;
}
static void __exit my_char_driver_exit(void) {
// 1. 销毁设备节点
if (simple_dev.device)
device_destroy(simple_dev.class, simple_dev.dev_num);
// 2. 销毁设备类
if (simple_dev.class)
class_destroy(simple_dev.class);
// 3. 注销字符设备
cdev_del(&simple_dev.cdev);
// 4. 释放设备号
unregister_chrdev_region(simple_dev.dev_num, 1);
printk(KERN_INFO "my_char_driver_exit\n");
}
module_init(my_char_driver_init);
module_exit(my_char_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple driver using concurrent and competition");
MODULE_AUTHOR("cmy");
// 写线程函数
void* writer_thread(void* arg) {
int fd = *(int*)arg;
char buffer[1000];
int i;
for (i = 0; i < 1000; i++) {
sprintf(buffer, "Message %d from writer\n", i);
write(fd, buffer, strlen(buffer));
usleep(1000); // 短暂休眠
}
return NULL;
}
// 读线程函数
void* reader_thread(void* arg) {
int fd = *(int*)arg;
char buffer[1000];
int i, bytes;
for (i = 0; i < 1000; i++) {
memset(buffer, 0, 1000);
bytes = read(fd, buffer, 1000);
if (bytes > 0) {
LOGD("Reader got: %s", buffer);
}
usleep(2000); // 短暂休眠
}
return NULL;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_testConcurrency1(JNIEnv *env, jobject thiz) {
int fd;
pthread_t writer, reader;
// 打开设备文件
fd = open("/dev/simple_char_dev", O_RDWR);
if (fd < 0) {
LOGD("Failed to open device");
return -1;
}
// 创建读写线程
pthread_create(&writer, NULL, writer_thread, &fd);
pthread_create(&reader, NULL, reader_thread, &fd);
// 等待线程结束
pthread_join(writer, NULL);
pthread_join(reader, NULL);
// 关闭设备
close(fd);
return 0;
}
模拟多进程 / 多线程环境下的并发访问。
3.3 内核抢占导致的并发与竞争
场景描述 当低优先级任务正在访问共享资源时,高优先级任务就绪,内核抢占发生,高优先级任务也试图访问同一资源,从而引发竞争。 解决方案:互斥锁(Mutex)与抢占控制 互斥锁:互斥锁与信号量类似,但它更专注于保护共享资源的互斥访问。使用struct mutex定义互斥锁,mutex_init初始化,mutex_lock获取锁,mutex_unlock释放锁。 抢占控制:在某些情况下,可以通过preempt_disable函数禁止内核抢占,在访问完共享资源后,使用preempt_enable函数恢复内核抢占。不过,过度禁止内核抢占会影响系统的响应性,应谨慎使用。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#define DEVICE_NAME "simple_char_dev"
#define BUFFER_SIZE 1024
struct simple_dev_t {
struct cdev cdev;
dev_t dev_num;
struct class *class;
struct device *device;
char buffer[BUFFER_SIZE]; // 数据缓冲区
struct mutex mutex; // 互斥锁
int count; // 缓冲区数据计数
} ;
static struct simple_dev_t simple_dev;
static int my_char_open(struct inode *inode, struct file *filp) {
struct simple_dev_t *dev = container_of(inode->i_cdev, struct simple_dev_t, cdev);
filp->private_data = dev;
return 0;
}
// 读操作
static ssize_t mutex_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
struct simple_dev_t *dev = filp->private_data;
ssize_t ret = 0;
// 获取互斥锁
if (mutex_lock_interruptible(&dev->mutex))
return -ERESTARTSYS;
// 临界区:访问共享资源
if (dev->count > 0) {
count = min(count, (size_t)dev->count);
if (copy_to_user(buf, dev->buffer, count)) {
ret = -EFAULT;
} else {
ret = count;
// 移动剩余数据
memmove(dev->buffer, dev->buffer + count, dev->count - count);
dev->count -= count;
}
}
// 释放互斥锁
mutex_unlock(&dev->mutex);
return ret;
}
// 写操作
static ssize_t mutex_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
struct simple_dev_t *dev = filp->private_data;
ssize_t ret = 0;
// 获取互斥锁
if (mutex_lock_interruptible(&dev->mutex))
return -ERESTARTSYS;
// 临界区:访问共享资源
if (dev->count + count > BUFFER_SIZE) {
count = BUFFER_SIZE - dev->count;
}
if (count > 0) {
if (copy_from_user(dev->buffer + dev->count, buf, count)) {
ret = -EFAULT;
} else {
ret = count;
dev->count += count;
}
}
// 释放互斥锁
mutex_unlock(&dev->mutex);
return ret;
}
static const struct file_operations my_char_fops = {
.owner = THIS_MODULE,
.open = my_char_open,
.read = mutex_read,
.write = mutex_write,
};
static int __init my_char_driver_init(void) {
int result;
// 动态分配设备号
result = alloc_chrdev_region(&simple_dev.dev_num, 0, 1, DEVICE_NAME);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to allocate major number\n");
return result;
}
printk(KERN_INFO "my_char_driver_init Allocated major number: %d\n", MAJOR(simple_dev.dev_num));
// 初始化互斥锁
mutex_init(&simple_dev.mutex);
// 初始化cdev结构
cdev_init(&simple_dev.cdev, &my_char_fops);
simple_dev.cdev.owner = THIS_MODULE;
// 添加字符设备
result = cdev_add(&simple_dev.cdev, simple_dev.dev_num, 1);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to add cdev\n");
unregister_chrdev_region(simple_dev.dev_num, 1);
return result;
}
// 创建类
simple_dev.class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(simple_dev.class)) {
printk(KERN_ERR "my_char_driver_init Failed to create class\n");
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.class);
}
// 创建设备节点
simple_dev.device = device_create(simple_dev.class, NULL, simple_dev.dev_num, NULL, DEVICE_NAME);
if (IS_ERR(simple_dev.device)) {
printk(KERN_ERR "my_char_driver_init Failed to create device\n");
class_destroy(simple_dev.class);
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.device);
}
return 0;
}
static void __exit my_char_driver_exit(void) {
// 1. 销毁设备节点
if (simple_dev.device)
device_destroy(simple_dev.class, simple_dev.dev_num);
// 2. 销毁设备类
if (simple_dev.class)
class_destroy(simple_dev.class);
// 3. 注销字符设备
cdev_del(&simple_dev.cdev);
// 4. 释放设备号
unregister_chrdev_region(simple_dev.dev_num, 1);
printk(KERN_INFO "my_char_driver_exit\n");
}
module_init(my_char_driver_init);
module_exit(my_char_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple driver using concurrent and competition");
MODULE_AUTHOR("cmy");
3.4 原子操作
原理 原子操作是指不会被系统调度机制打断的操作,其核心特点在于操作的不可分割性,即从开始执行到结束的整个过程中,不会受到其他执行单元的干扰。在 Linux 内核中,原子操作基于底层硬件指令实现,确保对共享资源的访问和修改在单条指令周期内完成,从而避免了并发访问导致的竞争问题。对于简单的变量增减、位操作等场景,原子操作是一种高效且简洁的解决方案。 应用场景 原子操作特别适用于对简单数据类型(如atomic_t类型的整数)进行操作的场景。例如,在统计设备的访问次数、控制设备的开关状态等场景中,原子操作可以确保数据的准确性和一致性。当多个执行单元需要对设备的访问计数进行累加时,使用原子操作可以避免计数错误。 代码示例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#define DEVICE_NAME "simple_char_dev"
#define BUFFER_SIZE 1024
struct simple_dev_t {
struct cdev cdev;
dev_t dev_num;
struct class *class;
struct device *device;
atomic_t counter; // 原子计数器
atomic_t flag; // 原子标志位
} ;
static struct simple_dev_t simple_dev;
static int my_char_open(struct inode *inode, struct file *filp) {
struct simple_dev_t *dev = container_of(inode->i_cdev, struct simple_dev_t, cdev);
filp->private_data = dev;
return 0;
}
// 读操作:获取计数器值
static ssize_t my_atomic_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
struct simple_dev_t *dev = filp->private_data;
int value;
char buffer[32];
// 读取原子变量
value = atomic_read(&dev->counter);
snprintf(buffer, sizeof(buffer), "%d\n", value);
// 复制到用户空间
if (count < strlen(buffer))
return -EINVAL;
if (copy_to_user(buf, buffer, strlen(buffer)))
return -EFAULT;
return strlen(buffer);
}
// 写操作:增加计数器或设置标志
static ssize_t my_atomic_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
struct simple_dev_t *dev = filp->private_data;
char buffer[32];
// 复制到内核空间
if (count >= sizeof(buffer))
return -EINVAL;
memset(buffer, 0, sizeof(buffer));
if (copy_from_user(buffer, buf, count))
return -EFAULT;
// 根据输入执行不同操作
if (strncmp(buffer, "inc", 3) == 0) {
// 原子递增
atomic_inc(&dev->counter);
} else if (strncmp(buffer, "dec", 3) == 0) {
// 原子递减
atomic_dec(&dev->counter);
} else if (strncmp(buffer, "set", 3) == 0) {
// 设置标志
atomic_set(&dev->flag, 1);
} else if (strncmp(buffer, "clear", 5) == 0) {
// 清除标志
atomic_set(&dev->flag, 0);
} else {
return -EINVAL;
}
return count;
}
static const struct file_operations my_char_fops = {
.owner = THIS_MODULE,
.open = my_char_open,
.read = my_atomic_read,
.write = my_atomic_write,
};
static int __init my_char_driver_init(void) {
int result;
// 动态分配设备号
result = alloc_chrdev_region(&simple_dev.dev_num, 0, 1, DEVICE_NAME);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to allocate major number\n");
return result;
}
printk(KERN_INFO "my_char_driver_init Allocated major number: %d\n", MAJOR(simple_dev.dev_num));
// 初始化原子变量
atomic_set(&simple_dev.counter, 0);
atomic_set(&simple_dev.flag, 0);
// 初始化cdev结构
cdev_init(&simple_dev.cdev, &my_char_fops);
simple_dev.cdev.owner = THIS_MODULE;
// 添加字符设备
result = cdev_add(&simple_dev.cdev, simple_dev.dev_num, 1);
if (result < 0) {
printk(KERN_ERR "my_char_driver_init Failed to add cdev\n");
unregister_chrdev_region(simple_dev.dev_num, 1);
return result;
}
// 创建类
simple_dev.class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(simple_dev.class)) {
printk(KERN_ERR "my_char_driver_init Failed to create class\n");
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.class);
}
// 创建设备节点
simple_dev.device = device_create(simple_dev.class, NULL, simple_dev.dev_num, NULL, DEVICE_NAME);
if (IS_ERR(simple_dev.device)) {
printk(KERN_ERR "my_char_driver_init Failed to create device\n");
class_destroy(simple_dev.class);
cdev_del(&simple_dev.cdev);
unregister_chrdev_region(simple_dev.dev_num, 1);
return PTR_ERR(simple_dev.device);
}
return 0;
}
static void __exit my_char_driver_exit(void) {
// 1. 销毁设备节点
if (simple_dev.device)
device_destroy(simple_dev.class, simple_dev.dev_num);
// 2. 销毁设备类
if (simple_dev.class)
class_destroy(simple_dev.class);
// 3. 注销字符设备
cdev_del(&simple_dev.cdev);
// 4. 释放设备号
unregister_chrdev_region(simple_dev.dev_num, 1);
printk(KERN_INFO "my_char_driver_exit\n");
}
module_init(my_char_driver_init);
module_exit(my_char_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple driver using concurrent and competition");
MODULE_AUTHOR("cmy");
// 线程函数:执行原子操作
void* thread_function(void* arg) {
int fd = *(int*)arg;
char buffer[32];
int i;
// 递增计数器
for (i = 0; i < 100000; i++) {
write(fd, "inc", 3);
}
// 读取当前值
memset(buffer, 0, sizeof(buffer));
read(fd, buffer, sizeof(buffer));
LOGD("Thread read counter: %s", buffer);
return NULL;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_testConcurrency2(JNIEnv *env, jobject thiz) {
int fd;
pthread_t threads[10];
int i, result;
char buffer[32];
// 打开设备文件
fd = open("/dev/simple_char_dev", O_RDWR);
if (fd < 0) {
LOGD("Failed to open device");
return -1;
}
// 初始化计数器
write(fd, "clear", 5);
// 创建多个线程
for (i = 0; i < 10; i++) {
result = pthread_create(&threads[i], NULL, thread_function, &fd);
if (result != 0) {
LOGD("Failed to create thread");
close(fd);
return -1;
}
}
// 等待所有线程完成
for (i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
// 读取最终结果
memset(buffer, 0, sizeof(buffer));
read(fd, buffer, sizeof(buffer));
LOGD("Final counter value: %s", buffer);
// 关闭设备
close(fd);
return 0;
}
开启10个线程循环写入100000个数据,可以看到最终结果为1000000。
在上述代码中,使用atomic_inc函数原子性地增加访问计数,使用atomic_dec函数原子性地减少访问计数。通过这种方式,即使在多进程并发访问设备的情况下,也能保证访问计数的准确性。