菜单

Administrator
发布于 2026-05-17 / 2 阅读
0
0

Linux线程的3种实现方式

一、线程的 3 种实现方式

在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是资源拥有的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。

根据操作系统内核是否对线程可感知,可以把线程分为内核线程用户线程

在多线程操作系统中,各个系统的实现方式并不相同,在有的系统中实现了用户级线程,有的系统中实现了内核级线程。

有些情况下,也把内核级线程叫做轻量级进程(LWP),但是这个是一个不太准确的描述,其实 LWP 的术语是借自于 SVR4/MP 和 Solaris 2.x 系统中,有些系统将 LWP 称为虚拟处理器,将之称为轻量级进程的原因可能是,在内核线程的支持下,LWP 是独立的调度单元,就像普通的进程一样。所以 LWP 的最大特点还是每个 LWP 都有一个内核线程支持。


二、用户级线程(多对一模型)

2.1 实现方式

在用户级线程中,内核对线程包一无所知。从内核角度考虑,就是按正常的方式管理,即单线程进程(存在运行时系统)。

用户级线程仅存在于用户空间中,此类线程的创建、撤销、线程之间的同步与通信功能,都无须利用系统调用来实现。用户进程利用线程库来控制用户线程。由于线程在进程内切换的规则远比进程调度和切换的规则简单,不需要用户态/核心态切换,所以切换速度快。

由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。为了在操作系统中加入线程支持,采用了在用户空间增加运行库来实现线程,这些运行库被称为线程包,用户线程是不能被操作系统所感知的。

用户级线程驻留在用户空间或模式。运行时库管理这些线程,它也位于用户空间。它们对于操作系统是不可见的,因此无法被调度到处理器内核。每个线程并不具有自身的线程上下文。因此,就线程的同时执行而言,任意给定时刻每个进程只能够有一个线程在运行,而且只有一个处理器内核会被分配给该进程。

2.2 特点

内核对线程包一无所知。从内核角度考虑,就是按正常的方式管理,即单线程进程(存在运行时系统)。

2.3 优点

  1. 可以在不支持线程的操作系统中实现。
  2. 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多,因为保存线程状态的过程和调用程序都只是本地过程。
  3. 允许每个进程定制自己的调度算法,线程管理比较灵活。
  4. 线程能够利用的表空间和堆栈空间比内核级线程多。
  5. 不需要陷阱,不需要上下文切换,也不需要对内存高速缓存进行刷新,使得线程调用非常快捷。
  6. 线程的调度不需要内核直接参与,控制简单。

2.4 缺点

  1. 线程发生 I/O 或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程,因此同一进程中只能同时有一个线程在运行。
  2. 页面失效也会产生类似的问题。
  3. 一个单独的进程内部,没有时钟中断,所以不可能用轮转调度的方式调度线程。
  4. 资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用。

补充:在用户级线程中,每个进程里的线程表由运行时系统管理。当一个线程转换到就绪状态或阻塞状态时,在该线程表中存放重新启动该线程所需的信息,与内核在进程表中存放的进程的信息完全一样。


三、内核级线程(一对一模型)

3.1 实现方式

在内核级线程中,内核线程建立和销毁都是由操作系统负责、通过系统调用完成的。在内核的支持下运行,无论是用户进程的线程,或者是系统进程的线程,他们的创建、撤销、切换都是依靠内核实现的。

线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也是在内核基于线程架构的基础上完成。

内核线程驻留在内核空间,它们是内核对象。有了内核线程,每个用户线程被映射或绑定到一个内核线程。用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两个线程都将离开系统。这被称作一对一线程映射。

特点:

  1. 线程的创建、撤销和切换等,都需要内核直接实现,即内核了解每一个作为可调度实体的线程。
  2. 这些线程可以在全系统内进行资源的竞争。
  3. 内核空间内为每一个内核支持线程设置了一个线程控制块(TCB),内核根据该控制块,感知线程的存在,并进行控制。

操作系统调度器管理、调度并分派这些线程。运行时库为每个用户级线程请求一个内核级线程。操作系统的内存管理和调度子系统必须要考虑到数量巨大的用户级线程。操作系统为每个线程创建上下文。进程的每个线程在资源可用时都可以被指派到处理器内核。

3.2 特点

当某个线程希望创建一个新线程或撤销一个已有线程时,它进行一个系统调用。

3.3 优点

  1. 多处理器系统中,内核能够并行执行同一进程内的多个线程。
  2. 如果进程中的一个线程被阻塞,能够切换同一进程内的其他线程继续执行(用户级线程的一个缺点)。
  3. 所有能够阻塞线程的调用都以系统调用的形式实现,代价可观。
  4. 当一个线程阻塞时,内核根据选择可以运行另一个进程的线程,而用户空间实现的线程中,运行时系统始终运行自己进程中的线程。
  5. 信号是发给进程而不是线程的,当一个信号到达时,应该由哪一个线程处理它?线程可以注册它们感兴趣的信号。

四、组合方式(多对多模型)

在一些系统中,使用组合方式的多线程实现。线程创建完全在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被映射到一些(小于或等于用户级线程的数目)内核级线程上。

在这种模型中,每个内核级线程有一个可以轮流使用的用户级线程集合。

POSIX 线程调度

POSIX 线程调度是一个混合模型,很灵活,足以在标准的特定实现中支持用户级和内核级的线程。模型中包括两级调度——线程级及内核实体级。线程级与用户级线程类似,内核实体由内核调度。由线程库来决定它需要多少内核实体,以及它们是如何映射的。

POSIX 引入了一个线程调度竞争范围(thread-scheduling contention scope)的概念,这个概念赋予了程序员一些控制权,使它们可以控制怎样将内核实体映射为线程。

线程的 contention scope 属性可以是:

  • PTHREAD_SCOPE_PROCESS:该线程与它所在的进程中的其他线程竞争处理器资源。
  • PTHREAD_SCOPE_SYSTEM:该线程很像内核级线程,他们在全系统的范围内竞争处理器资源。

内核级线程创建时先设置线程属性 PTHREAD_SCOPE_SYSTEM

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); // 设置内核级的线程,以获取较高的响应速度
// 创建线程
ret = pthread_create(&iAcceptThreadId, &attr, AcceptThread, NULL);

⚠️ 目前 LinuxThreads 仅实现了 PTHREAD_SCOPE_SYSTEM 一个值。

关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。


评论