Glibc 中互斥锁、自旋锁、信号量、once及sleep的实现机制
Glibc互斥锁(pthread_mutex_t)内部机制
互斥锁通过用户态与内核态协作的futex机制实现高效同步。
// 加锁流程示意
pthread_mutex_lock(mutex) {
if (CAS(mutex->__lock, 0, 1) == 0) {
return; // 快速路径:无竞争时立即获取锁
}
// 慢速路径:存在竞争
atomic_store(mutex->__lock, 2); // 标记有等待者
while (true) {
if (CAS(mutex->__lock, 0, 2) == 0) {
break; // 成功获取锁
}
futex_wait(&mutex->__lock, 2); // 系统调用进入等待
}
}
// 解锁流程示意
pthread_mutex_unlock(mutex) {
int old = atomic_exchange(&mutex->__lock, 0);
if (old == 2) {
futex_wake(&mutex->__lock, 1); // 唤醒一个等待线程
}
}
futex(快速用户态互斥锁)减少了低竞争场景下的系统调用开销。
Glibc自旋锁(pthread_spinlock_t)实现方式
自旋锁是完全在用户态基于原子操作实现的忙等待锁。
// 自旋锁加锁
pthread_spin_lock(lock) {
while (atomic_compare_exchange_strong(lock, 0, 1) == false) {
while (*lock == 1) {
__builtin_ia32_pause(); // 处理器优化指令,减少能耗
}
}
}
// 自旋锁解锁
pthread_spin_unlock(lock) {
__atomic_clear(lock, __ATOMIC_RELEASE); // 原子清0并保证内存序
}
自旋锁适用于锁持有时间极短的场景,避免上下文切换开销。
Glibc信号量(sem_t)工作原理
信号量内部使用64位值,高32位记录等待者数量,低32位存储计数值。
// 信号量结构示意
struct internal_sem {
uint32_t waiters; // 高32位
uint32_t count; // 低32位
};
// 信号量等待(P操作)
sem_wait(sem) {
uint64_t val = atomic_load(sem);
while (true) {
uint32_t cnt = (uint32_t)val;
if (cnt == 0) {
// 慢速路径:资源不足
val = atomic_fetch_add(sem, 1ULL << 32); // 增加等待者计数
futex_wait(sem, val); // 进入等待
val = atomic_load(sem); // 重新加载状态
continue;
}
// 尝试减少计数值
if (atomic_compare_exchange(sem, &val, val - 1)) {
break; // 操作成功
}
}
}
// 信号量发布(V操作)
sem_post(sem) {
uint64_t val = atomic_load(sem);
while (true) {
uint32_t cnt = (uint32_t)val;
if (cnt == UINT32_MAX) {
errno = EOVERFLOW;
return -1;
}
if (atomic_compare_exchange(sem, &val, val + 1)) {
if ((uint32_t)(val >> 32) > 0) {
futex_wake(sem, 1); // 唤醒等待者
}
break;
}
}
}
Glibc单次初始化(pthread_once_t)机制
确保初始化函数在多线程环境下仅执行一次。
// 内部状态值
#define ONCE_INIT 0 // 未初始化
#define ONCE_RUNNING 1 // 正在执行
#define ONCE_DONE 2 // 已完成
pthread_once(once_control, init_routine) {
int state = atomic_load(once_control);
if (state == ONCE_DONE) return;
if (state == ONCE_INIT) {
if (atomic_compare_exchange(once_control, ONCE_INIT, ONCE_RUNNING)) {
init_routine(); // 执行初始化函数
atomic_store(once_control, ONCE_DONE);
futex_wake(once_control, INT_MAX); // 唤醒所有等待线程
return;
}
}
// 其他线程等待初始化完成
while (atomic_load(once_control) != ONCE_DONE) {
futex_wait(once_control, ONCE_RUNNING);
}
}
Glibc睡眠(sleep)函数实现
sleep函数基于nanosleep系统调用,可被信号中断。
unsigned int custom_sleep(unsigned int sec) {
struct timespec req = {0, 0}, rem = {0, 0};
unsigned int unslept = sec;
while (unslept > 0) {
req.tv_sec = (time_t)MIN(unslept, INT_MAX);
req.tv_nsec = 0;
if (sys_nanosleep(&req, &rem) == -1) {
if (errno == EINTR) {
// 被信号中断,计算剩余时间
unslept = rem.tv_sec;
continue;
}
return unslept;
}
unslept -= req.tv_sec;
}
return 0;
}
注:标准sleep函数精度有限,对精度要求高的场景建议使用clock_nanosleep。