分类: LINUX
2015-04-15 18:48:01
读写自旋锁是增强版的自旋锁,其和自旋锁的主要区别是:自旋锁不管请求类型如何,他只允许一个请求来处理资源;读写自旋锁则不一样,他把请求分为读者(对资源进行读操作的请求)和写者(对资源执行写操作的请求),他允许有多个读者同时访问资源,但写者必须独占访问资源。
内核使用rwlock_t 结构来描述一个读写自旋锁。
我们可以发现读写自旋锁的定义和自旋锁完全相同,当然他们的锁值的内涵不同,读写自旋锁锁值的最高位被置一时,说明有写者在使用资源,而低 30 位则用来记录读者数量。
我们可以调用 read_lock 宏来为读者得到一个读写自旋锁。read_lock 实际上又被替换成对 _read_lock() 函数的调用,下面是 _read_lock() 的定义。
_read_lock() 几乎和 _spin_lock() 完全相同,唯一不同的是他最后使用 _raw_read_lock 宏来执行真正的取锁操作,而 _raw_read_lock 又会被替换成对 __raw_read_lock() 函数的调用。
173 行读出读写自旋锁的锁值,由于我们取得的是读锁(即又一个读者来了),所以在 174 行递增读出的锁值并根据修改后的结果修改 CPSR 的相关位,如果锁值被递增后为正数,就说明现在没有写锁存在(如果写锁存在,那么其值必然大于 0x80000000,而对于有符号数而言,大于 0x80000000 就是负数),没有写锁存在就说明我们可以获取这个读锁(写锁是独占资源的),那么就在 175 行写回锁值(得到读锁)。strex 命令会把写操作的结果写到 tmp2。
179 行检查 175 行的写入操作是否成功,如果 0 减去 tmp2 的值为负数,就说明写入锁值失败(如果写入成功,那么 tmp2 为0,故应是 0 减 0 结果不会是负数),那么就在 180 行跳回到 173 重新获取读锁。在这里大家可能会觉得有些奇怪,那就是为什么不直接判断 tmp2 是否为 0 呢?这是因为如果在 174 行检测出读写自旋锁上已经有了一个写者的话,其结果为负数,那么他也会在 180 行跳回到上面重新得到读锁(即开始自旋)。
释放读自旋锁实际上和释放自旋锁也非常类似,他最后会调用到__raw_read_unlock() 来释放读自旋锁。我们下面直接讲解 __raw_read_unlock() 的代码。
197 行读出锁值,然后在 198 行递减锁值(释放读锁,少了一个读者嘛!),最后在 199 行递减后的锁值写回读自旋锁。
200 行检查 199 行的写操作是否成功,如果失败,则跳回到 197 行重新开始释放读自旋锁的操作;如果成功,则函数退出。
我们可以使用 write_lock 宏来得到一个写锁,他和获取读锁的操作几乎完全相同,他最后会调用到__raw_write_lock () 函数来得到写锁。
101 行先取出锁值,然后在 102 行查看是否已经有读者或写者得到这把锁了,如果这个锁已经被获得了(锁值不为0),由于写者必须是独占资源的,所以他会直接在 108 行调回到 101 行准备进入自旋状态。如果锁没有被获得,那么就会在 106 行把 0x80000000 写入锁值(最高位置1,表示有写者在使用资源),然后在 107 行检查是否写入成功,若写入失败就会在 108 行跳回到 101 行重新开始。
我们可以使用 write_unlock 宏来释放写锁,他最后会调用到__raw_write_unlock() 函数来释放写锁。
由于写者是独占资源的,不可能存在其他读者或写者,所以我们直接把锁值清零即可。