【问题标题】:Linux pthread mutex and kernel schedulerLinux pthread 互斥锁和内核调度程序
【发布时间】:2014-05-28 10:09:36
【问题描述】:

对于我的一个朋友,我们不同意如何在用户空间级别(在 pthread 库中)处理同步。

一个。我认为在 pthread_mutex_lock 期间,线程会主动等待。意思是 linux 调度器启动这个线程,让它执行他的代码,它应该看起来像:

while (mutex_resource->locked);

然后,调度另一个线程,它可能释放locked 字段等。 所以这意味着调度程序在切换到下一个线程之前等待线程完成其调度时间,无论线程在做什么。

b.我的朋友认为等待线程以某种方式告诉内核“嘿,我睡着了,根本不要等我”。 在这种情况下,内核会立即调度下一个线程,而无需等待当前线程完成其调度时间,并知道该线程正在休眠。

从我在 pthread 的代码中看到的,似乎有循环处理锁。但也许我错过了什么。

在嵌入式系统中,防止内核等待是有意义的。所以他可能是对的(但我希望他不是:D)。

谢谢!

【问题讨论】:

    标签: linux pthreads mutex scheduling


    【解决方案1】:

    一个。我认为在pthread_mutex_lock 期间,线程会主动等待。

    是的,glibc 的 NPTL pthread_mutex_lock 有主动等待(旋转), 但是旋转仅用于非常短的时间,并且仅用于某些类型的互斥锁。在此数量之后,pthread_mutex_lock 将进入睡眠状态,方法是使用 WAIT 参数调用 linux syscall futex

    只有类型为 PTHREAD_MUTEX_ADAPTIVE_NP 的互斥体会自旋,默认为 PTHREAD_MUTEX_TIMED_NP(普通互斥体)不自旋。 Check MAX_ADAPTIVE_COUNT in __pthread_mutex_lock sources)。

    如果您想进行无限旋转(主动等待),请使用 pthread_spin_lock 函数和 pthread_spinlock_t-types 锁。

    我将考虑您的其余问题,就好像您使用的是pthread_spin_lock

    然后,调度另一个线程,这可能释放锁定的字段等。因此这意味着调度程序在切换到下一个线程之前等待线程完成其调度时间,无论线程在做什么。

    是的,如果 CPU 内核存在争用,您的主动旋转线程可能会阻止其他线程执行,即使另一个线程是解锁您的线程所需的互斥锁(自旋锁)的线程。

    但是如果没有争用(没有线程超额订阅),并且线程被安排在不同的核心上(巧合,或者通过手动设置 cpu 亲和性与sched_setaffinitypthread_setaffinity_np),旋转将使您能够更快地进行,然后使用基于操作系统的 futex。

    b.我的朋友认为等待线程以某种方式告诉内核“嘿,我睡着了,根本不要等我”。在这种情况下,内核会立即调度下一个线程,而无需等待当前线程完成...

    是的,他是对的。

    futex 是一种现代方式,表示操作系统该线程正在等待内存中的某个值(用于打开一些 mutex);在当前的实现中futex 也让我们的线程进入睡眠状态。如果内核知道何时唤醒该线程,则不需要唤醒它来进行旋转。它怎么知道?锁拥有者在执行pthread_mutex_unlock 时会检查是否有其他线程在此互斥锁上休眠。如果有的话,锁拥有者会调用futexFUTEX_WAKE,告诉操作系统唤醒一些线程,在这个互斥锁上注册为睡眠者。

    如果线程在操作系统中将自己注册为等待者,则无需旋转。

    【讨论】:

    • 什么是内核端休眠/不休眠before futex的机制?
    • @BeeOnRope,Unix 中有许多睡眠机制,如选择/轮询和解除睡眠的信号(以及各种IPC API)。对于 linux 中的线程,有 linuxthreads(线程是进程,管理器“线程”使用 SIGUSR1/2 唤醒),提出了 NGPT,现在有 NPTL,具有真正的 1:1 线程和 futex(LWN 0.1,@987654330 @; 使用内核 2.6.*、2.6.40+、...)。检查pdf的第8页。
    【解决方案2】:

    使用 gdb 对该测试程序进行一些调试:

    #include <pthread.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    
    pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
    
    void* thr_func(void *arg)
    {
      pthread_mutex_lock(&x);
    }
    
    int main(int argc, char **argv)
    {
      pthread_t thr;
    
      pthread_mutex_lock(&x);
      pthread_create(&thr, NULL, thr_func, NULL);
      pthread_join(thr,NULL);
      return 0;
    }
    

    表明在互斥体上调用pthread_mutex_lock 会导致调用系统调用futex 并将op 参数设置为FUTEX_WAIT (http://man7.org/linux/man-pages/man2/futex.2.html)

    这是对 FUTEX_WAIT 的描述:

    FUTEX_WAIT

    这个操作原子地验证futex地址 uaddr 仍然包含值 val,并休眠等待 FUTEX_WAKE on 这个 futex 地址。如果超时参数是 非NULL,其内容描述了等待的最大持续时间, 否则是无限的。参数 uaddr2 和 val3 是 忽略。

    所以从这个描述中我可以说如果一个互斥锁被锁定,那么一个线程将休眠而不是主动等待。它会一直休眠,直到调用了 op 等于 FUTEX_WAKE 的 futex。

    【讨论】:

    • 嗯,我可能错了,但我的理解不一样:在这个 futex 地址上等待 FUTEX_WAKE 并休眠。 如果函数正在休眠(而不是线程),这是一个主动等待,对吧?
    • futex 系统调用有参数“锁变量的地址”和“锁变量的预期值”。该地址用于将同一个互斥锁上的 futex 调用者链接在一起,而不是链接线程,等待不同的互斥锁。 Sleeper 将向 OS 显示它正在等待的地址,并且解锁时的锁所有者(OS,为锁所有者工作)将使用相同的(物理)地址来查找任何线程,等待这个互斥锁,并唤醒其中一个服务员,如果lock 的新值是预期的。
    猜你喜欢
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 2015-06-19
    • 2014-10-09
    • 2012-06-05
    • 1970-01-01
    相关资源
    最近更新 更多