多线程程序需要考虑的最后一个问题是内核和线程库之间的通信,这就需要用到多对多模型两级模型。这种协调允许动态调整内核线程的数量,以确保最佳性能。

很多实现多对多模型或者两级模型的系统,在用户线程和内核线程之间放置了一个中间数据结构--------通常被称为轻量级进程,或者LWP。如下图4-13所示:

对于用户线程库,LWP表现为一种在应用程序上可以调度用户线程来运行的虚拟处理器。每个LWP都连接到一个内核线程上,该内核线程被操作系统调度到物理处理器上运行。如果内核线程阻塞(例如等待I/O操作完成),则LWP也会阻塞。在这一条链上,连接到LWP的用户级线程也会阻塞。

为了高效的运行,应用程序可能需要一定数量的LWP。

考虑一个在单处理器上运行的cpu-bound应用程序。在这种情况下,一次只能运行一个线程,因此一个LWP就足够了。但是,I/O密集型应用程序可能需要执行多个LWPs。

通常,每一个并发阻塞系统调用都需要一个LWP。例如,假设有五个不同的文件读取请求同时发生。需要5个LWPs,因为所有的都可以在内核中等待I/O完成。如果一个进程只有4个LWP,那么第五个请求必须等待一个LWPs从内核返回。

用户线程库和内核之间通信的一个方案称为调度器激活(scheduler activation)。它的工作原理如下:

内核为应用程序提供了一组虚拟处理器(LWPs),应用程序可以将用户线程调度到可用的虚拟处理器上。此外,内核必须将某些事件通知应用程序。这一程序被称为upcall。upcall由带有upcall处理器( upcall handler)线程库处理,upcall处理器必须在虚拟处理器(LWP)上运行。

当一个应用程序线程即将阻塞时,一个会触发upcall的事件会出现。在这个场景中,内核向应用程序发出一个upcall,通知它一个线程将要阻塞并标识特定的线程。然后,内核为应用程序分配一个新的虚拟处理器(LWP)。该应用程序在这个新的虚拟处理器上运行一个upcall处理器( upcall handler),它保存了阻塞线程的状态,并放弃阻塞线程运行在的虚拟处理器。

然后,upcall处理器调度另一个线程,该线程有资格在新的虚拟处理器上运行。当阻塞线程等待发生的事件发生时,内核会向线程库发出另一个upcall,通知它之前阻塞的线程现在可以运行了。这个事件的upcall处理器也需要一个虚拟处理器,内核可能会分配一个新的虚拟处理器,或者抢占一个用户线程,在其虚拟处理器上运行upcall处理器。在将未阻塞的线程标记为符合运行条件后/有资格运行后,应用程序会调度一个符合条件/有资格的线程在可用的虚拟处理器上运行。

results matching ""

    No results matching ""