深入浅出Linux设备驱动之并发控制
来源:互联网 发布:域名和公网ip绑定 编辑:程序博客网 时间:2024/06/08 01:10
深入浅出Linux设备驱动之并发控制
在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发"竞态",因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。
自旋锁与信号量"类似而不类",类似说的是它们功能上的相似性,"不类"指代它们在本质和实现机理上完全不一样,不属于一类。
自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁,"自旋"就是"在原地打转"。而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。这就是它们的"不类"。
但是,无论是信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,即在任何时刻最多只能有一个执行单元获得锁。这就是它们的"类似"。
鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,会只能在进程上下文使用。如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,如果对共享资源的访问时间非常短,自旋锁也是好的选择。但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。
与信号量相关的API主要有:
定义信号量
初始化信号量
该函数初始化信号量,并设置信号量sem的值为val
该函数用于初始化一个互斥锁,即它把信号量sem的值设置为1,等同于sema_init (struct semaphore*sem, 1);
该函数也用于初始化一个互斥锁,但它把信号量sem的值设置为0,等同于sema_init (struct semaphore*sem, 0);
获得信号量
该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用;
该函数功能与down类似,不同之处为,down不能被信号打断,但down_interruptible能被信号打断;
该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0值。它不会导致调用者睡眠,可以在中断上下文使用。
释放信号量
该函数释放信号量sem,唤醒等待者。
与自旋锁相关的API主要有:
定义自旋锁
初始化自旋锁
该宏用于动态初始化自旋锁lock
获得自旋锁
该宏用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,直到该自旋锁的保持者释放;
该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则立即返回假,实际上不再"在原地打转";
释放自旋锁
该宏释放自旋锁lock,它与spin_trylock或spin_lock配对使用;
下面进入对并发控制的实战。首先,在globalvar的驱动程序中,我们可以通过信号量来控制对intglobal_var的并发访问,下面给出源代码:
#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <asm/uaccess.h>#include <asm/semaphore.h> MODULE_LICENSE("GPL");#define MAJOR_NUM 254static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);struct file_operations globalvar_fops ={ read: globalvar_read, write: globalvar_write,};static int global_var = 0;static struct semaphore sem;static int __init globalvar_init(void){ int ret; ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops); if (ret) { printk("globalvar register failure"); } else { printk("globalvar register success"); init_MUTEX(&sem); } return ret;}static void __exit globalvar_exit(void){ int ret; ret = unregister_chrdev(MAJOR_NUM, "globalvar"); if (ret) { printk("globalvar unregister failure"); } else { printk("globalvar unregister success"); }}static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off){ //获得信号量 if (down_interruptible(&sem)) { return - ERESTARTSYS; } //将global_var从内核空间复制到用户空间 if (copy_to_user(buf, &global_var, sizeof(int))) { up(&sem); return - EFAULT; } //释放信号量 up(&sem); return sizeof(int);}ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off){ //获得信号量 if (down_interruptible(&sem)) { return - ERESTARTSYS; } //将用户空间的数据复制到内核空间的global_var if (copy_from_user(&global_var, buf, sizeof(int))) { up(&sem); return - EFAULT; } //释放信号量 up(&sem); return sizeof(int);}module_init(globalvar_init);module_exit(globalvar_exit);
接下来,我们给globalvar的驱动程序增加open()和release()函数,并在其中借助自旋锁来保护对全局变量intglobalvar_count(记录打开设备的进程数)的访问来实现设备只能被一个进程打开(必须确保globalvar_count最多只能为1):
#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <asm/uaccess.h>#include <asm/semaphore.h> MODULE_LICENSE("GPL");#define MAJOR_NUM 254static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);static int globalvar_open(struct inode *inode, struct file *filp);static int globalvar_release(struct inode *inode, struct file *filp);struct file_operations globalvar_fops ={ read: globalvar_read, write: globalvar_write, open: globalvar_open, release:globalvar_release,};static int global_var = 0;static int globalvar_count = 0;static struct semaphore sem;static spinlock_t spin = SPIN_LOCK_UNLOCKED;static int __init globalvar_init(void){ int ret; ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops); if (ret) { printk("globalvar register failure"); } else { printk("globalvar register success"); init_MUTEX(&sem); } return ret;}static void __exit globalvar_exit(void){ int ret; ret = unregister_chrdev(MAJOR_NUM, "globalvar"); if (ret) { printk("globalvar unregister failure"); } else { printk("globalvar unregister success"); }}static int globalvar_open(struct inode *inode, struct file *filp){ //获得自选锁 spin_lock(&spin); //临界资源访问 if (globalvar_count) { spin_unlock(&spin); return - EBUSY; } globalvar_count++; //释放自选锁 spin_unlock(&spin); return 0;}static int globalvar_release(struct inode *inode, struct file *filp){ globalvar_count--; return 0;}static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t*off){ if (down_interruptible(&sem)) { return - ERESTARTSYS; } if (copy_to_user(buf, &global_var, sizeof(int))) { up(&sem); return - EFAULT; } up(&sem); return sizeof(int);}static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len,loff_t *off){ if (down_interruptible(&sem)) { return - ERESTARTSYS; } if (copy_from_user(&global_var, buf, sizeof(int))) { up(&sem); return - EFAULT; } up(&sem); return sizeof(int);}module_init(globalvar_init);module_exit(globalvar_exit);
为了上述驱动程序的效果,我们启动两个进程分别打开/dev/globalvar。在两个终端中调用./globalvartest.o测试程序,当一个进程打开/dev/globalvar后,另外一个进程将打开失败,输出"deviceopen failure"
- 深入浅出Linux设备驱动之并发控制
- 深入浅出Linux设备驱动之并发控制
- 深入浅出Linux设备驱动之并发控制
- 深入浅出Linux设备驱动之并发控制
- 深入浅出Linux设备驱动之并发控制
- Linux设备驱动之并发控制(1)
- Linux设备驱动之并发控制
- linux设备驱动之并发控制
- Linux设备驱动之并发控制
- 深入浅出Linux设备驱动编程--设备驱动中的并发控制
- Linux设备驱动并发控制
- 深入浅出Linux设备驱动并发…
- 深入浅出LDD-4-设备的驱动的并发控制2
- Linux设备驱动中的并发控制
- Linux设备驱动中的并发控制
- Linux 设备驱动中的并发控制
- linux设备驱动中的并发控制
- Linux设备驱动中的并发控制
- CUDA和OpenCL
- Java程序员从笨鸟到菜鸟之(一百零三)java操作office和pdf文件(一)java读取word,excel和pdf文档内容
- .net网站发布-允许更新此预编译站点 及修改发布后内容
- ffmpeg内部分析
- android电源管理简要
- 深入浅出Linux设备驱动之并发控制
- S3C2440 VxWorks移植随笔--CAN总线测试小问题
- 逐个取出jsonArray中的每一项
- 《Getting Started with D3》中文翻译(第一章)
- 基于边缘重组的遗传算法求解TSP问题
- Spring基础xfire报:报 Error initializing XFireServlet.
- 关于文本内容比较
- linux fork函数与vfork函数
- Spring IoC容器在Web环境中的启动源码阅读