




已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
内核同步机制-信号量/互斥锁/读-写信号量 sema ,mutex ,rwsem 目录1 信号量 o 1.1 通用信号量 o 1.2 互斥锁 o 1.3 读/写信号量 信号量 通用信号量 用户类进程之间使用信号量(semaphore)进行同步,内核线程之间也使用了信号量。信号量与自旋锁类似,保护临界区代码。但信号量与自旋锁有一定的区别,信号量在无法得到资源时,内核线程处于睡眠阻塞状态,而自旋锁处于忙等待状态。因此,如果资源被占用时间很短时,使用自旋锁较好,因为它可节约调度时间。如果资源被占用的时间较长,使用信号量较好,因为可让CPU调度去做其它进程的工作。操作信号量的API函数说明如表6。表6 信号量API函数功能说明 函数定义 功能说明 sema_init(struct semaphore *sem, int val) 初始化信号量,将信号量计数器值设置val。 down(struct semaphore *sem) 获取信号量,不建议使用此函数。 down_interruptible(struct semaphore *sem) 可被中断地获取信号量,如果睡眠被信号中断,返回错误-EINTR。 down_killable (struct semaphore *sem) 可被杀死地获取信号量。如果睡眠被致命信号中断,返回错误-EINTR。 down_trylock(struct semaphore *sem) 尝试原子地获取信号量,如果成功获取,返回0,不能获取,返回1。 down_timeout(struct semaphore *sem, long jiffies) 在指定的时间jiffies内获取信号量,若超时未获取,返回错误-ETIME。 up(struct semaphore *sem)释放信号量sem。 样例:信号量的使用下面函数do_utimes利用信号量防止多个线程对文件系统节点inode同时进行访问。其列出如下(在fs/open.c中): long do_utimes(char _user * filename, struct timeval * times) struct inode * inode; down(&inode-i_sem); /获取信号量 error = notify_change(nd.dentry, &newattrs);/修改inode中值 up(&inode-i_sem); /释放信号量 下面说明信号量API函数。 (1)信号量结构semaphore信号量用结构semaphore描述,它在自旋锁的基础上改进而成,它包括一个自旋锁、信号量计数器和一个等待队列。用户程序只能调用信号量API函数,而不能直接访问信号量结构,其列出如下(在include/linux/semaphore.h中): struct semaphore spinlock_t lock; unsigned int count; struct list_head wait_list; (2)初始化函数sema_init函数sema_init初始化信号量,将信号量值初始化为n,其列出如下: static inline void sema_init(struct semaphore *sem, int val) static struct lock_class_key _key; *sem = (struct semaphore) _SEMAPHORE_INITIALIZER(*sem, val); /*初始化一个锁的实例,用于调试中获取信号量的调试信息*/ lockdep_init_map(&sem-lock.dep_map, semaphore-lock, &_key, 0); #define _SEMAPHORE_INITIALIZER(name, n) / / .lock = _SPIN_LOCK_UNLOCKED(name).lock), / /初始化自旋锁 .count = n, / /将信号量计数器赋值为n .wait_list = LIST_HEAD_INIT(name).wait_list), / /初始化等待队列 (3)可中断获取信号量函数down_interruptible 函数down_interruptible获取信号量,存放在参数sem中。它尝试获取信号量,如果其他线程被允许尝试获取此信号量,则将本线程睡眠等待。如果有一个信号中断睡眠,则它返回错误-EINTR。如果成功获取信号量,函数返回0。函数down_interruptible列出如下(在kernel/semaphore.c中): int down_interruptible(struct semaphore *sem) unsigned long flags; int result = 0; spin_lock_irqsave(&sem-lock, flags); /获取自旋锁,关闭中断,将状态寄存器值存放在flags /*如果信号量计数器值大于0,说明有多个空闲资源可访问,可以成功获取信号量了*/ if (likely(sem-count 0) /likely表示成功获取的概率大,通知编译器进行分支预测优化 sem-count-; else result = _down_interruptible(sem); /进入睡眠等待 spin_unlock_irqrestore(&sem-lock, flags); return result; static noinline int _sched _down_interruptible(struct semaphore *sem) return _down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); 函数_down_common进入睡眠等待,其列出如下: static inline int _sched _down_common(struct semaphore *sem, long state, long timeout) struct task_struct *task = current; struct semaphore_waiter waiter; list_add_tail(&waiter.list, &sem-wait_list); /加入到等待队列 waiter.task = task; waiter.up = 0; for (;) if (state = TASK_INTERRUPTIBLE & signal_pending(task) goto interrupted; if (state = TASK_KILLABLE & fatal_signal_pending(task) goto interrupted; if (timeout lock); timeout = schedule_timeout(timeout); /调度 spin_lock_irq(&sem-lock); if (waiter.up) return 0; timed_out: list_del(&waiter.list); return -ETIME; interrupted: list_del(&waiter.list); return -EINTR; (3)释放信号量函数up 函数up在没有其他线程等待使用信号量的情况下释放信号量,否则,唤醒其他等待线程。其列出如下: void up(struct semaphore *sem) unsigned long flags; spin_lock_irqsave(&sem-lock, flags); /*判断是否有线程等待在此信号量上,即判断等待队列是否为空*/ if (likely(list_empty(&sem-wait_list) /*没有线程等待此信号量,释放信号量,将信号量计数器加1,表示增加了1个空闲资源*/ sem-count+; else _up(sem); /*将本线程从等待队列删除,唤醒等待此信号量的其他线程*/ spin_unlock_irqrestore(&sem-lock, flags); static noinline void _sched _up(struct semaphore *sem) struct semaphore_waiter *waiter = list_first_entry(&sem-wait_list, struct semaphore_waiter, list); list_del(&waiter-list); /将本线程从等待队列删除 waiter-up = 1; wake_up_process(waiter-task); /唤醒等待此信号量的其他线程 互斥锁 信号量的初始值表示可以有多少个任务可同时访问的共享资源,如果初始值为1,表示只有1个任务可以访问,信号量变成互斥锁(Mutex)。可见互斥锁是信号量的特例。互斥锁(mutex)是在原子操作API的基础上实现的信号量行为。互斥锁不能进行递归锁定或解锁,能用于交互上下文,同一时间只能有一个任务持有互斥锁。互斥锁功能上基本上与信号量一样,互斥锁占用空间比信号量小,运行效率比信号量高。互斥锁的API函数功能说明如表1。 表1 互斥锁的API函数功能说明 API函数 功能说明 DEFINE_MUTEX(mutexname) 创建和初始化互斥锁。 void mutex_lock(struct mutex *lock);加锁。 void mutex_unlock(struct mutex *lock); 解锁。 int mutex_trylock(struct mutex *lock);尝试加锁。 互斥锁用结构mutex描述,它含有信号量计数和等待队列成员,信号量的值为1或0或负数。其列出如下(在include/linux/mutex.h中): struct mutex /* 1:表示解锁,0:表示锁住,负数:表示锁住,可能有等待者*/ atomic_t count; spinlock_t wait_lock; /*操作等待队列的自旋锁*/ struct list_head wait_list; /*等待队列*/ /*省略了用于调试的结构成员*/; 读/写信号量 读/写信号量适于在读多写少的情况下使用。如果一个任务需要读和写操作时,它将被看作写者,在不需要写操作的情况下可降级为读者。任意多个读者可同时拥有一个读/写信号量,对临界区代码进行操作。在没有写者操作时,任何读者都可成功获得读/写信号量进行读操作。如果有写者在操作时,读者必须被挂起等待直到写者释放该信号量。在没有写者或读者操作时,写者必须等待前面的写者或读者释放该信号量后,才能访问临界区。写者独占临界区,排斥其他的写者和读者,而读者只排斥写者。读/写信号量可通过依赖硬件架构或纯软件代码两种方式实现。下面只说明纯软件代码实现方式。 (1)API说明 用户可通过调用读/写信号量API实现读/写操作的同步。读/写信号量API说明如表1。 表1 读/写信号量API函数功能说明 API函数定义 功能说明 DECLARE_RWSEM(name) 声明名为name的读写信号量,并初始化它。 void init_rwsem(struct rw_semaphore *sem); 对读写信号量sem进行初始化。 void down_read(struct rw_semaphore *sem); 读者用来获取sem,若没获得时,则调用者睡眠等待。 void up_read(struct rw_semaphore *sem); 读者释放sem。int down_read_trylock(struct rw_semaphore *sem); 读者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用。 void down_write(struct rw_semaphore *sem); 写者用来获取sem,若没获得时,则调用者睡眠等待。 int down_write_trylock(struct rw_semaphore *sem); 写者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用 void up_write(struct rw_semaphore *sem); 写者释放sem。 void downgrade_write(struct rw_semaphore *sem);把写者降级为读者。 (2)读/写信号量结构rw_semaphore 读/写信号量结构rw_semaphore描述了读/写信号量的值和等待队列,其列出如下(在include/linux/rwsem-spinlock.h中):struct rw_semaphore /*读/写信号量定义: * - 如果activity为0,那么没有激活的读者或写者。 * - 如果activity为+ve,那么将有ve个激活的读者。 * - 如果activity为-1,那么将有1个激活的写者。 */ _s32 activity; /*信号量值*/ spinlock_t wait_lock; /*用于锁等待队列wait_list*/ struct list_head wait_list; /*如果非空,表示有进程等待该信号量*/#ifdef CONFIG_DEBUG_LOCK_ALLOC /*用于锁调试*/ struct lockdep_map dep_map;#endif; (3)读者加锁/解锁操作实现分析 1)加读者锁操作读者加锁函数down_read用于加读者锁,如果没有写者操作时,等待队列为空,读者可以加读者锁,将信号量的读者计数加1。如果有写在操作时,等待队列非空,读者需要等待写者操作完成。函数down_read列出如下(在kernel/rwsem.c中): void _sched down_read(struct rw_semaphore *sem) might_sleep(); /*用于调试自旋锁睡眠*/ rwsem_acquire_read(&sem-dep_map, 0, 0, _RET_IP_); /*确认获得锁,用于调试*/ /*跟踪锁状态信息(如:锁深度),用于调试*/ LOCK_CONTENDED(sem, _down_read_trylock, _down_read); 函数_down_read 完成加读者的具体操作,其列出如下(在lib/rwsem-spinlock.c中):void _sched _down_read(struct rw_semaphore *sem) struct rwsem_waiter waiter; struct task_struct *tsk; spin_lock_irq(&sem-wait_lock); /*如果有0或多个读者,并且等待队列为空,就可以获取sem*/ if (sem-activity = 0 & list_empty(&sem-wait_list) /* 获得sem */ sem-activity+; /*读者计数加1*/ spin_unlock_irq(&sem-wait_lock); goto out; /*运行到这里,说明不能获取sem,将当前进程加入等待队列进行等待*/ tsk = current; set_task_state(tsk, TASK_UNINTERRUPTIBLE); /* 建立等待队列成员*/ waiter.task = tsk; waiter.flags = RWSEM_WAITING_FOR_READ; /*表示等待读操作*/ get_task_struct(tsk); /*进程使用计数加1*/ list_add_tail(&waiter.list, &sem-wait_list); /*将等待成员加到等待队列尾*/ /* 不再需要访问等待队列,因此,这里解锁*/ spin_unlock_irq(&sem-wait_lock); /* 读者等待获取sem */ for (;) if (!waiter.task) break; schedule(); set_task_state(tsk, TASK_UNINTERRUPTIBLE); /*运行这里,退出等待,说明可以获取sem了*/ tsk-state = TASK_RUNNING;out:; 2)解读者锁操作函数up_read释放读者锁,如果等待队列非空,说明有写者在等待,就从等待队列唤醒一个写者。其列出如下(在kernel/rwsem.c中): void up_read(struct rw_semaphore *sem) rwsem_release(&sem-dep_map, 1, _RET_IP_); /*获取解锁信息,用于调试*/ _up_read(sem); 函数_up_read是释放读者锁的具体操作函数,其列出如下: void _up_read(struct rw_semaphore *sem) unsigned long flags; spin_lock_irqsave(&sem-wait_lock, flags); /*如果所有读者完成读操作,并且有写者等待,那么唤醒一个写者*/ if (-sem-activity = 0 &!list_empty(&sem-wait_list) sem = _rwsem_wake_one_writer(sem); spin_unlock_irqrestore(&sem-wait_lock, flags); /*唤醒一个写者*/static inline struct rw_semaphore *_rwsem_wake_one_writer(struct rw_semaphore *sem) struct rwsem_waiter *waiter; struct task_struct *tsk; sem-activity = -1; /*表示有一个写者正在写操作*/ /*获取一个等待者*/ waiter = list_entry(sem-wait_list.next, struct rwsem_waiter, list); list_del(&waiter-list); /*将该等待者从等待队列删除*/ tsk = waiter-task; smp_mb(); /*加内存屏障,确保完成上面的指针引用操作*/ waiter-task = NULL; wake_up_process(tsk); /*唤醒进程*/ put_task_struct(tsk); /*进程上下文使用计数减1*/ return sem; (3)写者加锁/解锁操作实现分析 1)加写者锁操作函数down_write完成加写者锁操作,其列出如下:void _sched down_write(struct rw_semaphore *sem) might_sleep(); rwsem_acquire(&sem-dep_map, 0, 0, _RET_IP_); LOCK_CONTENDED(sem, _down_write_trylock, _down_write); void _sched _down_write(struct rw_semaphore *sem) _down_write_nested(sem, 0); 函数_down_write_nested完成加写者锁的具体操作。当没有读者或写者操作时,写者才可以获取写者锁。写者锁是独占的。如果有其他写者或读者操作时,写者必须等待。其列出如下: void _sched _down_write_nested(struct rw_semaphore *sem, int subclass) struct rwsem_waiter waiter; struct task_struct *tsk; spin_lock_irq(&sem-wait_lock); /*如果没有读者,并且等待队列为空(说明没有写者)时,写者才能获取写者锁*/ if (sem-activi
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025河北保定市雄安传媒有限公司招聘10人笔试参考题库附带答案详解
- 卸货平台安全培训课件
- 2025年度韩城矿业公司高校毕业生招聘86人笔试参考题库附带答案详解
- 2025年国网宁夏电力有限公司第二批招聘高校毕业生调剂笔试参考题库附带答案详解
- 2025山东威海市乳山鑫蜜客人力资源有限公司招聘书记员7人笔试参考题库附带答案详解
- 2025天津东疆综合保税区管理委员会招聘10人笔试参考题库附带答案详解
- 2025四川长虹新网科技有限责任公司招聘装调工等岗位31人笔试参考题库附带答案详解
- 2025人保财险嘉兴市分公司社会招聘3人(浙江)笔试参考题库附带答案详解
- 2025中国机械工业集团有限公司国机集团总部社会招聘19人笔试参考题库附带答案详解
- 地铁岗前安全培训课件
- 《构成设计基础》全套教学课件
- 项目初步验收汇报
- 2025年山东省济宁市电工等级低压电工作业(应急管理厅)真题(含答案)
- otc药品管理办法
- 康复医学科病历书写规范与质量控制
- 商用厨房设计汇报
- 战术搜索教学课件
- 教科版五年级科学上册第一单元《光》测试卷及答案(含四题)
- Linux操作系统基础任务式教程(慕课版)课件 任务4 使用Linux操作系统中的硬盘
- 自控系统报警管理制度
- 口腔服务5S管理
评论
0/150
提交评论