内核同步机制-信号量/互斥锁/读-写信号量sema,mutexwsem信号量通用信号量用户类进程之间使用信号量(semaphore)进行同步,内核线程之间也使用了信号量,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作。信号量与自旋锁类似,保护临界区代码。但信号量与自旋锁有一定的区别,信号量在无法得到资源时,内核线程处于睡眠阻塞状态,而自旋锁处于忙等待状态。因此,如果资源被占用时间很短时linux 信号量 互斥锁,使用自旋锁较好,因为它可节约调度时间。如果资源被占用的时间较长,使用信号量较好,因为可让CPU调度去做其它进程的工作。函数定义功能说明sema_init(structsemaphoresem,intval)初始化信号量,将信号量计数器值设置val。down(structsemaphoresem)获取信号量,不建议使用此函数。down_interruptible(structsemaphoresem)可被中断地获取信号量,如果睡眠被信号中断,返回错误-EINTR。down_killable(structsemaphoresem)可被杀死地获取信号量。如果睡眠被致命信号中断,返回错误-EINTR。
down_trylock(structsemaphoresem)尝试原子地获取信号量,如果成功获取linux空间,返回0,不能获取,返回1。down_timeout(structsemaphoresem,longiffies)在指定的时间jiffies内获取信号量,若超时未获取,返回错误-ETIME。up(structsemaphoresem)释放信号量sem。样例:信号量的使用下面函数do_utimes利用信号量防止多个线程对文件系统节点inode同时进行访问。其列出如下(在fs/open.clongdo_utimes(char__userfilename,structtimevalstructinode获取信号量errornotify_change(nd.dentry,&newattrs);/修改inode中值up(&inode->i_sem);下面说明信号量API函数。(1)信号量结构semaphore信号量用结构semaphore描述,它在自旋锁的基础上改进而成,它包括一个自旋锁、信号量计数器和一个等待队列。用户程序只能调用信号量API函数,而不能直接访问信号量结构,其列出如下(在include/linux/semaphore.hPDFcreatedpdfFactoryProtrialversion;unsignedintcount;structlist_headwait_list;(2)初始化函数sema_init函数sema_init初始化信号量,将信号量值初始化为n,其列出如下:staticinlinevoidsema_init(structsemaphoresem,intval)staticstructlock_class_key__key;(structsemaphore)__SEMAPHORE_INITIALIZER(*sem,val);lockdep_init_map(&sem->lock.dep_map,"semaphore->lock",&__key,#define__SEMAPHORE_INITIALIZER(name,__SPIN_LOCK_UNLOCKED((name).lock),初始化自旋锁.count将信号量计数器赋值为n.wait_listLIST_HEAD_INIT((name).wait_list),(3)可中断获取信号量函数down_interruptible函数down_interruptible获取信号量,存放在参数sem中。
它尝试获取信号量,如果其他线程被允许尝试获取此信号量,则将本线程睡眠等待。如果有一个信号中断睡眠,则它返回错误-EINTR。如果成功获取信号量,函数返回0。函数down_interruptible列出如下(在kernel/semaphore.cintdown_interruptible(structsemaphoreunsignedlongflags;intresultspin_lock_irqsave(&sem->lock,flags);likely表示成功获取的概率大,通知编译器进行分支预测优化sem->count--;elseresult进入睡眠等待spin_unlock_irqrestore(&sem->lock,flags);returnresult;PDFcreatedpdfFactoryProtrialversion(structsemaphorereturn__down_common(sem,TASK_INTERRUPTIBLE,MAX_SCHEDULE_TIMEOUT);函数__down_common进入睡眠等待linux 信号量 互斥锁,其列出如下:staticinlineint__sched__down_common(structsemaphoresem,longstate,longtimeout)current;structsemaphore_waiterwaiter;list_add_tail(&waiter.list,&sem->wait_list);加入到等待队列waiter.tasktask;waiter.upTASK_INTERRUPTIBLEsignal_pending(task))gotointerrupted;TASK_KILLABLEfatal_signal_pending(task))gotointerrupted;gototimed_out;__set_task_state(task,state);spin_unlock_irq(&sem->lock);timeout调度spin_lock_irq(&sem->lock);(waiter.up)returntimed_out:list_del(&waiter.list);return-ETIME;interrupted:list_del(&waiter.list);return-EINTR;(3)释放信号量函数up函数up在没有其他线程等待使用信号量的情况下释放信号量,否则,唤醒其他等待线程。
其列出如PDFcreatedpdfFactoryProtrialversion(structsemaphore unsignedlong flags; spin_lock_irqsave(&sem->lock, flags); 没有线程等待此信号量,释放信号量,将信号量计数器加1,表示增加了1个空闲资源* sem->count++;else __up(sem); spin_unlock_irqrestore(&sem->lock,flags); staticnoinline void __sched __up(struct semaphore structsemaphore_waiter list_first_entry(&sem->wait_list,struct semaphore_waiter, list); list_del(&waiter->list); 将本线程从等待队列删除waiter->up 互斥锁信号量的初始值表示可以有多少个任务可同时访问的共享资源,如果初始值为1,表示只有1 务可以访问,信号量变成互斥锁(Mutex)。可见互斥锁是信号量的特例。互斥锁(mutex)是在原子操作API 的基础上实现的信号量行为。
互斥锁不能进行递归锁定或解锁, 能用于交互上下文,同一时间只能有一个任务持有互斥锁。 互斥锁功能上基本上与信号量一样,互斥锁占用空间比信号量小,运行效率比信号量高。 API 函数 功能说明 DEFI NE_MUTEX(mutexname) 创建和初始化互斥锁。 void mutex_lock(struct mutex lock);加锁。 void mutex_unlock(struct mutex lock);解锁。 int mutex_trylock(struct mutex lock);尝试加锁。 互斥锁用结构mutex 描述,它含有信号量计数和等待队列成员,信号量的值为1 或负数。其列出如下(在include/ linux/ mutex.h structmutex atomic_tcount; PDF created pdfFactoryPro trial version spinlock_t wait_lock; structlist_head wait_list; 写信号量适于在读多写少的情况下使用。如果一个任务需要读和写操作时,它将被看作写者,在不需要写操作的情况下可降级为读者。
任意多个读者可同时拥有一个读/ 写信号量,对临界区代码进 行操作。 在没有写者操作时,任何读者都可成功获得读/ 写信号量进行读操作。如果有写者在操作时,读者必 须被挂起等待直到写者释放该信号量。在没有写者或读者操作时,写者必须等待前面的写者或读者 释放该信号量后,才能访问临界区。写者独占临界区,排斥其他的写者和读者,而读者只排斥写者。 写信号量可通过依赖硬件架构或纯软件代码两种方式实现。下面只说明纯软件代码实现方式。(1)API 说明 用户可通过调用读/ 写信号量API 实现读/ 写操作的同步。 API 函数定义 功能说明 DECLARE_RWSEM(name) 声明名为name 的读写信号量,并初始化它。 void init_rwsem(struct rw_semaphore sem);对读写信号量sem 进行初始化。 void down_read(struct rw_semaphore sem);读者用来获取semubuntu linux,若没获得时,则调用者睡眠等 voidup_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,若没获得时,则调用者睡眠等 intdown_write_trylock(struct rw_semaphore sem);写者尝试获取sem,如果获得返回1,如果 没有获得返回0。可在中断上下文使用 void up_write(struct rw_semaphore sem);写者释放sem。 void downgrade_write(struct rw_semaphore sem);把写者降级为读者。 写信号量结构rw_semaphore描述了读/ 写信号量的值和等待队列,其列出如下(在 include/ linux/ rwsem-spinlock.h structrw_semaphore 如果activity为0,那么没有激活的读者或写者。 如果activity为+ve,那么将有ve 个激活的读者。 如果activity为-1,那么将有1 个激活的写者。 __s32activity; spinlock_twait_lock; PDFcreated pdfFactoryPro trial version struct list_head wait_list; #ifdefCONFI G_DEBUG_LOCK_ALLOC structlockdep_map dep_map; #endif (3)读者加锁/解锁操作实现分析 1)加读者锁操作 读者加锁函数down_read 用于加读者锁,如果没有写者操作时,等待队列为空,读者可以加读者锁, 将信号量的读者计数加1。
如果有写在操作时,等待队列非空,读者需要等待写者操作完成。函数 down_read 列出如下(在kernel/ rwsem.c void__sched down_read(struct rw_semaphore LOCK_CONTENDED(sem,__down_read_trylock, __down_read); 函数__down_read完成加读者的具体操作,其列出如下(在lib/ rwsem-spinlock.c void__sched __down_read(struct rw_semaphore structrwsem_waiter waiter; struct tsk;spin_lock_irq(&sem->wait_lock); 如果有0或多个读者,并且等待队列为空,就可以获取sem* spin_unlock_irq(&sem->wait_lock);goto out; current;set_task_state(tsk, TASK_UNI NTERRUPTI BLE); tsk;waiter.flags RWSEM_WAITI NG_FOR_READ; PDFcreated pdfFactoryPro trial version list_add_tail(&waiter.list, &sem->wait_list); (!waiter.task)break; schedule(); set_task_state(t sk, TASK_UNI NTERRUPTI BLE); tsk->state TASK_RUNNING; out: 2)解读者锁操作函数up_read 释放读者锁,如果等待队列非空,说明有写者在等待,就从等待队列唤醒一个写者。
其列出如下(在kernel/ rwsem.c voidup_read(struct rw_semaphore 函数__up_read是释放读者锁的具体操作函数,其列出如下: void __up_read(struct rw_semaphore unsignedlong flags; spin_lock_irqsave(&sem->wait_lock, flags); !list_empty(&sem->wait_list))sem __rwsem_wake_one_writer(sem);spin_unlock_irqrestore(&sem->wait_lock, flags); staticinline struct rw_semaphore __rwsem_wake_one_writer(structrw_semaphore sem)PDF created pdfFactoryPro trial version structrwsem_waiter waiter;struct tsk;sem->activity list_entry(sem->wait_list.next,struct rwsem_waiter, list); list_del(&waiter->list); waiter->task; smp_mb(); NULL;wake_up_process(tsk); put_task_struct(tsk); returnsem; (3)写者加锁/解锁操作实现分析 1)加写者锁操作 函数down_write 完成加写者锁操作,其列出如下: void __sched down_write(struct rw_semaphore might_sleep();rwsem_acquire(&sem->dep_map, LOCK_CONTENDED(sem,__down_write_trylock, __down_write); void__sched __down_write(struct rw_semaphore 函数__down_write_nested完成加写者锁的具体操作。
当没有读者或写者操作时,写者才可以获取 写者锁。写者锁是独占的。如果有其他写者或读者操作时,写者必须等待。其列出如下: void __sched __down_write_nested(struct rw_semaphore sem,int subclass) structrwsem_waiter waiter; struct tsk;spin_lock_irq(&sem->wait_lock); PDFcreated pdfFactoryPro trial version -1;spin_unlock_irq(&sem->wait_lock); goto out; current;set_task_state(tsk, TASK_UNI NTERRUPTI BLE); tsk;waiter.flags RWSEM_WAITI NG_FOR_WRI TE; list_add_tail(&waiter.list,&sem->wait_list); (!waiter.task)break; schedule(); set_task_state(t sk, TASK_UNI NTERRUPTI BLE); tsk->state TASK_RUNNING; out: 函数up_write释放写者锁,将读者计数设置为0,其列出如下: void up_write(struct rw_semaphore void__up_write(struct rw_semaphore PDFcreated pdfFactoryPro trial version unsigned long flags; spin_lock_irqsave(&sem->wait_lock, flags); sem->activity 表示有0个读者* (!list_empty(&sem->wait_list))sem spin_unlock_irqrestore(&sem->wait_lock,flags); PDFcreated pdfFactoryPro trial version