内核级线程修改.text区域?

时间:2012-05-25 14:59:50

标签: linux multithreading

我真的不明白用户级线程和内核级线程之间的区别。这就是我目前所知道的:

内核级主题

优点:

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

缺点:

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

用户级主题

优点:

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

缺点:

  • 内核对线程一无所知,所以最多只需要一次使用一个处理器/核心。
  • 如果一个线程进行阻塞调用,则所有线程将再次被阻塞,因为内核不知道线程是否存在,并且调度程序将给予处理器/核心其他进程访问权限,即使其他线程也是如此在这个程序中可以运行。

据我所知,到目前为止,我无法回答这个问题

创建内核级线程时,进程的.text(代码区域)如何修改?

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

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

注意:我主要谈谈linux内核上的线程(不知道很多关于windows线程的东西)

3 个答案:

答案 0 :(得分:2)

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

  1. 内核自己创建的线程,用于处理后台任务。
  2. fork()系统调用。
  3. 分叉的进程
  4. pthread线程,由用户使用Linux clone()系统调用创建,但由内核管理
  5. 完全在内核调度程序之外运行的“绿色”线程
  6. 以下答案假定帖子中提到的“用户级”线程是pthread变种而不是“green”变种。

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

    这个答案假设您正在讨论使用pthread_create()创建的线程。还有"green" threads由用户空间调度,内核不知道。我会在这里留下我的答案给后人。


    用户主题==绿色主题

    所谓的“绿色线程”是定义线程,完全由VM处理而不涉及内核。

      

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

    右。

      

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

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

      

    创建内核级线程时,进程的.text(代码区域)如何修改?

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

      

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


    用户主题== pthreads

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

      

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

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

      

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

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

      

    创建内核级线程时,进程的.text(代码区域)如何修改?

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

      

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

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

答案 1 :(得分:2)

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

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

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

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

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

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

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

答案 2 :(得分:0)

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

在任何情况下,所有线程共享.text,.data和.bss部分。 他们唯一不分享的是他们的筹码。 (是的,是的,并且线程局部变量。让我们不要进入那个。)