pthread_mutex_t 有3种类型, fast, recursive, error checking
recursive运行递归调用lock, error checking立即返回死锁错误
fast比较常见,默认的锁是fast的,其它两种其实可以不需要,自己可以知道自己是否持有锁
锁可以是多线程之间共享(private),或者进程间,默认是private,这影响futex的处理方式
mutex的核心是个lock字段,该值为0代表锁是解开的,1标示被人占用,2表示被人占用的同时又有
线程试图加锁而去睡眠
如果锁竞争不激烈, 即较少碰到锁被占用
那么pthread_mutex_lock,只需要原子的执行一条cmpxchg类似的指令,把lock设置为1并且发现其原来
的值是0, 就成功了
这种情况下不需要睡眠,不需要进行futex系统调用
只有需要睡眠(锁竞争),才用futex通知内核
通过lock; cmpxchg这样的指令原子的检查lock 字段的值, 如果
Compare EAX with r/m32. If
equal, ZF is set and r32 is
loaded into r/m32. Else,
clear ZF and load r/m32 into
EAX.
-
-
#define lll_lock(futex) \
-
(void) \
-
({ int ignore1, ignore2; \
-
__asm __volatile (
- "cmpl $0, %%gs:%P6\n\t" \
-
"je 0f\n\t" \
-
"lock\n" \
-
"0:\tcmpxchgl %1, %2\n\t"
-
"jnz _L_lock_%=\n\t" \
-
".subsection 1\n\t" \
-
".type _L_lock_%=,@function\n" \
-
"_L_lock_%=:\n" \
-
"1:\tleal %2, %%ecx\n" \
-
"2:\tcall __lll_lock_wait_private\n" \
-
"3:\tjmp 18f\n" \
-
"4:\t.size _L_lock_%=, 4b-1b\n\t" \
-
".previous\n" \
-
LLL_STUB_UNWIND_INFO_3 \
-
"18:" \
-
: "=a" (ignore1), "=c" (ignore2), "=m" (futex) \
-
: "0" (0), "1" (1), "m" (futex), \
-
"i" (MULTIPLE_THREADS_OFFSET) \
-
: "memory"); \
pthread_mutex_lock 简化之后的就是这样
如果没有竞争
-
"jnz _L_lock_%=\n\t"
这条指令就不会执行,这段代码就结束了
否则调用 __lll_lock_wait_private , 该函数把lock设置为2,调用futex睡眠
该函数在调用futex睡眠之前锁有可能被释放了,所以futex的val参数为2,保证如果不是2就不会真正睡眠
否则永远将不会被唤醒了
futex的实现检查的lock是否等于2和被唤醒的动作是互斥的
kernel/futex.c
ret = get_futex_value_locked(&uval, uaddr);
if (uval != val) {
表明正想睡眠的时候锁却被解开了
queue_unlock(q, *hb);
ret = -EWOULDBLOCK;
}
pthread_mutex_unlock类似,如果发现原来的值是1,就不需要唤醒了,否则执行唤醒一个线程
如果被锁保护的代码不会阻塞,比较快能执行完,pthread_spin_lock 或许是一个不错的选择
阅读(5299) | 评论(0) | 转发(0) |