分类: LINUX
2010-10-14 17:05:28
来自:linux那些事我是USB
网名为卖血上网的哥们说话了,那么到底什么是信号量?或者什么是互斥锁?
先说互斥锁.它诞生于这样一个背景.这个世界上,有些东西只能属于某一个人,或者说在一个时间里只能属于一个人,这你承认吧,比如一个女孩的心.当你要追求一个女孩时,你首先会去了解其人是否名花有主,若是否,你才会去追求,若已然有主,那么你只能放弃,或者准确的说,你只能等待.当然你可以很激昂的说,如果等待可以换来奇迹的话,我宁愿等下去,哪怕一年,抑或一生!然而,她爱的是他,终究不是你,所以你伤悲,流泪,却打不开她心中那把锁.这里你应该就能感觉到什么是互斥锁了,一个女孩如果心有所属,那么对你来说,就仿佛已有人在你之前给她上了一把锁,而钥匙,不在你这里.那么你问代码中为什么要锁?Ok,我告诉你,如果你正在操作一个队列,比如一个队列一百个元素,你想把第七十个读出来,于是你去遍历这个队列,可是如果没有锁,那么可能你遍历的时候别人也可以操作这个队列,比如你马上就要找到第七十个了,可是,可是,注意了,可是,这个时候,说不定哪位哥们儿缺德,把第七十个数给删除了,那你不是白忙活了?所以,怎么办?设一个锁,每个人要想操作这个队列就得先获得这把锁,而一旦你获得了这把锁,你在操作这把锁期间,别人就不能操作,因为他要操作他就得先获得锁,而这个时候锁在你这里,所以他只能等待,等你结束了,释放了锁,他才可以去操作.那么互斥锁指的就是这种情况,一个资源只能同时被一个进程操作,互斥的字面意思也正是如此.互相排斥,就像爱情是自私的一样.
那什么是信号量?信号量和互斥锁略有不同.它允许一个时间里有几个进程同时访问一段资源.到底允许几个可以设定,这称为设定信号量的初值,如设定为1,那就说明是同一时间只有一个进程可以访问,那么这就是互斥锁了,不过有的时候一个资源确实可以让几个进程访问,特别是读访问,你想一个文件可以被两个进程同时读,这不要紧吧,各读各的,谁也影响不了谁,只要大家都不写就是了.设定信号量的初值,比如设定5,那么就是说,同一时间你就让最多5 个进程同时去读这个文件.每个进程获取了信号量就把信号量的值减一,到第六个进程了,它去判断,发现值已经等于0 了,于是它不能访问了,只能等待,等待别的进程释放锁.
不过也许,一把钥匙只能开一把锁,更能代表爱情的专一.所以实际上,Linux 内核代码中锁用得更多,而初值不为1 的信号量用得相对来说不多.比如我们刚才看到的这两个信号量,都是属于当作互斥锁在用.因为他们的初值一个被设置成了1(init_MUTEX),一个被设置成了0(init_MUTEX_LOCKED).设置成一很好理解,就是一把互斥锁,只能容许一个进程去获得它,设置成0 呢,就表示这把锁已经被锁住了,得有进程释放它才可以.我们这里767 行这句down(&us->dev_semaphore)和up(&us->dev_semaphore)是一对,一个是获得锁,一个是释放锁.它们就是为了保护中间那段代码.我刚才说我不想讲这段代码,的确,现在讲为什么这里要用锁还为时过早,整个故事中us->dev_semaphore 出现的次数不是很多,但是我们必须对整个代码都熟悉了才可能理解为什么要用这把锁,因为这些代码都是环环相扣的,不能孤立来看.所以,我们将在故事的收尾阶段来一次性来以高屋建瓴的方式看每一处down(&us->dev_semaphore)和up(&us->dev_semaphore)的使用原因.接下来我们看到了这把锁的时候,也将一并跳过不提.到最后再来看.需要说的是这里down 和up 这两个函数的作用分别就是去获得锁和释放锁.对于down 来说,它每次判断一下信号量的值是否大于0,若是,就进入下面的代码,同时将信号量的值减一,若否,就等待,或者说专业一点,进入睡眠.对于down(),我们小时候那部<<挥剑问情>>的歌词很形象的描述了其行为,
男:挥剑问情,如果问是有情,也愿以身相许,以身相殉;
女:挥剑问情,如果问是无情,又怕回首别是一种伤心.
不过,我们这里看到了两把锁,除了us->dev_semaphore 以外,另一个是us->sema,现在还没有使用它,但是我们可以先说一下,us->sema 在整个故事中出现的次数不多,总共只有三次.加上这里提到的这句初始化为0 的语句,一个出现了四次.所以我们在遇到它的时候不需要跳过,会详细的讲.因为很容易理解它为什么会用在那里.请你记住,这个信号量或者说这把锁是被初始化为0了,不是1,它一开始就处于锁住的状态,到时候你就会知道为什么了.我们边走边看.另外一个需要说一下的是,与down ()相似的有一个叫做down_interruptible ()的函数,它们的区别在于后者可被信号打断,而前者不可被信号打断,而一旦问是无情,那么他们将进入等待,或者专业一点说,进入睡眠,直到某一天...
我们将看到的获取us->sema 的函数正是down_interruptible.而释放锁的函数仍然可以用up.