rw_semaphore

来源:互联网 发布:淘宝店铺可以转让 编辑:程序博客网 时间:2024/06/05 15:29

摘抄一段:

读写信号量对访问者进行了细分,或者为读者,或者为写者,读者在保持读写信号量期间只能对该读写信号量保护的共享资源进行读访问,如果一个任务除了需要读,可能还需要写,那么它必须被归类为写者,它在对共享资源访问之前必须先获得写者身份,写者在发现自己不需要写访问的情况下可以降级为读者。读写信号量同时拥有的读者数不受限制,也就说可以有任意多个读者同时拥有一个读写信号量。如果一个读写信号量当前没有被写者拥有并且也没有写者等待读者释放信号量,那么任何读者都可以成功获得该读写信号量;否则,读者必须被挂起直到写者释放该信号量。如果一个读写信号量当前没有被读者或写者拥有并且也没有写者等待该信号量,那么一个写者可以成功获得该读写信号量,否则写者将被挂起,直到没有任何访问者。因此,写者是排他性的,独占性的。
读写信号量有两种实现,一种是通用的,不依赖于硬件架构,因此,增加新的架构不需要重新实现它,但缺点是性能低,获得和释放读写信号量的开销大;另一种是架构相关的,因此性能高,获取和释放读写信号量的开销小,但增加新的架构需要重新实现。在内核配置时,可以通过选项去控制使用哪一种实现。

1 数据结构

struct rw_semaphore {longcount;raw_spinlock_twait_lock;struct list_headwait_list;#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lockdep_mapdep_map;#endif};

2 初始化

2.1 动态

#define init_rwsem(sem)\do {\static struct lock_class_key __key;\\__init_rwsem((sem), #sem, &__key);\} while (0)
void __init_rwsem(struct rw_semaphore *sem, const char *name,  struct lock_class_key *key){#ifdef CONFIG_DEBUG_LOCK_ALLOC/* * Make sure we are not reinitializing a held semaphore: */debug_check_no_locks_freed((void *)sem, sizeof(*sem));lockdep_init_map(&sem->dep_map, name, key, 0);#endifsem->count = RWSEM_UNLOCKED_VALUE;raw_spin_lock_init(&sem->wait_lock);INIT_LIST_HEAD(&sem->wait_list);}

2.2 静态

#define DECLARE_RWSEM(name) \struct rw_semaphore name = __RWSEM_INITIALIZER(name)
#define __RWSEM_INITIALIZER(name)\{ RWSEM_UNLOCKED_VALUE,\  __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),\  LIST_HEAD_INIT((name).wait_list)\  __RWSEM_DEP_MAP_INIT(name) }
count初始化为0,表示读写信号量可用。

3 获取write锁

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);}
#define LOCK_CONTENDED(_lock, try, lock)\do {\if (!try(_lock)) {\lock_contended(&(_lock)->dep_map, _RET_IP_);\lock(_lock);\}\lock_acquired(&(_lock)->dep_map, _RET_IP_);\} while (0)
先try一下lock,如果拿不到lock,再进行加锁动作。
static inline int __down_write_trylock(struct rw_semaphore *sem){long tmp;tmp = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE,      RWSEM_ACTIVE_WRITE_BIAS);return tmp == RWSEM_UNLOCKED_VALUE;}
#define cmpxchg(ptr,o,n)\((__typeof__(*(ptr)))__cmpxchg_mb((ptr),\  (unsigned long)(o),\  (unsigned long)(n),\  sizeof(*(ptr))))
static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, unsigned long new, int size){unsigned long ret;smp_mb();ret = __cmpxchg(ptr, old, new, size);smp_mb();return ret;}
static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,      unsigned long new, int size){unsigned long oldval, res;switch (size) {#ifndef CONFIG_CPU_V6/* min ARCH >= ARMv6K */case 1:do {asm volatile("@ __cmpxchg1\n""ldrexb%1, [%2]\n""mov%0, #0\n""teq%1, %3\n""strexbeq %0, %4, [%2]\n": "=&r" (res), "=&r" (oldval): "r" (ptr), "Ir" (old), "r" (new): "memory", "cc");} while (res);break;case 2:do {asm volatile("@ __cmpxchg1\n""ldrexh%1, [%2]\n""mov%0, #0\n""teq%1, %3\n""strexheq %0, %4, [%2]\n": "=&r" (res), "=&r" (oldval): "r" (ptr), "Ir" (old), "r" (new): "memory", "cc");} while (res);break;#endifcase 4:do {asm volatile("@ __cmpxchg4\n""ldrex%1, [%2]\n""mov%0, #0\n""teq%1, %3\n""strexeq %0, %4, [%2]\n": "=&r" (res), "=&r" (oldval): "r" (ptr), "Ir" (old), "r" (new): "memory", "cc");} while (res);break;default:__bad_cmpxchg(ptr, size);oldval = 0;}return oldval;}
%0:res %1:oldval %2:ptr %3:old %4:new
oldval = *ptr;
res = 0;
if (oldval == old) *ptr = new;这个过程是原子的,而且保证更新成功为止。
最后返回的是oldval。
if (oldval == old)那么一定会保证*ptr = new原子的执行成功;传进来的old为RWSEM_UNLOCKED_VALUE(0),需要当前是lock可用的状态;如果当前状态不可用的,那么if (oldval != old),直接return oldval,应该是RWSEM_ACTIVE_WRITE_BIAS。
try lock没成功,就要调用__down_write()进行加锁了。
static inline void __down_write(struct rw_semaphore *sem){__down_write_nested(sem, 0);}
static inline void __down_write_nested(struct rw_semaphore *sem, int subclass){long tmp;tmp = atomic_long_add_return(RWSEM_ACTIVE_WRITE_BIAS,     (atomic_long_t *)&sem->count);//下面的if成立,说明之前的count不为0,lock是不可用的,要等待处理if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS))rwsem_down_write_failed(sem);}
struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem){long count, adjustment = -RWSEM_ACTIVE_WRITE_BIAS;struct rwsem_waiter waiter;struct task_struct *tsk = current;/* set up my own style of waitqueue */waiter.task = tsk;waiter.type = RWSEM_WAITING_FOR_WRITE;raw_spin_lock_irq(&sem->wait_lock);//如果是第一个需要等待的请求,adjustment = -RWSEM_ACTIVE_BIAS = -1if (list_empty(&sem->wait_list))adjustment += RWSEM_WAITING_BIAS;list_add_tail(&waiter.list, &sem->wait_list);/* we're now waiting on the lock, but no longer actively locking */count = rwsem_atomic_update(adjustment, sem);/* If there were already threads queued before us and there are no * active writers, the lock must be read owned; so we try to wake * any read locks that were queued ahead of us. *//*如果这里已经有线程排在我们前面,并且不是writers,那么lock必须被read拥有,所以我们尝试唤醒排在我们前面的read lock。if()就说明当前的waiter前面还有reader waiter。*/if (count > RWSEM_WAITING_BIAS &&    adjustment == -RWSEM_ACTIVE_WRITE_BIAS)sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);/* wait until we successfully acquire the lock */set_task_state(tsk, TASK_UNINTERRUPTIBLE);while (true) {/*这里说明已经没有active的writer了,如果有,走下面的while*/if (!(count & RWSEM_ACTIVE_MASK)) {/* Try acquiring the write lock. */count = RWSEM_ACTIVE_WRITE_BIAS;if (!list_is_singular(&sem->wait_list))count += RWSEM_WAITING_BIAS;/*如果是第一个需要等待的请求,adjustment = -RWSEM_ACTIVE_BIAS = -1,rwsem_atomic_update(adjustment, sem)执行,并且有up_write()后,sem->count = n*RWSEM_ACTIVE_WRITE_BIAS- RWSEM_ACTIVE_WRITE_BIAS-RWSEM_ACTIVE_BIAS=RWSEM_WAITING_BIAS;如果wait list上没有其他的waiter,说明此时可以获取了。如果不是第一个需要等待的请求,也总会等待sem->count == RWSEM_WAITING_BIAS 的时候,然后执行下面的语句,重写sem->count,如果wait list上没有其他的waiter,count=RWSEM_ACTIVE_WRITE_BIAS,直接获得lock;如果还有其他的waiter,count += RWSEM_WAITING_BIAS,其他的waiter会等待本次up_write()后,继续进行相同的动作。*/if (sem->count == RWSEM_WAITING_BIAS &&    cmpxchg(&sem->count, RWSEM_WAITING_BIAS, count) ==RWSEM_WAITING_BIAS)break;}raw_spin_unlock_irq(&sem->wait_lock);/* Block until there are no active lockers. *//*如果有active的lock下面的while就不会结束*/do {schedule();set_task_state(tsk, TASK_UNINTERRUPTIBLE);} while ((count = sem->count) & RWSEM_ACTIVE_MASK);raw_spin_lock_irq(&sem->wait_lock);}list_del(&waiter.list);raw_spin_unlock_irq(&sem->wait_lock);tsk->state = TASK_RUNNING;return sem;}
static struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type){struct rwsem_waiter *waiter;struct task_struct *tsk;struct list_head *next;long oldcount, woken, loop, adjustment;waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);if (waiter->type == RWSEM_WAITING_FOR_WRITE) {if (wake_type == RWSEM_WAKE_ANY)/* Wake writer at the front of the queue, but do not * grant it the lock yet as we want other writers * to be able to steal it.  Readers, on the other hand, * will block as they will notice the queued writer. *//*wake 在队列前面的writer,但是不保证我们需要的其他writers能偷走该lock。Readers将会阻塞,并通知队列中的writer。如果type是writer,up_write()的时候,才能wake。*/wake_up_process(waiter->task);goto out;}/* Writers might steal the lock before we grant it to the next reader. * We prefer to do the first reader grant before counting readers * so we can bail out early if a writer stole the lock. *//*Writers 可以抢占lock,在我们保证它到下一个reader之前。在连续的reader之前,我们更喜欢去做第一个reader的保护,所以如果一个writer偷到了lock,我们可以尽早离开。*/adjustment = 0;//RWSEM_WAKE_READ_OWNED用于write lock降级为read lockif (wake_type != RWSEM_WAKE_READ_OWNED) {adjustment = RWSEM_ACTIVE_READ_BIAS; try_reader_grant:oldcount = rwsem_atomic_update(adjustment, sem) - adjustment;if (unlikely(oldcount < RWSEM_WAITING_BIAS)) {/* A writer stole the lock. Undo our reader grant. */if (rwsem_atomic_update(-adjustment, sem) &RWSEM_ACTIVE_MASK)goto out;/* Last active locker left. Retry waking readers. */goto try_reader_grant;}}/* Grant an infinite number of read locks to the readers at the front * of the queue.  Note we increment the 'active part' of the count by * the number of readers before waking any processes up. *///找到可以wake的readerwoken = 0;do {woken++;if (waiter->list.next == &sem->wait_list)break;waiter = list_entry(waiter->list.next,struct rwsem_waiter, list);} while (waiter->type != RWSEM_WAITING_FOR_WRITE);adjustment = woken * RWSEM_ACTIVE_READ_BIAS - adjustment;if (waiter->type != RWSEM_WAITING_FOR_WRITE)/* hit end of list above */adjustment -= RWSEM_WAITING_BIAS;if (adjustment)rwsem_atomic_add(adjustment, sem);next = sem->wait_list.next;loop = woken;do {waiter = list_entry(next, struct rwsem_waiter, list);next = waiter->list.next;tsk = waiter->task;smp_mb();waiter->task = NULL;wake_up_process(tsk);put_task_struct(tsk);} while (--loop);sem->wait_list.next = next;next->prev = &sem->wait_list; out:return sem;}

3.2 down_write_trylock()

尝试获取write lock。
int down_write_trylock(struct rw_semaphore *sem){int ret = __down_write_trylock(sem);if (ret == 1)rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_);return ret;}
成功返回1,失败返回0。

4 释放write锁

void up_write(struct rw_semaphore *sem){rwsem_release(&sem->dep_map, 1, _RET_IP_);__up_write(sem);}
static inline void __up_write(struct rw_semaphore *sem){//如果还有其他writer等待获取lock,需要wake它if (unlikely(atomic_long_sub_return(RWSEM_ACTIVE_WRITE_BIAS, (atomic_long_t *)&sem->count) < 0))rwsem_wake(sem);}
struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem){unsigned long flags;raw_spin_lock_irqsave(&sem->wait_lock, flags);/* do nothing if list empty *///wake根据前面说的情况,此时writer和reader都能wakeif (!list_empty(&sem->wait_list))sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);raw_spin_unlock_irqrestore(&sem->wait_lock, flags);return sem;}

5 获取read lock

5.1 down_read()

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);}
static inline int __down_read_trylock(struct rw_semaphore *sem){long tmp;while ((tmp = sem->count) >= 0) {//没有writer在等lockif (tmp == cmpxchg(&sem->count, tmp,   tmp + RWSEM_ACTIVE_READ_BIAS)) {return 1;}}return 0;}
static inline void __down_read(struct rw_semaphore *sem){if (unlikely(atomic_long_inc_return((atomic_long_t *)&sem->count) <= 0))//获取之前有active write lockrwsem_down_read_failed(sem);}
struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem){long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;struct rwsem_waiter waiter;struct task_struct *tsk = current;/* set up my own style of waitqueue */waiter.task = tsk;waiter.type = RWSEM_WAITING_FOR_READ;get_task_struct(tsk);raw_spin_lock_irq(&sem->wait_lock);//如果是第一个需要等待的请求if (list_empty(&sem->wait_list))adjustment += RWSEM_WAITING_BIAS;list_add_tail(&waiter.list, &sem->wait_list);/* we're now waiting on the lock, but no longer actively locking */count = rwsem_atomic_update(adjustment, sem);/* If there are no active locks, wake the front queued process(es). * * If there are no writers and we are first in the queue, * wake our own waiter to join the existing active readers ! *//*如果没有active locks,wake队列之前的处理,如果此处没有writers,并且我们就是队列的第一个请求,wake我们自己的waiter加入已存在的active readers。count == RWSEM_WAITING_BIAS,该请求前面有一个read lock请求。(count > RWSEM_WAITING_BIAS &&     adjustment != -RWSEM_ACTIVE_READ_BIAS),该请求前面有很多read lock请求*/if (count == RWSEM_WAITING_BIAS ||    (count > RWSEM_WAITING_BIAS &&     adjustment != -RWSEM_ACTIVE_READ_BIAS))sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);raw_spin_unlock_irq(&sem->wait_lock);/* wait to be given the lock */while (true) {set_task_state(tsk, TASK_UNINTERRUPTIBLE);if (!waiter.task)break;schedule();}tsk->state = TASK_RUNNING;return sem;}

5.2 down_read_trylock()

尝试获取read lock。
int down_read_trylock(struct rw_semaphore *sem){int ret = __down_read_trylock(sem);if (ret == 1)rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_);return ret;}
成功返回1,失败返回0。

6 释放read lock

void up_read(struct rw_semaphore *sem){rwsem_release(&sem->dep_map, 1, _RET_IP_);__up_read(sem);}
static inline void __up_read(struct rw_semaphore *sem){long tmp;tmp = atomic_long_dec_return((atomic_long_t *)&sem->count);//前面有active write lock,并且只有一个if (unlikely(tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0))rwsem_wake(sem);}
struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem){unsigned long flags;raw_spin_lock_irqsave(&sem->wait_lock, flags);/* do nothing if list empty */if (!list_empty(&sem->wait_list))sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);raw_spin_unlock_irqrestore(&sem->wait_lock, flags);return sem;}

7 downgrade_write()

该操作会原子的将一个已经获得的写锁转化成读锁。
当某个快速改变获得了writer lock,而其后是更长时间的只读访问,可在结束修改之后调用downgrade_write(),它允许其他读操作在你结束写后,马上获得rwsem了。否侧通常的处理是需要等待这个紧跟的漫长的读操作。
最好在很少需要写访问且writer只会短期拥有信号量的时候使用rwsem。
void downgrade_write(struct rw_semaphore *sem){/* * lockdep: a downgraded write will live on as a write * dependency. */__downgrade_write(sem);}
static inline void __downgrade_write(struct rw_semaphore *sem){long tmp;/*当前只有一个active write lock时,tmp>0,count由RWSEM_ACTIVE_WRITE_BIAS变成了RWSEM_ACTIVE_READ_BIAS,实现了write到read的转换,该lock不会被其他write waiter偷走。如果有多个active write lock,tmp < 0,需要进入rwsem_downgrade_wake(sem)去wake read waiter,避免被下一个write waiter偷走。*/tmp = atomic_long_add_return(-RWSEM_WAITING_BIAS,     (atomic_long_t *)&sem->count);if (tmp < 0)rwsem_downgrade_wake(sem);}
/* * downgrade a write lock into a read lock * - caller incremented waiting part of count and discovered it still negative * - just wake up any readers at the front of the queue */struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem){unsigned long flags;raw_spin_lock_irqsave(&sem->wait_lock, flags);/* do nothing if list empty */if (!list_empty(&sem->wait_list))sem = __rwsem_do_wake(sem, RWSEM_WAKE_READ_OWNED);raw_spin_unlock_irqrestore(&sem->wait_lock, flags);return sem;}
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 车子累计扣30分怎么办 实习期间扣满12分怎么办 新手驾照扣6分怎么办 a2驾驶证逾期未审验怎么办 c1驾照扣了6分怎么办 b1驾照被扣12分怎么办 b2驾驶本扣分了怎么办 驾驶本扣9分后怎么办 b1照扣12分怎么办 b2扣了15分怎么办 b2有扣分未年审怎么办 b2驾驶证扣4分怎么办 b2驾驶证扣10分怎么办 刚发驾驶证照片太丑想换怎么办! 考驾照时户口变更怎么办 驾照年审色盲未过怎么办 考驾驶证互联网注册号码怎么办 驾驶证体检视力不过关怎么办 六年驾照满了怎么办 驾照扣了40多分怎么办 一个驾照扣24分怎么办 南昌电动车牌照丢了怎么办 上海餐饮工作人员怎么办健康证 房产过户没有遗嘱公证怎么办 在外地被扣12分怎么办 公务员体检视力不过关怎么办弱视 身份证被盗用注册公司怎么办 驾照分卖了12分怎么办 一年12分扣完了怎么办 滴滴车管所信息不同步怎么办 驾驶证过期两个月了怎么办 科目二考试第一次不合格怎么办 科目二不想考了怎么办 科二有事考不了怎么办 科四有事去不了怎么办 社保卡发了密码怎么办 不知道社保卡号怎么办 科目三未到30天怎么办 户口地址变了驾照怎么办 拿了驾照没开车怎么办 免检的车在外地怎么办