linux中mutex和semaphore的区别
来源:互联网 发布:mac finder打不开 编辑:程序博客网 时间:2024/04/30 13:09
很多编程的书里在介绍mutex和semaphore的时候都会说,mutex是一种特殊的semaphore.
当semaphore的N=1时,就变成了binary semaphore,也就等同与mutex了。
但是实际上,在linux中,他们的实现什有区别的,导致最后应用的行为也是有区别的。
先看下面这个例子,这是一段linux kernel的代码:
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/semaphore.h>
- #include <linux/sched.h>
- #include <linux/delay.h>
- static DEFINE_MUTEX(g_mutex);
- static DEFINE_SEMAPHORE(g_semaphore);
- static int fun1(void *p)
- {
- while (true) {
- mutex_lock(&g_mutex);
- msleep(1000);
- printk("1\n");
- mutex_unlock(&g_mutex);
- }
- return 0;
- }
- static int fun2(void *p)
- {
- while (true) {
- mutex_lock(&g_mutex);
- msleep(1000);
- printk("2\n");
- mutex_unlock(&g_mutex);
- }
- return 0;
- }
- static int fun3(void *p)
- {
- while (true) {
- down(&g_semaphore);
- msleep(1000);
- printk("3\n");
- up(&g_semaphore);
- }
- return 0;
- }
- static int fun4(void *p)
- {
- while (true) {
- down(&g_semaphore);
- msleep(1000);
- printk("4\n");
- up(&g_semaphore);
- }
- return 0;
- }
- static int hello_init(void)
- {
- kernel_thread(fun1, NULL, 0);
- kernel_thread(fun2, NULL, 0);
- kernel_thread(fun3, NULL, 0);
- kernel_thread(fun4, NULL, 0);
- return 0;
- }
- module_init(hello_init);
这段代码很简单,4个线程,2个去获取mutex,2个去获取semaphore。
我们可以先只enable thread1和thread2。
我开始预期的结果什输出1212121212...
但是实际的结果是111111111...22222222222...11111111111...
假设enable thread3和thread4,输出则变成了34343434343434...
显然,mutex和semaphore使用的结果不一样。
为什么会造成这样的结果哪?
我们分析一下kernel里mutex和semaphore的实现就可以明白了
kernel/mutex.c
- __mutex_unlock_common_slowpath(atomic_t *lock_count, int nested)
- {
- struct mutex *lock = container_of(lock_count, struct mutex, count);
- unsigned long flags;
- spin_lock_mutex(&lock->wait_lock, flags);
- mutex_release(&lock->dep_map, nested, _RET_IP_);
- debug_mutex_unlock(lock);
- /*
- * some architectures leave the lock unlocked in the fastpath failure
- * case, others need to leave it locked. In the later case we have to
- * unlock it here
- */
- if (__mutex_slowpath_needs_to_unlock())
- atomic_set(&lock->count, 1);
- if (!list_empty(&lock->wait_list)) {
- /* get the first entry from the wait-list: */
- struct mutex_waiter *waiter =
- list_entry(lock->wait_list.next,
- struct mutex_waiter, list);
- debug_mutex_wake_waiter(lock, waiter);
- wake_up_process(waiter->task);
- }
- spin_unlock_mutex(&lock->wait_lock, flags);
- }
然后从等待这个mutex的队列里取出第一个任务,并wake_up这个任务。
这里要注意,wake_up_process只是把这个任务设置成可调度,并不是直接就进行调度了。
所以当一个线程unlock mutex之后,只要在自己还没有被调度出去之前再次很快的lock mutex的话,他依旧会成功。
于是,这就出现了一开始那个程序的结果。
在代码本身没有死锁的情况下,不合适得使用mutex,会造成饥饿的发生。
那semaphore到底是如何避免这样的情况发生的哪?
kernel/semaphore.c
- void down(struct semaphore *sem)
- {
- unsigned long flags;
- raw_spin_lock_irqsave(&sem->lock, flags);
- if (likely(sem->count > 0))
- sem->count--;
- else
- __down(sem);
- raw_spin_unlock_irqrestore(&sem->lock, flags);
- }
- static inline int __sched __down_common(struct semaphore *sem, long state,
- long timeout)
- {
- struct task_struct *task = current;
- struct semaphore_waiter waiter;
- list_add_tail(&waiter.list, &sem->wait_list);
- waiter.task = task;
- waiter.up = false;
- for (;;) {
- if (signal_pending_state(state, task))
- goto interrupted;
- if (unlikely(timeout <= 0))
- goto timed_out;
- __set_task_state(task, state);
- raw_spin_unlock_irq(&sem->lock);
- timeout = schedule_timeout(timeout);
- raw_spin_lock_irq(&sem->lock);
- if (waiter.up)
- return 0;
- }
- timed_out:
- list_del(&waiter.list);
- return -ETIME;
- interrupted:
- list_del(&waiter.list);
- return -EINTR;
- }
- void up(struct semaphore *sem)
- {
- unsigned long flags;
- raw_spin_lock_irqsave(&sem->lock, flags);
- if (likely(list_empty(&sem->wait_list)))
- sem->count++;
- else
- __up(sem);
- raw_spin_unlock_irqrestore(&sem->lock, flags);
- }
- static noinline void __sched __up(struct semaphore *sem)
- {
- struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
- struct semaphore_waiter, list);
- list_del(&waiter->list);
- waiter->up = true;
- wake_up_process(waiter->task);
- }
这就好比第一个线程去down semaphore。
等到第二个线程再去down的时候,count为0了,就进入了__down_common函数。
这个函数里面会一直检查waiter.up,知道为true了才会退出。
至此,第二个线程就被block在了down函数里。
等到第一个线程up semaphore,假如这个semaphore的等待队列里还有任务,则设置waiter->up为true并唤醒任务。
这里用的也是wake_up_process,貌似和mutex的实现什一样的,但是接下来就不一样了。
假设第一个线程之后又很快的去down semaphore,会发生什么哪?
由于sem->count还是为0,这个线程在down的时候就会被block住而发生调度。
第二个线程此时就可以获得semaphore而继续执行代码了。
一直直到没有任何线程在等待队列里了,sem->count才会被++。
所以,semaphore就变成了这样的行为。
总结:mutex在使用时没有任何顺序的保证,它仅仅是保护了资源,但是效率会比较高。
而semaphore则有顺序的保证,使得每个使用者都能依次获得他,但是相应的会损失一点效率。
- linux中mutex和semaphore的区别
- linux中mutex和semaphore的区别
- mutex和semaphore的区别
- mutex和Semaphore的区别
- linux mutex互斥体和semaphore信号量的区别
- 多线程中Semaphore,mutex和lock的区别
- Mutex和Semaphore区别
- Mutex和Semaphore区别
- Semaphore 和 Mutex 的区别 [No. 32]
- mutex与semaphore的区别
- mutex与semaphore的区别
- mutex与semaphore的区别
- mutex与semaphore的区别
- mutex与semaphore的区别
- Semaphore和Mutex -- Linux下的互斥机制分析
- 互斥锁Mutex与信号量Semaphore的区别
- 互斥锁Mutex与信号量Semaphore的区别
- mutex和semaphore不为人知的秘密
- flex中使用RadioButtonGroup时如何取出所选项的值
- 通过winscp实现一键修改多台机器文件,并执行相应命令的脚本
- poj 2823单调队列模板题
- 第十六周项目二-简单指针
- xFace3.x 开发技巧(1)---从web开发转向移动开发
- linux中mutex和semaphore的区别
- 正在看的一些书(值得推荐)
- Spring3.2.4 + Quartz2.2.0集成实例
- hive转义字符
- windows vc下实现图片透明
- spring 任务调度框架Quartz
- jQuery 获取手机屏幕高度、宽度
- vs2008编译时出现问题解决方法:MSVCRTD.lib(crtexew.obj) : error LNK2019: unresolved external symbol _WinMain@16 r
- Windows窗口属性和拓展属性