首页 文章

Linux中的线程与进程

提问于
浏览
225

我最近听到一些人说在Linux中,使用进程而不是线程几乎总是更好,因为Linux在处理进程方面非常有效,并且因为线程有很多问题(例如锁定) . 但是,我很怀疑,因为在某些情况下,线程似乎可以带来相当大的性能提升 .

所以我的问题是,当遇到线程和进程都能很好地处理的情况时,我应该使用进程还是线程?例如,如果我正在编写Web服务器,我应该使用进程或线程(或组合)吗?

13 回答

  • 8

    Linux使用1-1线程模型,(对内核)没有进程和线程之间的区别 - 一切都只是一个可运行的任务 . *

    在Linux上,系统调用 clone 克隆任务,具有可配置的共享级别,其中包括:

    • CLONE_FILES :共享相同的文件描述符表(而不是创建副本)

    • CLONE_PARENT :don 't set up a parent-child relationship between the new task and the old (otherwise, child' s getppid() =父母的 getpid()

    • CLONE_VM :共享相同的内存空间(而不是创建COW副本)

    fork() 调用 clone( 至少共享 )pthread_create() 调用 clone( 最多共享 ) . **

    由于复制表并为内存创建COW映射, fork 的成本略高于 pthread_create ,但Linux内核开发人员已经尝试(并成功)将这些成本降至最低 .

    如果任务共享相同的内存空间和各种表,则在任务之间切换比不共享任务要便宜一点,因为数据可能已经加载到缓存中 . 但是,即使没有共享任何内容,切换任务仍然非常快 - 这是Linux内核开发人员试图确保(并成功确保)的其他内容 .

    实际上,如果您使用的是多处理器系统, not 共享实际上可能对性能有益:如果每个任务在不同的处理器上运行,则同步共享内存非常昂贵 .


    *简化 . CLONE_THREAD 导致信号传递被共享(需要 CLONE_SIGHAND ,它共享信号处理程序表) .

    **简化 . 存在 SYS_forkSYS_clone 系统调用,但在内核中, sys_forksys_clone 都是围绕同一个 do_fork 函数的非常薄的包装器,它本身是 copy_process 的薄包装器 . 是的,术语 processthreadtask 在Linux内核中可以互换使用......

  • 55

    Linux(实际上是Unix)为您提供了第三种选择 .

    选项1 - 流程

    创建一个独立的可执行文件来处理应用程序的某些部分(或所有部分),并为每个进程单独调用它,例如:程序运行自己的副本以将任务委派给 .

    选项2 - 线程

    创建一个独立的可执行文件,它以单个线程启动,并创建其他线程来执行某些任务

    选项3 - 分叉

    仅在Linux / Unix下可用,这有点不同 . 分叉进程实际上是它自己的进程,它有自己的地址空间 - 孩子无法做(通常)影响其父进程或兄弟地址空间(与线程不同) - 因此你可以增加健壮性 .

    但是,内存页面不会被复制,它们是写时复制的,因此通常使用的内存比您想象的要少 .

    考虑一个Web服务器程序,它包含两个步骤:

    • 读取配置和运行时数据

    • 提供页面请求

    如果您使用了线程,则步骤1将完成一次,而步骤2将在多个线程中完成 . 如果使用“传统”进程,则需要为每个进程重复步骤1和2,并且存储配置和运行时数据的内存重复 . 如果使用了fork(),则可以执行步骤1,然后执行fork(),将运行时数据和配置保留在内存中,不受影响,不进行复制 .

    所以真的有三种选择 .

  • 4

    这取决于很多因素 . 进程比线程更重,并且具有更高的启动和关闭成本 . 进程间通信(IPC)也比跨线通信更难,更慢 .

    相反,进程比线程更安全,更安全,因为每个进程都在自己的虚拟地址空间中运行 . 如果一个进程崩溃或缓冲区溢出,它根本不会影响任何其他进程,而如果一个线程崩溃,它将关闭进程中的所有其他线程,如果一个线程有缓冲区溢出,它会打开所有线程中的安全漏洞 .

    因此,如果您的应用程序的模块可以在几乎没有通信的情况下独立运行,那么如果您能负担起启动和关闭成本,则应该使用流程 . IPC的性能影响将是最小的,您将对bug和安全漏洞稍微安全一些 . 如果您需要每一点性能,您可以获得或拥有大量共享数据(例如复杂的数据结构),请使用线程 .

  • -1

    其他人已经讨论了注意事项 .

    也许重要的区别在于,与线程相比,Windows进程繁重且昂贵,而在Linux中,差异要小得多,因此方程式在不同点进行 balancer .

  • 3

    曾几何时有Unix,在这个老的Unix中有很多进程的开销,所以一些聪明的人做的是创建线程,它将与父进程共享相同的地址空间,他们只需要减少上下文切换,这将使上下文切换更有效 .

    在当代Linux(2.6.x)中,进程的上下文切换与线程之间的性能差别不大(只有MMU的东西是线程的额外内容) . 共享地址空间存在问题,这意味着线程中的错误指针可能破坏父进程的内存或同一地址空间内的另一个线程 .

    进程受MMU保护,因此错误的指针只会导致信号11而不会损坏 .

    我一般会使用进程(在Linux中没有太多的上下文切换开销,但由于MMU导致的内存保护),但如果我需要一个实时的调度程序类,那就是pthreads,这是一个不同的茶 .

    为什么你认为线程在Linux上有如此大的性能提升?你有这方面的数据,还是只是一个神话?

  • 9

    你的任务有多紧密耦合?

    如果他们可以彼此独立生活,那么使用流程 . 如果他们互相依赖,那么使用线程 . 这样,您可以在不干扰其他任务操作的情况下终止并重启坏的进程 .

  • 2

    更复杂的是,有一个像thread-local storage和Unix共享内存的东西 .

    线程局部存储允许每个线程具有单独的全局对象实例 . 我唯一一次使用它是在linux / windows上构建仿真环境,用于在RTOS中运行的应用程序代码 . 在RTOS中,每个任务都是一个具有自己地址空间的进程,在仿真环境中,每个任务都是一个线程(具有共享地址空间) . 通过将TLS用于单例等事物,我们能够为每个线程创建一个单独的实例,就像在“真正的”RTOS环境下一样 .

    共享内存可以(显然)为您提供使多个进程访问同一内存的性能优势,但代价是成本/风险必须正确地同步进程 . 一种方法是让一个进程在共享内存中创建数据结构,然后通过传统的进程间通信(如命名管道)向该结构发送句柄 .

  • 3

    我听说过了 . 当我们对集群进行基准测试时( xhpl 等),我们总是通过线程进程获得明显更好的性能 . </anecdote>

  • 287

    线程/进程之间的决定取决于您将使用它的内容 . 进程的一个好处是它具有PID并且可以在不终止父进程的情况下被终止 .

    对于Web服务器的真实示例,apache 1.3过去只支持多个进程,但在2.0中它们添加了an abstraction,以便您可以在两者之间切换 . Comments seems to同意进程更强大但线程可以提供更好的性能(除了窗口,其中进程的性能很糟糕而你只想使用线程) .

  • 3

    在我最近使用LINUX的工作中,需要注意的是库 . 如果您正在使用线程,请确保您可以跨线程使用的任何库都是线程安全的 . 这烧了我几次 . 值得注意的是,libxml2不是开箱即用的线程安全的 . 它可以使用线程安全编译,但这不是aptitude安装所能获得的 .

  • 0

    对于大多数情况,我更喜欢进程而不是线程 . 当您具有相对较小的任务(进程开销>>每个划分的任务单元所花费的时间)并且它们之间需要内存共享时,线程可能很有用 . 想一个大阵列 . 另外(offtopic),请注意,如果您的CPU利用率是100%或接近它,那么多线程或处理将没有任何好处 . (事实上它会恶化)

  • 5

    线程 - >线程共享一个内存空间,它是CPU的抽象,它是轻量级的 . 进程 - >进程有自己的内存空间,它是计算机的抽象 . 要并行化任务,您需要抽象CPU . 然而,在线程上使用进程的优点是安全性,稳定性,而线程使用的内存比进程少,并且提供较少的延迟 . 关于web的一个例子是chrome和firefox . 在Chrome的情况下,每个选项卡都是一个新进程,因此chrome的内存使用率高于firefox,而提供的安全性和稳定性优于firefox . chrome提供的安全性更好,因为每个选项卡都是一个新进程,不同的选项卡无法窥探到给定进程的内存空间 .

  • 49

    如果您需要共享资源,那么您真的应该使用线程 .

    还要考虑这样一个事实,即线程之间的上下文切换比进程之间的上下文切换便宜得多 .

    除非你有充分的理由这样做(安全性,经过验证的性能测试等等),否则我认为没有理由明确采用单独的流程 .

相关问题