版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、实验五:设备驱动中的并发控制1.并发与竞态并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race con ditio ns )。例如,对于globalmem设备,假设一个执行单元A对其写入3000个字符a”,而另一个执行单元B对其写入4000个字符b”,第三个执行单元 C读取globalmem的所有字符。如果执行 单元A、B的写操作如图1所示的顺序执行,执行单元C的读操作不会有问题。但是,如果执行单元A、B如图2所示的顺序执行,而执行单元C又“不合时宜”地读,则会读出3000个“b”
2、。忱行单元A执行单元H掛行单元匚cop _troni _uscr(dcv-mem+pF buft count)dev- cumBnt Jemp+eountLIHKHMHIM 1 1 Hi 1 MJ 1read_lobalncm()copy -Inxii _uscr(dcv-mcm+pt but, count)Jcv- c ufrcnl _ n=p+cou nl图7.1出钱执打单应的顾中执口re men+p, buf, count) Vdev- current _len=p+eountcopj_from_u5erTnen+p,huicountrm m e mrt r L ri-rr一ft t k
3、rLFL”Lrir-ivani-ri;F-imiv niwi!t r t rn iwierLmiir-wins-ir-wircad_ globilniciTidev_kn=picuu|图工2胖发执行单兀的交错执疔比图2更复杂、更混乱的并发大量地存在于设备驱动中,只要并发的多个执行单元存在对 共享资源的访问,竞态就可能发生。在Linux内核中,主要的竞态发生于如下几种情况。1 .对称多处理器(SMP )的多个CPUSMP是一种紧耦合、共享存储的系统模型,它的特点是多个CPU使用共同的系统总线,因此可访问共同的外设和储存器。2.单CPU内进程与抢占它的进程Linux2.6内核支持抢占调度,一个进程
4、在内核执行的时候可能被另一高优先级进程打断, 进程与抢占它的进程访问共享资源的情况类似于SMP的多个CPU。3 .中断(硬中断、软中断、Tasklet、底半部)与进程之间中断可以打断正在执行的进程,如果中断处理程序访问进程正在访问的资源,则竞态也会 发生。此外,中断也有可能被新的更高优先级的中断打断,因此,多个中断之间本身也可能引起 并发而导致竞态。上述并发的发生情况除了SMP是真正的并行以外,其他的都是“宏观并行、微观串行”的,但其引发的实质问题和 SMP相似。解决竞态问题的途径是保证对共享资源的互斥访问,所谓互斥访问是指一个执行单元在访 问共享资源的时候,其他的执行单元被禁止访问。访问共享
5、资源的代码区域称为临界区( critical sections),临界区需要以某种互斥机制加以 保护。中断屏蔽、原子操作、自旋锁和信号量等是Linux设备驱动中可采用的互斥途径,下节将讲解信号量是如何实现对资源的互斥访问的。2.信号量信号量(semaphore)是用于保护临界区的一种常用方法。只有得到信号量的进程才能执行 临界区代码,当获取不到信号量时,进程进入休眠等待状态。Linux系统中与信号量相关的操作主要有如下4种。1 .定义信号量下列代码定义名称为 sem的信- 号量。struct semaphore sem;其中结构体semaphore的定义为:struct semaphoresp
6、inlock_tlock;unsigned intcount;struct list headwait list;2 .初始化信号量void sema_init (struct semaphore *sem, int val);该函数初始化信号量,并设置信号量sem的值为val。尽管信号量可以被初始化为大于1的值从而成为一个计数信号量,但是它通常不被这样使用。void init_MUTEX(struct semaphore *sem);该函数用于初始化一个用于互斥的信号量,它把信号量sem的值设置为1,等同于sema_init(struct semaphore *sem, 1)。void in
7、it_MUTEX_LOCKED (struct semaphore *sem);该函数也用于初始化一个信号量,但它把信号量sem的值设置为0,等同于sema_init(structsemaphore *sem, 0)。此外,下面两个宏是定义并初始化信号量的“快捷方式”。DECLARE_MUTEX(name) DECLARE_MUTEX_LOCKED(name)前者定义一个名为name的信号量并初始化为1,后者定义一个名为name的信号量并初始 化为0。3 .获得信号量void down(struct semaphore * sem);该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下
8、文使用。int down_interruptible(struct semaphore * sem);该函数功能与down()类似,不同之处为,因为 down()而进入睡眠状态的进程不能被信号打 断,而因为down_interruptible()而进入睡眠状态的进程能被信号打断,信号也会导致该函数返回,这时候函数的返回值非 0。int down_trylock(struct semaphore * sem);该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0值。它不会导致调用者睡眠,可以在中断上下文使用。在使用down_interruptible()获取信号
9、量时,对返回值一般会进行检查,如果非0,通常立即返回-ERESTARTSYS,如:if (down_i nterruptible(&sem)return - ERESTARTSYS;4 释放信号量void up(struct semaphore * sem);该函数释放信号量sem,唤醒等待者。信号量一般这样被使用,如下所示:/定义信号量DECLARE_MUTEX(mount_sem);down(&mount_sem);获取信号量,保护临界区critical section / 临界区up(&mount_sem); 释放信号量以下代码给出了使用信号量实现设备只能被一个进程打开的例子。stati
10、c DECLARE_MUTEX(xxx_lock); 定义互斥锁static int xxx_open(struct inode *inode, struct file *filp)if (down_trylock (& xxx_lock) / 获得打开锁return - EBUSY; / 设备忙return 0; /* 成功 */static int xxx_release(struct inode *inode, struct file *filp)up(&xxx_lock); / 释放打开锁return 0;3增加并发控制后的globalmem马驱动在globalmem()的读写函数中,
11、由于要调用copy_from_user()、copy_to_user()这些可能导致阻 塞的函数,因此使用信号量。驱动工程师习惯将某设备所使用的自旋锁、信号量等辅助手段也放在设备结构中,因此, 可如代码清单1那样修改globalmem_dev结构体的定义,并在模块初始化函数中初始化这个信号 量,如代码清单2所示。1. 增加并发控制后的globalmem设备结构体struct globalmem_devstruct cdev cdev; /*cdev 结构体 */unsigned char memGLOBALMEM_SIZE; /* 全局内存 */struct semaphore sem; /*
12、并发控制用的信号量 */;2. 增加并发控制后的globalmem设备驱动模块加载函数int globalmem _i nit(void)int result;dev_t devno = MKDEV(globalmem_major, 0);/*申请设备号*/if (globalmem_major)result = register_chrdev_region(devno, 1, globalmem);else /*动态申请设备号*/result = alloc_chrdev_regio n(&devno, 0, 1, globalmem); globalmem_major = MAJOR(de
13、vno);if (result sem);return 0;fail_malloc: unregister_chrdev_region(devno, 1);return result;在访问globalmem_dev中的共享资源时,需先获取这个信号量,访问完成后,随即释放这个 信号量。驱动中新的globalmem读、写操作如代码清单3所示。static ssize_t globalmem_read(struct file *filp, char _user *buf, size_t size, loff_t *ppos)unsigned long p = *ppos;unsigned int
14、count = size;int ret = 0;struct globalmem_dev *dev = filp-private_data; /*获得设备结构体指针 */*分析和获取有效的写长度*/if (p = GLOBALMEM_SIZE)return count ?- ENXIO: 0;if (count GLOBALMEM_SIZE - p)count = GLOBALMEM_SIZE - p;if (down_interruptible (&dev-sem)return - ERESTARTSYS;/*内核空间-用户空间*/if (copy_to_user(buf, (void*)
15、(dev-mem + p), count)ret =- EFAULT;else*ppos += count;ret = count;printk(KERNNFO read %d bytes(s) from %dn, count, p);up(&dev-sem); / 释放信号量return ret;static ssize_t globalmem_write(struct file *filp, const char _user *buf, size_t size, loff_t *ppos)unsigned long p = *ppos;unsigned int count = size;i
16、nt ret = 0;struct globalmem_dev *dev = filp-private_data; /*获得设备结构体指针 */*分析和获取有效的写长度*/if (p = GLOBALMEM_SIZE)return count ?- ENXIO: 0;if (count GLOBALMEM_SIZE - p)count = GLOBALMEM_SIZE - p;if (down_interruptible (&dev-sem)/获得信号量return - ERESTARTSYS;/*用户空间- 内核空间*/if (copy_from_user(dev-mem + p, buf,
17、 count)ret =- EFAULT;else*ppos += count;ret = count;printk(KERNl_INFO written %d bytes(s) from %dn, count, p);up(&dev-sem); / 释放信号量return ret;代码第1619和第5356行用于获取信号量,如果 down_interruptible()返回值非0,则意味 着其在获得信号量之前已被打断,这时写函数返回-ERESTARTSYS。代码第33和第67行用于在对临界资源访问结束后释放信号量。除了 globalmem的读写操作之外,如果在读写的同时,另一执行单元执行ME
18、M_CLEAR 10控制命令,也会导致全局内存的混乱,因此,globalmem_ioctl()函数也需被重写,如代码清单4所示。/* ioctl设备控制函数*/static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsignedint cmd, unsigned long arg)struct globalmem_dev *dev = filp-private_data; /*获得设备结构体指针 */switch (cmd)case MEM_CLEAR:if (down_interruptible(&dev-s
19、em)return - ERESTARTSYS;memset(dev-mem, 0, GLOBALMEM_SIZE);up(&dev-sem); / 释放信号量printk(KERNNFO globalmem is set to zeron); break;default:return - EINVAL;return 0;实验1在实验四中同时打开两个终端,分别执行test程序,观察实验现象,注意命令的执行顺序(以红色数字标注)。可发现执行的结果跟预期不一致。File Edit View Tfenminal Help5glabalmenk is goodbyeglDbalmem is goodb
20、yebaokbook-desktop: -/hellFile Edit View Tfermihal Helpbookbcok-desktop/heUo sudo , /test Please input the string written to giobaLmem hello3The str i? helloThe ret is 5eThe gidbatmen goodbyebookbmk- des ktop: hel lot str 15 hello ret i烷 5bookbtjtjk-desktopsudo ./test Please input the string written
21、 to globalbieni goodbyeTheThestr is goodbye ret is 7The ”bookfbook- desktop: -/hellos 2.在/dev/book/globalmem 中新建文件 globalmen.c。代码如下:#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #defin
22、e GLOBALMEM_SIZE 0x1000/*全局内存最大 4K 字节 */#define MEM_CLEAR 0x1/*清 0 全局内存 */#define GLOBALMEM_MAJOR 150/*预设的 globalmem 的主设备号 */static int globalmem_major = GLOBALMEM_MAJOR;/*globalmem设备结构体*/struct globalmem_devstruct cdev cdev; /*cdev 结构体 */unsigned char memGLOBALMEM_SIZE; /* 全局内存 */struct semaphore s
23、em1; /*并发控制用的信号量*/struct semaphore sem2; /*并发控制用的信号量*/;struct globalmem_dev *globalmem_devp;int globalmem_open(struct inode *inode, struct file *filp)filp-private_data = globalmem_devp;struct globalmem_dev *dev = filp-private_data;if (downnterruptible(&dev-sem2) down_trylock(&dev-sem2)return - EBUSY
24、;return 0;int globalmem_release(struct inode *inode, struct file *filp)struct globalmem_dev *dev = filp-private_data;up(&dev-sem2);return 0;static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsignedint cmd, unsigned long arg)struct globalmem_dev *dev = filp-private_data;switch (cmd
25、)case MEM_CLEAR:if (down_interruptible(&dev-sem1)return - ERESTARTSYS;memset(dev-mem, 0, GLOBALMEM_SIZE);up(&dev-sem1);printk(KERNNFO globalmem is set to zeron);break;default:return - EINVAL;return 0;static ssize_t globalmem_read(struct file *filp, char _user *buf, size_t size, loff_t *ppos)unsigned
26、 long p = *ppos;unsigned int count = size;int ret = 0;struct globalmem_dev *dev = filp-private_data;if (p = GLOBALMEM_SIZE)return count ?- ENXIO: 0;if (count GLOBALMEM_SIZE - p)count = GLOBALMEM_SIZE - p;if (down_interruptible (&dev-sem1)return - ERESTARTSYS;if (copy_to_user(buf, (void*)(dev-mem + p
27、), count)ret =- EFAULT;else*ppos += count;ret = count;printk(KERNNFO read %d bytes(s) from %dn, count, p);up(&dev-sem1);return ret;static ssize_t globalmem_write(struct file *filp, const char _user *buf, size_t size, loff_t *ppos) unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct
28、globalmem_dev *dev = filp-private_data;if (p = GLOBALMEM_SIZE)return count ?- ENXIO: 0;if (count GLOBALMEM_SIZE - p)count = GLOBALMEM_SIZE - p;if (down_interruptible (&dev-sem1)return - ERESTARTSYS;if (copy_from_user(dev-mem + p, buf, count)ret =- EFAULT;else*ppos += count;ret = count;printk(KERNl_I
29、NFO written %d bytes(s) from %dn, count, p);up(&dev-sem1);return ret;static const struct file_operations globalmem_fops =.owner = THIS_MODULE,.read = globalmem_read,.write = globalmem_write,.ioctl = globalmem_ioctl,.open = globalmem_open,.release = globalmem_release,;static void globalmem_setup_cdev
30、(struct globalmem_dev *dev, int index) int err, devno = MKDEV(globalmem_major, index);cdev_init (&dev-cdev, &globalmem_fops);dev-cdev.owner = THIS_MODULE;dev-cdev.ops = &globalmem_fops;err = cdev_add(&dev-cdev, devno, 1);if (err)printk(KERN_NOTICE Error %d adding LED%d, err, index);int globalmem _i
31、nit(void)int result;dev_t devno = MKDEV(globalmem_major, 0);if (globalmem_major)result = register_chrdev_region(devno, 1, globalmem);elseresult = alloc_chrdev_regio n(&devno, 0, 1, globalmem); globalmem_major = MAJOR(devno);if (result sem1);init_MUTEX(&globalmem_devp-sem2);return 0;fail_malloc: unregister_chrdev_region(devno, 1);return result;void globalmem_exit(void)cdev_del(&globalmem_devp-cdev);kfree(globalmem_devp); unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);MODU
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年智能浴室防跌倒系统项目公司成立分析报告
- 2026年江西财经职业学院单招职业技能笔试模拟试题带答案解析
- 2026年云南林业职业技术学院高职单招职业适应性考试参考题库带答案解析
- 2026年江西工商职业技术学院高职单招职业适应性考试模拟试题带答案解析
- 2026年抬头显示器项目营销方案
- 2026年重庆艺术工程职业学院高职单招职业适应性考试备考题库带答案解析
- 2026年民办四川天一学院高职单招职业适应性考试备考题库带答案解析
- 2026年江苏航空职业技术学院高职单招职业适应性考试备考试题带答案解析
- 2026年湖南有色金属职业技术学院单招职业技能笔试备考题库带答案解析
- 2026年上海应用技术大学单招职业技能考试备考题库带答案解析
- DB13(J)T 273-2018 被动式超低能耗居住建筑节能设计标准
- 2025年湖北省公务员申论真题试卷
- 穿越机基础课件
- 谷歌员工关系管理案例
- 高等学府零基预算管理体系深化策略研究
- 物流企业仓储安全操作规程与培训教材
- 黄体酮破裂课件
- 结算审计踏勘现场实施方案详细版
- 手机玻璃工厂年终总结报告
- 全国大学生职业规划大赛《信息与计算科学》专业生涯发展展示
- 急诊科护士年终总结汇报
评论
0/150
提交评论