0

我不太了解用户级线程和内核级线程之间的区别。这是我目前所知道的:

内核级线程

好处:

  • 如果我们有多个处理器/内核,我们实际上可以同时运行线程(真正的并行性,因为内核调度程序可以控制线程)
  • 阻塞线程(例如用于 I/O 操作)不要阻塞所有线程

缺点:

  • 上下文切换是在内核中进行的(这意味着它比在用户空间中慢)

用户级线程

好处:

  • 更快的上下文切换
  • 可以在应用程序中计划调度(而不是由 OS 调度程序)

缺点:

  • 内核对线程一无所知,因此该进程一次最多使用一个处理器/内核。
  • 如果一个线程进行阻塞调用,所有线程将再次被阻塞,因为内核不知道线程的存在,调度程序将让其他进程访问处理器/内核,即使该程序中的其他线程可以跑。

据我所知,我无法回答这个问题

创建内核级线程时,进程的 .text(code area) 如何修改?

我的直觉说它不会改变,因为线程共享相同的地址空间,因此 .text 区域不会改变。我发现支持这个答案的另一个原因是 .text 区域是只读的。此外,所有其他区域将保持不变(.bss、堆等)。唯一会改变的是堆栈。

但是我想确定这是正确的答案。

注:我主要讲linux内核上的线程(关于windows线程的东西不太了解)

4

3 回答 3

2

这里可能会询问 4 种不同类型的线程。

  1. 内核自己创建的用于处理后台任务的线程。
  2. fork()系统调用派生的进程。
  3. pthread 线程由用户使用 Linuxclone()系统调用创建但由内核管理
  4. 完全在内核调度程序之外运行的“绿色”线程

以下答案假设帖子中提到的“用户级”线程是 pthread 品种,而不是“绿色”品种。

编辑:我认为@Hristo 是对的,OP 正在谈论“绿色”线程或者混淆了两者。

此答案假定您正在谈论您使用创建的线程pthread_create()。还有一些“绿色”线程由用户空间调度,内核不知道。我将把我的答案留给后代。


用户线程 == 绿色线程

所谓的“绿色线程”根据定义是完全由 VM 处理而无需内核参与的线程。

“用户级”内核对线程一无所知,因此该进程一次最多使用一个处理器/内核。

对。

如果一个线程进行阻塞调用,所有线程将再次被阻塞,因为内核不知道线程的存在

不会。只要您使用正确的原语,阻塞调用将导致线程被放入等待队列并运行另一个绿色线程。

创建内核级线程时,进程的 .text(code area) 如何修改?

在绿色线程中,它们都使用相同的代码区域。

此外,所有其他区域将保持不变(.bss、堆等)。

是的。


用户线程 == pthreads

对于pthread_create()“用户线程”,它们与“内核”线程之间几乎没有区别。clone()linux内核使用系统调用 将用户级线程实现为单独的内核进程。clone()创建一个使用与父进程相同的内存空间的新进程,而不是fork()创建一个具有父内存副本的新进程。

“用户级”内核对线程一无所知,因此该进程一次最多使用一个处理器/内核。

不,这是不正确的。内核像任何其他进程一样管理用户级线程,因为它具有上下文切换和在多个 CPU 上调度各种用户级线程的能力等。

如果一个线程进行阻塞调用,所有线程将再次被阻塞,因为内核不知道线程的存在

不,一个线程可以阻塞,而其他线程将继续运行。同样,这是在 pthread 样式线程下。

创建内核级线程时,进程的 .text(code area) 如何修改?

创建新线程时,它会继承其父线程的内存分段表。文本/代码页(和其他只读页)被标记为只读,并且两个线程在线程的生命周期内共享内存。所有读写页面也是共享的,直到新线程对这些部分进行写入,此时页面被复制到另一个位置(写入时复制)。

此外,所有其他区域将保持不变(.bss、堆等)。

不,堆页面以及其他未标记为只读的内容(包括您提到的堆栈)将在写入后立即被复制。我不确定 .bss 部分,但由于它们是在运行时初始化的,我相信它们也是读写的。DATA 是只读的。

于 2012-05-25T15:13:00.343 回答
2

当从用户级线程切换到内核级线程(也称为 LWP 或轻量级进程)时,程序的文本会发生变化。原因是这两种线程以完全不同的方式实现/编码。

内核级线程只是单独的指令流,它们共享除堆栈和线程本地存储 (TLS) 之外的所有内容。它们大多被内核视为真实进程,但它们是轻量级的,因为它们彼此共享大量资源。实际上,Linux 中的单线程进程只是只有一个 LWP 加上所有其他内核结构的进程,这使它成为一个成熟的进程。LWP 或多或少像通常的独立 Linux 进程执行一样并发执行——您不需要做任何事情来使它们同时运行——这一切都由内核调度程序完成。与成熟流程的不同之处在于 LWP 共享其大部分数据。他们还共享.text可执行文件的部分,但实际上.text部分在同一可执行文件的所有实例之间共享,不仅在其 LWP 之间。

用户级线程提供类似于真正的多线程的东西,但它实际上是一种分时解决方案,通常由显式切换机制驱动。这意味着当一个用户级线程正在执行时,所有其他线程都被搁置,因为只有一个正在运行的指令流从代码中的一个点跳转到另一个点,同时保存/恢复一些 CPU 寄存器(最明显的是堆栈指针)。用户级线程的一种形式是在libpth(GNU Portable Threads) 中实现的协作多任务。用户级线程向用户代码添加了特殊机制,允许线程以被认为是同时模式的方式运行。libpth例如要求每个线程调用特定的 API 函数以允许其他线程运行。计时器信号可用于抢占用户级线程,但如果进程当前卡在阻塞系统调用中,这通常是不可能的。这就是为什么用户级线程通常使用异步 I/O、非阻塞操作等高级东西来编写,这与内核级线程完全不同,后者完全可以接受阻塞系统调用。就内核而言,这一切都发生在单个内核级线程中。

与用户级线程类似的最显着示例是在单线程可执行文件中并行使用select()或处理多个网络连接。poll()在这种情况下,“线程”状态是连接状态结构,然后用于调用特定通信函数,该函数处理套接字上的数据,该套接字被轮询系统调用检测为活动。

将用户级线程转换为内核级线程通常需要更改源代码(例如,必须删除对切换 API 的调用),因此该.text部分也会更改。将内核级线程转换为用户级线程还需要更改源代码,并且可能并不总是可行。您不能只在调用中插入实现用户级线程的函数的名称,pthread_create()尽管如果用户级线程实现使用诸如计时器信号之类的东西并且线程不调用特殊的切换 API...

两个线程实现之间的另一个显着区别是,在协作线程中,很难有竞争条件或共享冲突(即两个线程同时读取-修改-写入相同的内存位置),因为控制权在代码中的众所周知的点,而在抢占式情况下,进程可以在其间的任何地方中断(例如,可以说在协作情况下所有操作都是原子的),并且这种访问必须由关键部分或其他同步原语保护。

只是为了完整起见,Windows 也有自己的用户级线程实现,称为fiber

于 2012-05-25T17:30:14.610 回答
0

Linux 中实际上并不存在用户级线程。每当您使用 pthread_create 创建新线程时,也会创建相应的内核级线程。使用strace它应该清楚,clone()每当您创建新线程时都会调用系统调用。

在任何情况下,所有线程都共享 .text、.data 和 .bss 部分。他们唯一不共享的是他们的堆栈。(是的,是的,还有线程局部变量。我们先不谈这个。)

于 2012-05-25T15:12:07.630 回答