admin 管理员组文章数量: 1087649
NPTL:lll
NPTL 库中 lll_lock() 和 lll_unlock() 的实现用到了:
-
sys_futex() 使用了 FUTEX_WAIT 操作,使得调用者线程被挂起;使用 FUTEX_WAKE 操作来恢复之前被挂起的线程
-
使用了 GCC 内置的原子性操作函数,__sync_lock_test_and_set()、__sync_synchronize()
-
CAS 的实现借助了 GCC 内置的原子性操作函数 __sync_bool_compare_and_swap()
总结
-
使用系统调用 sys_futex() 来完成调用者线程的挂起和恢复
-
使用 GCC 内置的原子性操作函数,来完成对锁 futex 的同步访问(读/写)
关于 GCC 内置的原子性操作函数,参考 .1.2/gcc/Atomic-Builtins.html#Atomic-Builtins
测试程序,这是可以编译并测试的程序,测试平台 x86_64 64位,代码是从 NPTL 源码中整理出来的,基于 x86_64 64位进行了整理和修改。
/* For test */
/* 测试平台 x86_64 64位 */
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <x86_64-linux-gnu/asm/unistd_64.h>
#include <sys/prctl.h>
/* For test end */#define LLL_PRIVATE 0
#define LLL_SHARED 128#define FUTEX_WAIT 0
#define FUTEX_WAKE 1/*
#define __glibc_unlikely(cond) __builtin_except((cond), 0)
#define __glibc_likely(cond) __builtin_except((cond), 1)
#define __builtin_except(expr, val) (expr)
*//* GCC 内置函数 __sync_synchronize() 和 __sync_lock_test_and_set() */
#define atomic_exchange_rel(mem, value) \(__sync_synchronize (), __sync_lock_test_and_set (mem, value))/* Unconditionally set FUTEX to 0 (not acquired), releasing the lock. If FUTEXwas >1 (acquired, possibly with waiters), then wake any waiters. The waiterthat acquires the lock will set FUTEX to >1. */
#define __lll_unlock(futex, private) \((void) \({ \int *__futex = (futex); \int __oldval = atomic_exchange_rel (__futex, 0); \if (__glibc_unlikely (__oldval > 1)) \lll_futex_wake (__futex, 1, private); \}))#define lll_unlock(futex, private) \__lll_unlock (&(futex), private)/* CAS */
#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \(!__sync_bool_compare_and_swap(mem, oldval, newval))#define lll_lock(futex, private) \__lll_lock(&(futex), private)/* This is an expression rather than a statement even though its value isvoid, so that it can be used in a comma expression or as an expressionthat's cast to void. */
/* The inner conditional compiles to a call to __lll_lock_wait_private ifprivate is known at compile time to be LLL_PRIVATE, and to a call to__lll_lock_wait otherwise. *//* If FUTEX is 0 (not acquired), set to 1 (acquired with no waiters) andreturn. Otherwise, ensure that it is >1 (acquired, possibly with waiters)and then block until we acquire the lock, at which point FUTEX will still be>1. The lock is always acquired on return. */#define __lll_lock(futex, private) \
((void) \({ \int *__futex = (futex); \if (__glibc_unlikely \(atomic_compare_and_exchange_bool_acq(__futex, 1, 0))) \{ \if (__builtin_constant_p(private) && (private) == LLL_PRIVATE) \__lll_lock_wait_private(__futex); \else \__lll_lock_wait(__futex, private); \} \}) \
) \/* 1 if 'type' is a pointer type, 0 otherwise. */
# define __pointer_type(type) (__builtin_classify_type ((type) 0) == 5)
/* __intptr_t if P is true, or T if P is false. */
# define __integer_if_pointer_type_sub(T, P) \__typeof__ (*(0 ? (__typeof__ (0 ? (T *) 0 : (void *) (P))) 0 \: (__typeof__ (0 ? (__intptr_t *) 0 : (void *) (!(P)))) 0))/* __intptr_t if EXPR has a pointer type, or the type of EXPR otherwise. */
# define __integer_if_pointer_type(expr) \__integer_if_pointer_type_sub(__typeof__ ((__typeof__ (expr)) 0), \__pointer_type (__typeof__ (expr)))/* Cast an integer or a pointer VAL to integer with proper type. */
#define cast_to_integer(val) ((__integer_if_pointer_type (val)) (val))/* x86_64 */
/* 使用 xchg 指令实现原子性交换 */
#define atomic_exchange_acq(mem, newvalue) \({ __typeof (*mem) result; \if (sizeof (*mem) == 1) \__asm __volatile ("xchgb %b0, %1" \: "=q" (result), "=m" (*mem) \: "0" (newvalue), "m" (*mem)); \else if (sizeof (*mem) == 2) \__asm __volatile ("xchgw %w0, %1" \: "=r" (result), "=m" (*mem) \: "0" (newvalue), "m" (*mem)); \else if (sizeof (*mem) == 4) \__asm __volatile ("xchgl %0, %1" \: "=r" (result), "=m" (*mem) \: "0" (newvalue), "m" (*mem)); \else \__asm __volatile ("xchgq %q0, %1" \: "=r" (result), "=m" (*mem) \: "0" ((int64_t) cast_to_integer (newvalue)), \"m" (*mem)); \result; })/* x86_64 */
# define LOAD_ARGS_0()
# define LOAD_REGS_0
# define ASM_ARGS_0# define LOAD_ARGS_TYPES_1(t1, a1) \t1 __arg1 = (t1) (a1); \LOAD_ARGS_0 ()
# define LOAD_REGS_TYPES_1(t1, a1) \register t1 _a1 asm ("rdi") = __arg1; \LOAD_REGS_0
# define ASM_ARGS_1 ASM_ARGS_0, "r" (_a1)
# define LOAD_ARGS_1(a1) \LOAD_ARGS_TYPES_1 (long int, a1)
# define LOAD_REGS_1 \LOAD_REGS_TYPES_1 (long int, a1)
# define LOAD_ARGS_TYPES_2(t1, a1, t2, a2) \t2 __arg2 = (t2) (a2); \LOAD_ARGS_TYPES_1 (t1, a1)
# define LOAD_REGS_TYPES_2(t1, a1, t2, a2) \register t2 _a2 asm ("rsi") = __arg2; \LOAD_REGS_TYPES_1(t1, a1)
# define ASM_ARGS_2 ASM_ARGS_1, "r" (_a2)
# define LOAD_ARGS_2(a1, a2) \LOAD_ARGS_TYPES_2 (long int, a1, long int, a2)
# define LOAD_REGS_2 \LOAD_REGS_TYPES_2 (long int, a1, long int, a2)
# define LOAD_ARGS_TYPES_3(t1, a1, t2, a2, t3, a3) \t3 __arg3 = (t3) (a3); \LOAD_ARGS_TYPES_2 (t1, a1, t2, a2)
# define LOAD_REGS_TYPES_3(t1, a1, t2, a2, t3, a3) \register t3 _a3 asm ("rdx") = __arg3; \LOAD_REGS_TYPES_2(t1, a1, t2, a2)
# define ASM_ARGS_3 ASM_ARGS_2, "r" (_a3)
# define LOAD_ARGS_3(a1, a2, a3) \LOAD_ARGS_TYPES_3 (long int, a1, long int, a2, long int, a3)
# define LOAD_REGS_3 \LOAD_REGS_TYPES_3 (long int, a1, long int, a2, long int, a3)
# define LOAD_ARGS_TYPES_4(t1, a1, t2, a2, t3, a3, t4, a4) \t4 __arg4 = (t4) (a4); \LOAD_ARGS_TYPES_3 (t1, a1, t2, a2, t3, a3)
# define LOAD_REGS_TYPES_4(t1, a1, t2, a2, t3, a3, t4, a4) \register t4 _a4 asm ("r10") = __arg4; \LOAD_REGS_TYPES_3(t1, a2, t2, a2, t3, a3)
# define ASM_ARGS_4 ASM_ARGS_3, "r" (_a4)
# define LOAD_ARGS_4(a1, a2, a3, a4) \LOAD_ARGS_TYPES_4 (long int, a1, long int, a2, long int, a3, \long int, a4)# define LOAD_REGS_4 \LOAD_REGS_TYPES_4 (long int, a1, long int, a2, long int, a3, \long int, a4)
# define LOAD_ARGS_TYPES_5(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \t5 __arg5 = (t5) (a5); \LOAD_ARGS_TYPES_4 (t1, a1, t2, a2, t3, a3, t4, a4)
# define LOAD_REGS_TYPES_5(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \register t5 _a5 asm ("r8") = __arg5; \LOAD_REGS_TYPES_4 (t1, a1, t2, a2, t3, a3, t4, a4)
# define ASM_ARGS_5 ASM_ARGS_4, "r" (_a5)
# define LOAD_ARGS_5(a1, a2, a3, a4, a5) \LOAD_ARGS_TYPES_5 (long int, a1, long int, a2, long int, a3, \long int, a4, long int, a5)
# define LOAD_REGS_5 \LOAD_REGS_TYPES_5 (long int, a1, long int, a2, long int, a3, \long int, a4, long int, a5)
# define LOAD_ARGS_TYPES_6(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) \t6 __arg6 = (t6) (a6); \LOAD_ARGS_TYPES_5 (t1, a1, t2, a2, t3, a3, t4, a4, t5, a5)
# define LOAD_REGS_TYPES_6(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) \register t6 _a6 asm ("r9") = __arg6; \LOAD_REGS_TYPES_5 (t1, a1, t2, a2, t3, a3, t4, a4, t5, a5)
# define ASM_ARGS_6 ASM_ARGS_5, "r" (_a6)
# define LOAD_ARGS_6(a1, a2, a3, a4, a5, a6) \LOAD_ARGS_TYPES_6 (long int, a1, long int, a2, long int, a3, \long int, a4, long int, a5, long int, a6)
# define LOAD_REGS_6 \LOAD_REGS_TYPES_6 (long int, a1, long int, a2, long int, a3, \long int, a4, long int, a5, long int, a6)
#define INTERNAL_SYSCALL(name, err, nr, args...) \INTERNAL_SYSCALL_NCS(__NR_##name, err, nr, ##args)/* x86_64 */
#define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \({ \unsigned long int resultvar; \LOAD_ARGS_##nr (args) \LOAD_REGS_##nr \asm volatile ( \"syscall\n\t" \: "=a" (resultvar) \: "0" (name) ASM_ARGS_##nr \: "memory", "cc", "r11", "cx" \); \(long int) resultvar; \})#define INTERNAL_SYSCALL_DECL(err)
/* Wait while *FUTEXP == VAL for an lll_futex_wake call on FUTEXP. */
#define lll_futex_wait(futexp, val, private) \lll_futex_timed_wait(futexp, val, NULL, private)
/* INTERNAL_SYSCALL 将执行系统调用 sys_futex() */
#define lll_futex_timed_wait(futexp, val, timespec, private) \({ \INTERNAL_SYSCALL_DECL(__err); \long int __ret; \__ret = INTERNAL_SYSCALL(futex, __err, 4, \(futexp), FUTEX_WAIT, (val), (timespec)); \__ret; \})#define lll_futex_wake(futexp, nr, private) \({ \INTERNAL_SYSCALL_DECL (__err); \long int __ret; \__ret = INTERNAL_SYSCALL (futex, __err, 4, \(futexp), FUTEX_WAKE, (nr), 0); \__ret; \})void __lll_lock_wait_private (int *futex)
{if (*futex == 2)lll_futex_wait (futex, 2, LLL_PRIVATE); /* Wait if *futex == 2. *//* 功能:将 *futex 设置为 2 并返回 *futex 的原来的值。所以如果其它线程已经释放了 futex,也就是已经把 *futex设置为 0 的话,此时 atomic_exchange_acq() 的调用将会把*futex 设置为 2 并返回 0 退出 while,调用线程获得 futex */while (atomic_exchange_acq (futex, 2) != 0)lll_futex_wait (futex, 2, LLL_PRIVATE); /* Wait if *futex == 2. */
}/* These functions don't get included in libc.so */
/* 不会用到该函数 */
void __lll_lock_wait (int *futex, int private)
{if (*futex == 2)lll_futex_wait (futex, 2, private); /* Wait if *futex == 2. */while (atomic_exchange_acq (futex, 2) != 0)lll_futex_wait (futex, 2, private); /* Wait if *futex == 2. */
}/* Test */
#define TASK_NUM 20
#define COUNT 100
static int sum = 0;
static int futex = 0;
static int go = 0;static void *fn(void *arg)
{int i;static int n = 'A';char name[2] = {'\0'};name[0] = (char) (n+(unsigned long int)arg);prctl(PR_SET_NAME, name);while (!go);for (i=0; i<COUNT; i++) { lll_lock(futex, LLL_PRIVATE); sum++; lll_unlock(futex, LLL_PRIVATE);}return NULL;
}int main(int argc, char **argv)
{pthread_t task[TASK_NUM];unsigned long int i;for (i=0; i<TASK_NUM; i++)pthread_create(&task[i], NULL, fn, (void*) i); go = 1; for (i=0; i<TASK_NUM; i++)pthread_join(task[i], NULL); printf("[Main pid=%d] sum=%d (should be %d)\n", getpid(), sum, TASK_NUM*COUNT);printf("Test %s\n", sum==TASK_NUM*COUNT ? "PASS" : "FAIL");return 0;
}
运行结果
[Main pid=6674] sum=2000 (should be 2000)
Test PASS
本文标签: NPTLlll
版权声明:本文标题:NPTL:lll 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/p/1700299900a386386.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论