linux设备驱动第五章(并发和竞争)读书笔记

上传人:wm****3 文档编号:43097741 上传时间:2018-06-04 格式:DOC 页数:9 大小:19.08KB
返回 下载 相关 举报
linux设备驱动第五章(并发和竞争)读书笔记_第1页
第1页 / 共9页
linux设备驱动第五章(并发和竞争)读书笔记_第2页
第2页 / 共9页
linux设备驱动第五章(并发和竞争)读书笔记_第3页
第3页 / 共9页
linux设备驱动第五章(并发和竞争)读书笔记_第4页
第4页 / 共9页
linux设备驱动第五章(并发和竞争)读书笔记_第5页
第5页 / 共9页
点击查看更多>>
资源描述

《linux设备驱动第五章(并发和竞争)读书笔记》由会员分享,可在线阅读,更多相关《linux设备驱动第五章(并发和竞争)读书笔记(9页珍藏版)》请在金锄头文库上搜索。

1、LinuxLinux 设备驱动第五章设备驱动第五章( (并发和竞争并发和竞争) )读书笔记读书笔记第 5 章 并发和竞争参考文章:http:/ 旗标和互斥体1)旗标的核心是一个单个整型值。结合一对函数来实现,也称“PV 操作” 。一个想进入临界区的进程在相关旗标上调用 P,如果旗标值大于0,这个值递减 1,并且进程继续。如果旗标小于或者等于 0,说明该旗标已经被其他进程占用,此时进程必须等待知道旗标被释放。解锁旗标,也就是释放旗标,通过调用 V 操作来完成,V 操作递增旗标的值,并且可以唤醒等待该旗标的进程。2)互斥体 作用是:互斥,阻止多个进程同时在同一临界区内运行。互斥体也就是值初始化为

2、1 的旗标。因为:初始化为 1,当第一个进程 P 操作时,10,所以进程得以运行,同时旗标递减 1,值为 0,此时第 2 个进程想获得该旗标用来访问临界区,却发现旗标值为 0,因此进程 2 不能运行一直到进程 1 释放该旗标。5.3.1 LINUX 旗标实现1)要使用旗标,必须包含.相关的类型是 struct semaphore;2)旗标的实现:创建一个旗标, 接着使用 sema_init 来设定它例如:struct semaphore *sem;/定义旗标sema_init(sem,val);/初始化旗标 val 为初始化旗标的值。3)通常,旗标是以互斥锁的模式使用。因此内核提供了一系列的宏

3、来初始化。DECLARE_MUTEX(name); /初始化一个互斥锁,值为 1DECLARE_MUTEX_LOCKED(name);/初始化一个互斥锁,值为 0 就是:旗标初始化后就不可用。任何要使用该旗标的进程都要先解锁它再使用。当我们需要在运行时间初始化(也就是动态创建)我们使用:void init_MUTEX(struct semaphore *sem);/初始化一个互斥锁,值为 1void init_MUTEX_LOCKED(struct semaphore *sem);初始化一个互斥锁,值为 0例如:我们要创建一个初始化为 0 的互斥锁:struct semaphore *sem;

4、/定义旗标ECLARE_MUTEX_LOCKED(sem);/初始化旗标值为 0。以上是初始化旗标,初始化后我们当然就要使用旗标了,不然创建它有何意义?4)旗标的获取与释放(也可以成为互斥锁,因为 LINUX 中基本上所有的旗标都用来互斥)获取旗标也称 P 操作,也叫 down。down 字面意思就是下降的意思。相对于旗标来说就是减 1。p 函数如下:void down(struct semaphore *sem); /*不推荐使用,会建立不可杀进程*/int down_interruptible(struct semaphore *sem);/*推荐使用,使用 down_interrupti

5、ble 需要格外小心,若操作被中断,该函数会返回非零值,而调用这不会拥有该信号量。对down_interruptible 的正确使用需要始终检查返回值,并做出相应的响应。*/int down_trylock(struct semaphore *sem);/*带有“_trylock”的永不休眠,若信号量在调用是不可获得,会返回非零值。*/我们一般使用 down_interruptible 函数就好。书中推荐滴。一旦获取了旗标,则获取旗标的进程就可以存取该旗标保护的临界区了。当使用完后,要怎么办?当然是丢弃对临界区的控制,也就是释放旗标。释放旗标也称 V 操作,也叫 UP,up 字面意思就是上升的

6、意思。相对于旗标来说就是加 1。(down 和 up 形象的描述了对旗标的操作)。V 函数如下:void up(struct semaphore *sem); 一旦调用 UP,进程就不在拥有旗标了。5)使用旗标的容易犯错误获得旗标的进程使用对 UP 的调用来释放旗标,但不能多次调用 UP,就是说:一个 down 对于一个 UP。在持有旗标遇到错误时,我们必须在 return(错误状态)是调用 UP 释放旗标,不然临界区一直被该进程占有,但也许该进程已经被 kill 了,而其他要使用临界区的进程就会因一直得不到临界区而一直挂起。5.3.2. 在 scull 中使用旗标正确使用加锁原语的关键是严密

7、地指定要保护哪个资源并且确认每个对这些资源的存取都使用了正确的加锁方法.首先看看一个 scull 结构体:struct scull_dev struct scull_qset *data; /* Pointer to first quantum set */int quantum; /* the current quantum size */int qset; /* the current array size */unsigned long size; /* amount of data stored here */unsigned int access_key; /* used by sc

8、ulluid and scullpriv */struct semaphore sem; /* mutual exclusion semaphore */struct cdev cdev; /* Char device structure */;struct semaphore sem 是我们的旗标,而这个结构体是我们要保护的对象。旗标在使用前必须初始化. scull 在加载时进行这个初始化, 在这个循环中:for (i = 0; i sem)return -ERESTARTSYS;注意对 down_interruptible 返回值的检查; 如果它返回非零, 操作被打断了. 在这个情况下通常

9、要做的是返回 -ERESTARTSYS. 看到这个返回值后, 内核的高层要么从头重启这个调用要么返回这个错误给用户. 如果你返回 -ERESTARTSYS, 你必须首先恢复任何用户可见的已经做了的改变, 以保证当重试系统调用时正确的事情发生. 如果你不能以这个方式恢复, 你应当替之返回 -EINTR.当我们使用完临界区后,wirte 函数一定要释放旗标,不管它的操作是否成功,或者当我们使用 write 函数时发生错误,比如:kmalloc 分配内存失败,copy_form_uesr 从用户空间 copy 数据失败。记住:一定要释放旗标。代码:out:up(return retval;在遇到不同

10、的错误时,使用 goto 语句来跳转到 out.另:必须确保在不拥有信号量的时候不会访问 scull_dev 结构体。5.3.3 读者/写者旗标旗标为所有调用者进行互斥,但有时我们可以想想:旗标的作用是什么?一般情况下,我们用在对临界区的存取上,因为如果没有旗标,可能进程 1 刚修改完一个变量,进程 2 又修改了同一个变量,导致后来的数据把前面的数据给覆盖了。如同 5.1. scull 中的缺陷中所举得例子一样。 但是但我们只读的时候,我们也只允许一个进程读,这样做就显的没有效率。因此,有了读者/写者旗标。读者/写者旗标实现的结果:我们可以并发的读,但是只能互斥的写。提高了效率。只读的任务可以

11、并行进行它们的工作而不必等待其他读者退出临界区.1)使用 rwsem 要包含. 2)初始化一个 rwsem.void init_rwsem(struct rw_semaphore *sem); 3)函数如下:需要只读存取的代码的接口是:void down_read(struct rw_semaphore *sem);int down_read_trylock(struct rw_semaphore *sem);void up_read(struct rw_semaphore *sem);对 down_read 的调用提供了对被保护资源的只读存取, 与其他读者可能地并发地存取. 注意 down_

12、read 可能将调用进程置为不可中断的睡眠. down_read_trylock 如果读存取是不可用时不会等待; 如果被准予存取它返回非零, 否则是 0. 注意 down_read_trylock 的惯例不同于大部分的内核函数, 返回值 0 指示成功. 一个使用 down_read 获取的 rwsem 必须最终使用 up_read 释放.读者的接口类似:void down_write(struct rw_semaphore *sem);int down_write_trylock(struct rw_semaphore *sem);void up_write(struct rw_semapho

13、re *sem);void downgrade_write(struct rw_semaphore *sem);down_write, down_write_trylock, 和 up_write 全部就像它们的读者对应部分, 除了, 当然, 它们提供写存取. 如果你处于这样的情况, 需要一个写者锁来做一个快速改变, 接着一个长时间的只读存取, 你可以使用 downgrade_write 在一旦你已完成改变后允许其他读者进入.4)rwsem 在驱动中的使用相对较少, 但是有时它们有用。rwsem 允许多个读来持有旗标,而写着有优先权,当一个写者试图获得旗标,就不允许读者进入直到写者完成工作。但

14、是它可以导致读者饥饿,因为如何大量的写者来竞争旗标,因为写者有优先权,所以读者被长时间的拒绝. 因此:rwsem 最好用的很少写并且写占用很短时间的情况下。5.4. Completions 机制上面讲述了旗标的互斥使用,但是旗标还有一种方式就是:同步。同步的含义就是:顺序执行,只有事件 A 运行结束,事件 B 才开始运行。这叫同步。例如:struct semaphore sem; init_MUTEX_LOCKED(/初始化旗标为 0 不可用start_external_task(/在该任务中调用 UP。down(/获得旗标但是事实证明:这种情况旗标不是最好的工具,因此Completions

15、机制产生了。1)要使用 Completions 机制,必须包含.2)创建 completions 的 2 种方式:1:DECLARE_COMPLETION(my_completion); 2:struct completion my_completion;/动态创建/* . */init_completion(3)函数:void wait_for_completion(struct completion *c); /等待 completion该函数是一个不可打断的等待,如果被调用但没有进程来完成条件,则会是一个不可杀死的进程,因此必须有函数来完成条件,也就是发出 completion 事件。v

16、oid complete(struct completion *c);/只唤醒一个等待的进程void complete_all(struct completion *c);/唤醒所有等待的进程以上 2 个函数被用来发出 completion 事件。如果我们使用了 complete_all()函数,那么我们必须再重新使用它之前调用 INIT_COMPLETION(struct completion c)来快速初始化 completion 结构。4)实例程序DECLARE_COMPLETION(comp);/初始化 completionssize_t complete_read (struct file *filp, char _user *buf, size_t count, loff_t *pos)printk(KERN_DEBUG “process %i (%s) going to sleepn“,current-pid, cur

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 生活休闲 > 社会民生

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号