当前位置:  技术问答>linux和unix

互斥锁与信号量的深层探讨,请赐教!!!

    来源: 互联网  发布时间:2017-01-09

    本文导语:  最近在研究互斥锁与信号量,信号量还好理解,但是互斥锁的逻辑却有些乱,求人帮忙理一下。太乱了!!! 获得与释放互斥锁最重要的几段代码如下: //这是提供给驱动的接口 void __sched mutex_lock(struct mutex *lock) ...

最近在研究互斥锁与信号量,信号量还好理解,但是互斥锁的逻辑却有些乱,求人帮忙理一下。太乱了!!!
获得与释放互斥锁最重要的几段代码如下:

//这是提供给驱动的接口
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);
}

//这个函数会根据CPU所支持的ARM指令版本而选择不同的版本,以下为V5以下
//arm指令通用的函数,V5以上的在arch/arm目录下
static inline void
__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
if (unlikely(atomic_xchg(count, 0) != 1))
fail_fn(count);
}

static __used noinline void __sched
__mutex_lock_slowpath(atomic_t *lock_count)
{
struct mutex *lock = container_of(lock_count, struct mutex, count);

__mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0, _RET_IP_);
}

//以下为没有获得锁而处理的最核心的函数,看着多,其实不难
static inline int __sched
__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
        unsigned long ip)
{
struct task_struct *task = current;
struct mutex_waiter waiter;
unsigned long flags;

preempt_disable();
mutex_acquire(&lock->dep_map, subclass, 0, ip);
spin_lock_mutex(&lock->wait_lock, flags);

debug_mutex_lock_common(lock, &waiter);
debug_mutex_add_waiter(lock, &waiter, task_thread_info(task));

/* add waiting tasks to the end of the waitqueue (FIFO): */
list_add_tail(&waiter.list, &lock->wait_list);
waiter.task = task;

if (atomic_xchg(&lock->count, -1) == 1)
goto done;

lock_contended(&lock->dep_map, ip);

for (;;) {

if (atomic_xchg(&lock->count, -1) == 1)
break;

if (unlikely(signal_pending_state(state, task))) {
mutex_remove_waiter(lock, &waiter,
    task_thread_info(task));
mutex_release(&lock->dep_map, 1, ip);
spin_unlock_mutex(&lock->wait_lock, flags);

debug_mutex_free_waiter(&waiter);
preempt_enable();
return -EINTR;
}
__set_task_state(task, state);

                //printk(KERN_DEBUG "%s: before, lock_count: 0x%x, pid: %dn", __func__, 
                                //(int)lock->count.counter, task_tgid_vnr(current));
/* didnt get the lock, go to sleep: */
spin_unlock_mutex(&lock->wait_lock, flags);
preempt_enable_no_resched();
schedule();
preempt_disable();
                printk(KERN_DEBUG "%s: after, lock_count: 0x%x, pid: %dn", __func__, 
                                (int)lock->count.counter, task_tgid_vnr(current));
spin_lock_mutex(&lock->wait_lock, flags);
}

done:
        //printk(KERN_DEBUG "%s: lock_count: 0x%x, pid: %dn", __func__, 
                        //(int)lock->count.counter, task_tgid_vnr(current));
lock_acquired(&lock->dep_map, ip);
/* got the lock - rejoice! */
mutex_remove_waiter(lock, &waiter, current_thread_info());
mutex_set_owner(lock);

/* set it to 0 if there are no waiters left: */
if (likely(list_empty(&lock->wait_list)))
atomic_set(&lock->count, 0);

spin_unlock_mutex(&lock->wait_lock, flags);

debug_mutex_free_waiter(&waiter);
preempt_enable();

return 0;
}

//以下为释放锁的
void __sched mutex_unlock(struct mutex *lock)
{
__mutex_fastpath_unlock(&lock->count, __mutex_unlock_slowpath);
}

static inline void
__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
if (unlikely(atomic_xchg(count, 1) != 0))
fail_fn(count);
}

static __used noinline void
__mutex_unlock_slowpath(atomic_t *lock_count)
{
__mutex_unlock_common_slowpath(lock_count, 1);
}

static inline void
__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);

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);
                printk(KERN_DEBUG "%s: lock_count: 0x%x, pid: %dn", __func__, 
                                (int)lock->count.counter, task_tgid_vnr(current));
}
spin_unlock_mutex(&lock->wait_lock, flags);
}


互斥锁的实现原理和信号量是一样的,最根本的区别是互斥锁的计数变量是volitale的,而信号量却不是。双方都有一个等待队列。
若此时有a和b两个进程,a先获得锁,然后b也要获得锁,而因为a已经占有锁了,所以b会进入休眠。 当a释放锁后,会去唤醒在等待队列上的进程,按正常逻辑此时应该是b被唤醒而获得锁。但是现在却是,a在释放锁之后,又重新获得锁,a会在调度b之前先将计数值减1,然后才调度进程b,b会因为判断计数值不成功而重新进入休眠。为什么呢?为什么a会在调度b之前而先将计数值减1了呢?

而信号量却不是这样的,信号量是在a释放这个信号之后,唤醒等待队列上的进程,此时会马上调度b,使b获得信号量,若a又要重新获得信号量,会因为信号量的计数值小于等于0而进入休眠。

谁能帮我解释一下。

以下是我打印的log

/这是互斥锁的log
[   83.890838] i2c_transfer: get i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
[   83.890930] i2c_transfer: get i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x0, len: 1
[   83.890960] __mutex_lock_common: before, lock_count: 0xffffffff, pid: 1041
[   83.891143] __mutex_unlock_common_slowpath: lock_count: 0x1, pid: 206
[   83.891174] i2c_transfer: release i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
[   83.891174] 
[   83.891204] i2c_transfer: get i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
[   83.891235] __mutex_lock_common: after, lock_count: 0x0, pid: 1041
[   83.891235] __mutex_lock_common: before, lock_count: 0xffffffff, pid: 1041
[   83.891510] __mutex_unlock_common_slowpath: lock_count: 0x1, pid: 206
[   83.891510] i2c_transfer: release i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
[   83.891876] 
[   83.891876] i2c_transfer: get i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
[   83.891906] __mutex_lock_common: after, lock_count: 0x0, pid: 1041
[   83.891937] __mutex_lock_common: before, lock_count: 0xffffffff, pid: 1041
[   83.895751] __mutex_unlock_common_slowpath: lock_count: 0x1, pid: 206
[   83.895751] i2c_transfer: release i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
.........
[   83.904693] i2c_transfer: get i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 2
[   83.904693] __mutex_lock_common: after, lock_count: 0x0, pid: 1041
[   83.904724] __mutex_lock_common: before, lock_count: 0xffffffff, pid: 1041
[   83.904876] __mutex_unlock_common_slowpath: lock_count: 0x1, pid: 206
[   83.904876] i2c_transfer: release i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 2
[   83.904907] 
[   83.904937] __mutex_lock_common: after, lock_count: 0x1, pid: 1041
[   83.904937] __mutex_lock_common: lock_count: 0xffffffff, pid: 1041
[   83.905090] i2c_transfer: release i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x0, len: 1

//以下是信号量的log
[   53.400207] i2c_transfer: get i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x0, len: 1
[   53.400329] i2c_transfer: get i2c lock, slave->addr: 0x44, cur_pid: 5, flags: 0x0, len: 1
[   53.400360] __down_common: before count: 0x0, pid: 5, up: 0
[   53.400390] __up: count: 0x0, pid: 1041, up: 1
[   53.400390] i2c_transfer: release i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x0, len: 1
[   53.400421] 
[   53.400421] i2c_transfer: get i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x1, len: 32
[   53.400451] __down_common: before count: 0x0, pid: 1041, up: 0
[   53.400451] __down_common: after count: 0x0, pid: 5, up: 1
[   53.400695] __up: count: 0x0, pid: 5, up: 1
[   53.400726] i2c_transfer: release i2c lock, slave->addr: 0x44, cur_pid: 5, flags: 0x0, len: 1
[   53.400726] 
[   53.400726] i2c_transfer: get i2c lock, slave->addr: 0x44, cur_pid: 5, flags: 0x0, len: 1
[   53.400756] __down_common: before count: 0x0, pid: 5, up: 0
[   53.400756] __down_common: after count: 0x0, pid: 1041, up: 1
[   53.402404] __up: count: 0x0, pid: 1041, up: 1
[   53.402435] i2c_transfer: release i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x1, len: 32
[   53.402435] 
[   53.402465] i2c_transfer: get i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x0, len: 2
[   53.402496] __down_common: before count: 0x0, pid: 1041, up: 0
[   53.402557] __down_common: after count: 0x0, pid: 5, up: 1
[   53.402832] __up: count: 0x0, pid: 5, up: 1
[   53.402862] i2c_transfer: release i2c lock, slave->addr: 0x44, cur_pid: 5, flags: 0x0, len: 1
[   53.402862] 
[   53.402862] i2c_transfer: get i2c lock, slave->addr: 0x44, cur_pid: 5, flags: 0x0, len: 1
[   53.402893] __down_common: before count: 0x0, pid: 5, up: 0
[   53.402923] __down_common: after count: 0x0, pid: 1041, up: 1
[   53.403106] __up: count: 0x0, pid: 1041, up: 1
[   53.403106] i2c_transfer: release i2c lock, slave->addr: 0x67, cur_pid: 1041, flags: 0x0, len: 2
[   53.403137] 
[   53.404083] __down_common: after count: 0x0, pid: 5, up: 1
[   53.404479] i2c_transfer: release i2c lock, slave->addr: 0x44, cur_pid: 5, flags: 0x0, len: 1



|
按照93楼的解释,互斥锁的交互性仍然很高啊,你遇到的问题是 __mutex_unlock_common_slowpath 与 up 的差异导致的。

信号量计数机制应该比互斥锁更复杂一些,所以相对而言,还是互斥锁效率高些吧。


|
互斥锁为什么是这样的特性呢?
那就不能实现a、b两个进程交替访问了

|
如果a在解锁之后,不是立即再加锁,比如sleep一小会
那么b貌似就能抢到锁了

|
同步机制是相对于线程而言的。。。

|
唯一能明白的就是和通信相关,以现在的能力也只能明白到这了。

|
受益非浅,前段出用过,最后选择了信号量,看来还是明智啊,不然也会遇到这样的问题 。

|
互锁应该可以用信号量实现吧!!!

|
看你的mutex的log似乎有问题。

注意一点:mutex跟semaphore重要的区别就是 ownership,
对于mutex,只有获得mutex的进程才能释放该mutex,如果其他的进程调用mutex_unlock,是不能释放的!
检查一下你使用mutex的代码。

|
按理说系统会公平的选择等待的线程获得互斥量的所有权的,这个信号量和互斥量应该是一样的,还是查查是否由于其他原因导致的。

|

这期间应该是在内核态与用户态之间切换了100次吧
而不是一直处于内核态

|


我觉得在mutex_unlock后,若发生了调度,等待队列上的进程就可能得到该锁了。

|
个人浅见:
互斥是信号量的一个特例,互斥量在连续unlock后,只要有一个线程lock,其他的线程就不能lock成功了,而信号量不一样,可以在signal两次后,连续调用两次wait,并能成功。

互斥资源同时只有一个线程能使用,信号量能控制同时使用资源的线程的数量。

|
信号量一般用于多线程/进程的同步,互斥锁一般用于锁定临界资源的吧。
就定义来说是没有先后顺序的吧。

|
[   83.890960] __mutex_lock_common: before, lock_count: 0xffffffff, pid: 1041
[   83.891143] __mutex_unlock_common_slowpath: lock_count: 0x1, pid: 206
[   83.891174] i2c_transfer: release i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
[   83.891174] 
[   83.891204] i2c_transfer: get i2c lock, slave->addr: 0x34, cur_pid: 206, flags: 0x0, len: 1
[   83.891235] __mutex_lock_common: after, lock_count: 0x0, pid: 1041
[   83.891235] __mutex_lock_common: before, lock_count: 0xffffffff, pid: 1041
[   83.891510] __mutex_unlock_common_slowpath: lock_count: 0x1, pid: 206

看起来,你只在pid 1041里调用了mutex_lock (没调用mutex_unlock),在pid 206里调用了mutex_unlcok,这是你的本意吗?

|
好像把问题复杂化了吧
信号量 : 生产者up,消费者down,没人up,消费者就等待
互斥锁 : 谁想要访问资源,谁去锁资源,如果有人已经锁住了资源,后续的申请者就只有等待拿着锁的人解锁

线程or进程 : 至少对互斥锁来说,如果要进程间使用,把它放在系统的共享内存区就可以了


我以前的代码有用信号量的,后来发现信号量把逻辑搞复杂了,于是就换成锁了,效果还行

|

代码太长没有认真一句一句的看。现在再看看的话确实是FIFO的队列。
重看了下互斥锁与信号量的定义,确实是有先后顺序的。
那造成你互斥锁的LOG不按先后顺序进行可能是加锁策略造成的,互斥锁加锁是直接加锁而不会先把其放到等待队列中。
而信号量的加锁是会先根据信号量判断是否需要等待,也就是会先查看等待队列中是否进程/线程在等待。

|
任务调度似乎还有优先级自动调整,不能让一个任务不停占有资源的

|
93楼正解。sem的up操作中首先检查是否有任务在down队列,如果有则交给最应该给任务,其余继续等待。
mutex的unlock则不同,直接释放。 

|
对的。

如果没有进程等待的话,行为应该是一样的。
如果有进程等待的时候,不一样。mutex:想要获取mutex的进程,可以“插队”,
semaphore:想要获取semaphore的进程,必须排在队伍的后面,不能“插队”。

    
 
 
 
本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • c++的boost库多线程(Thread)编程(线程操作,互斥体mutex,条件变量)详解
  • 互斥锁的一个问题
  • 在下面这段代码中,如果对init方法只加synchronized关键字,则不能达到互斥的目的,还要再加上static关键字才能互斥,为什么?
  • wince程序防止创建多个实例实现互斥作用
  • linux进程同步或互斥
  • 进程对串口读写时使用互斥机制吗?
  • 在linux下,如何进行“互斥”和“并发”的控制?
  • 条件变量和互斥量区别
  • 多线程调用ioctl 应在哪进行互斥操作?
  • 关于用文件实现进程互斥的问题
  • 问个多线程网络服务程序的文件操作符的互斥问题
  • 请教POSIX问题:一个进程中,可以有多个互斥锁麽?
  • 条件变量是不是一定要搭配互斥锁才能发挥作用?
  • 线程同步读取变量可不可以不用互斥锁
  • linux/unix里的进程互斥问题 ,有关lockf()函数!
  • 关于内核互斥体的问题
  • 关于文件读些互斥的问题
  • c语言实现程序互斥问题 急.....
  • 求助 程序中添加互斥锁代码后编译怎么通不过 ?
  • 请问Unix下的进程互斥是用什么实现的?
  • 管程如何实现互斥


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3