大话linux(五)之互斥锁mutex



    上文说到小明驱车离开银行,随即开到了比较大的商城茂业(在深圳来说,茂业算是大商城咯)楼下,准备把车停在停车场下,进里面看看zippo火机,自己怎么也算是个“白骨精”(新时代对白领、骨干、精英的称谓),吸烟也得有个好装备:)驱车来到停车场前,发现保安在为排在前面的10辆车一个个的发停车卡,发停车卡很快的,自己等一下吧。不禁又联想到这个和操作系统的那个机制有些类似呢?对了,互斥锁。其实互斥锁和当count为1的信号量有相似之处,但却又不同。
信号量:count为1时,也可以达到互斥的效果,当进程想进入临界区的时候,首先获得信号量,当count为
        0时,进程进入睡眠状态,等待获得信号量的进程释放信号量。
互斥锁:专为互斥设计的,内部实现为: 首先快速的的方式获得互斥锁,若是快速获得信号量
        失败,则进入通用的(慢速的)获取互斥锁,通用互斥锁获取方式同count为0时的机制相同。
从这里可以看到,互斥锁的一个设计原则是基于一个事实:拥有互斥锁的进程总是会在尽可能短的时间里释放。只有在这个前提下,使用互斥锁效率才是最高的,否则,和使用信号量差别也不大。对应到自己,发现自己在排队等待的过程多类似获得停车卡的一个过程,大家首先都认为发停车卡是一个很快的过程,所以,都排队等候而不会离开。当然,也有运气不好的,发到自己就发完了,没有停车位,那么你也可以选择停车等候,一旦有了停车位,保安会通知你让你进去。
互斥锁在内核中的定义如下:
struct mutex {
 /* 1: unlocked, 0: locked, negative: locked, possible waiters */
 atomic_t  count;
 spinlock_t  wait_lock;
 struct list_head wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
 struct thread_info *owner;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
 const char   *name;
 void   *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
 struct lockdep_map dep_map;
#endif
};
去除调试信息后,我们发现和信号量的定义没有什么不同,主要差别体现在获取锁的操作DOWN和释放锁的操作UP上:
DOWN操作:
void __sched mutex_lock(struct mutex *lock)
{
 might_sleep();
 /*
  * The locking fastpath is the 1->0 transition from
  * 'unlocked' into 'locked' state.
  */
  __mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);
 mutex_set_owner(lock);
}
UP操作:
void __sched mutex_unlock(struct mutex *lock)
{
 /*
  * The unlocking fastpath is the 0->1 transition from 'locked'
  * into 'unlocked' state:
  */
#ifndef CONFIG_DEBUG_MUTEXES
 /*
  * When debugging is enabled we must not clear the owner before time,
  * the slow path will always be taken, and that clears the owner field
  * after verifying that it was indeed current.
  */
 mutex_clear_owner(lock);
#endif
  __mutex_fastpath_unlock(&lock->count, __mutex_unlock_slowpath);
}
我们看红色和蓝色部分,一个是快速路径获取,一个是慢速路径获取,慢速路径获取没有什么可说的,现在我们重点说一下快速路径的获取:
static inline void
__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
 int __ex_flag, __res;
 __asm__ (
  " ldrex %0, [%2] \n\t"  //完成__res=count->counter
  "sub %0, %0, #1 \n\t"  //完成__res=__res - 1
  " strex %1, %0, [%2] "  //完成用_res的当前值来更新count->counter
  : "=&r" (__res), "=&r" (__ex_flag)
  : "r" (&(count)->counter)
  : "cc","memory" );
 __res |= __ex_flag;
 if (unlikely(__res != 0))
  fail_fn(count);
}
红色的ldrex和strex是ARM架构下实现“读-更新-写回”原子操作的方式,其 原理也是在操作过程中对bus进行监控,x86上则是利用带有LOCK前缀的方式来实现总线的监控,从而达到原子操作的方式。up的实现类似,在此不累述。其实上面的原理很简单就可以对应到我们现实生活中,想想看:首先,保安从对讲机或电脑中获知车位的情况,然后,发给等待停车位的车辆以停车卡,最后,更新车位的数量。这个过程在我们现实过程中是没有问题的,是我们认为的保证这个过程是原子的;若哪天保安犯了糊涂,发给停车卡后,忘了更新停车位的数量,那么最终一定会出现一个停车位多个车在抢的情况。而对应到内核中,准确的说应当是计算机体系结构中,唯一可以控制对一块内存的并发性访问的要点就是控制总线,所以,不同架构产生了不同的控制总线的指令,但其目的是相同的。
    好了,让我们再次回到小明那里,小明今天的运气还不错,没等几分钟,就获得了停车卡,径直驶向停车场,下一次还会遇到哪些与内核中相似的机制呢?敬请期待。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值