在Linux/GLIBC中存在多种解决方案,但没有一种允许在用户空间和内核空间之间显式共享信号量。
内核提供了挂起线程/进程的解决方案,最有效的是futex。以下是当前同步用户空间应用程序的现有实现的最新情况的一些详细信息。
SYSV服务
Linux System V(SysV)信号量是同名Unix OS的遗留物。它们基于系统调用来锁定/解锁信号量。相应的服务包括:
-
semget() 获取标识符
-
semop() 对信号量执行操作(例如增加/减少)
-
semctl() 对信号量进行某些控制操作(例如销毁)
GLIBC(例如
2.31版本)在这些服务之上没有提供任何附加值。库服务直接调用同名系统调用。例如,
semop()(位于
sysdeps/unix/sysv/linux/semtimedop.c中)直接调用相应的系统调用:
int
__semtimedop (int semid, struct sembuf *sops, size_t nsops,
const struct timespec *timeout)
{
#if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS && defined __NR_semtimedop
return INLINE_SYSCALL_CALL (semtimedop, semid, sops, nsops, timeout);
#else
return INLINE_SYSCALL_CALL (ipc, IPCOP_semtimedop, semid,
SEMTIMEDOP_IPC_ARGS (nsops, sops, timeout));
#endif
}
weak_alias (__semtimedop, semtimedop)
现在,SysV信号量(以及其他SysV IPC,如共享内存和消息队列)被视为过时,因为它们每次操作都需要进行系统调用,会通过系统上下文切换减缓调用进程的速度。新应用程序应使用GLIBC提供的符合POSIX标准的服务。
POSIX信号量基于快速用户互斥锁(FUTEX)。其原理是,在没有争用的情况下,使用原子操作在用户空间中递增/递减信号量计数器。但是当存在争用(多个线程/进程同时想要“锁定”信号量)时,将执行futex()系统调用,以便在“解锁”信号量时唤醒等待的线程/进程或暂停等待释放信号量的线程/进程。从性能角度来看,与上述SysV服务相比,这产生了很大的差异,后者对任何操作都需要系统调用。 POSIX服务在GLIBC中实现了用户空间部分操作(原子操作),仅在存在争用时才切换到内核空间。
例如,在GLIBC 2.31中,锁定信号量的服务位于nptl/sem_waitcommon.c中。它检查信号量的值,并使用原子操作将其减少(在__new_sem_wait_fast()中),并调用futex()系统调用(在__new_sem_wait_slow()中)来挂起调用线程,
仅当尝试将其减少之前,信号量的值等于0时。
static int
__new_sem_wait_fast (struct new_sem *sem, int definitive_result)
{
[...]
uint64_t d = atomic_load_relaxed (&sem->data);
do
{
if ((d & SEM_VALUE_MASK) == 0)
break;
if (atomic_compare_exchange_weak_acquire (&sem->data, &d, d - 1))
return 0;
}
while (definitive_result);
return -1;
[...]
}
[...]
static int
__attribute__ ((noinline))
__new_sem_wait_slow (struct new_sem *sem, clockid_t clockid,
const struct timespec *abstime)
{
int err = 0;
[...]
uint64_t d = atomic_fetch_add_relaxed (&sem->data,
(uint64_t) 1 << SEM_NWAITERS_SHIFT);
pthread_cleanup_push (__sem_wait_cleanup, sem);
for (;;)
{
if ((d & SEM_VALUE_MASK) == 0)
{
err = do_futex_wait (sem, clockid, abstime);
[...]
基于futex的POSIX服务的示例包括:
要管理互斥锁(即二进制信号量),可以使用pthread服务。它们也基于futex。例如:
up()
或down()
,更不用说一些页面故障和其他复杂的内核空间了。 - artless noise