线程与(分叉)进程


9

Linux应用程序通常派生然后执行(使用execve()),但是Java应用程序和某些Apache MPM使用线程。如果进行分叉,请使用fork + exec生成进程,线程的高级版本是什么?JVM或Worker MPM如何产生线程?


2
查看Stackoverflow。那里有一些问答,解释了部分原因。
Henk Langeveld

Answers:


13

线程和进程背后的想法大致相同:您分叉执行路径。否则,线程和进程在诸如内存之类的事物上会有所不同。即,进程具有不同的VM空间,而线程共享拆分前存在的任何内容。

通过使用clone()调用(man 2 clone)在线程和派生工作的基础上:

与fork(2)不同,clone()允许子进程与调用进程共享其执行上下文的部分,例如内存空间,文件描述符表和信号处理程序表。(请注意,在此手册页上,“调用过程”通常对应于“父过程”。但是请参见下面的CLONE_PARENT的描述。)

clone()的主要用途是实现线程:程序中的多个控制线程在共享内存空间中同时运行。

区别来自传递给clone()的标志。从手册页可以看到,fork和threading只是clone()的一组预定义参数。但是,也可以用它来做定制的东西。


1
嗯?什么?请重新阅读有关该主题的几乎所有书籍,因为用于进程的单独内存空间很大。还可以帮助“捕获”崩溃的代码,而内核将简单地杀死单个线程陷入混乱或侵入的进程。
0xC0000022L 2014年

3
@ 0xC0000022L在我看来,您的论点与答案并不矛盾。
罗斯兰2014年

1
@Ruslan:我想不同的是:“这个想法是差不多的”?线程背后的想法确实是并发的,但是对于进程来说,这是一个完全不同的故事。
0xC0000022L 2014年

4
@ 0xC0000022L您错过了V13答案的重要部分:“您分叉了执行路径”-问题是关于如何产生线程,而不是线程和进程之间的区别
Izkata 2014年

@Izkata:一点也不。我只是认为这不是正确的主张。
0xC0000022L 2014年

8

大多数非Unix多处理操作系统(OS)使用“ spawn()”调用或类似方法来生成新的OS进程或控制流。Spawn()往往是一个非常复杂的调用,具有很多选项和大量开销。Unix的一项创新是提供一种创建进程的开销要低得多的方法-fork()。Unix通过使用exec()在spawn()的另一半之前允许任意数量的处理,来照顾了spawn()的许多必要选项。

随着Unix及其变体的使用越来越多,发现低开销的进程创建是有用的,并且被使用。实际上,它的使用非常广泛,以至于人们希望使用更低的开销来创建进程,因此“线程”的思想应运而生。最初,线程是完全由原始进程处理的(JVM之类的程序可以通过“绿色线程”来完成);但是处理多线程调度很棘手,而且经常做错。因此,存在一种更简单的中间线程处理方式,其中OS处理调度,但通过(通常)在线程之间共享地址空间来节省一些开销。

您的问题很难回答,因为存在多个不同但相关的概念,它们都是“线程”,并且要详细说明,您需要一个形容词来描述您所引用的对象。另一方面,了解差异可能会导致您找到想要的特定答案。查找诸如“轻量级进程”,“用户线程”和“ rfork()”之类的详细信息。


1
需要引用“处理多线程调度是棘手的,并且经常做得不正确”。实施用户空间线程不是问题。用户空间线程的问题在于,如果线程执行了阻塞的系统调用,则所有线程都会被阻塞。避免这种情况的唯一方法是使用系统级线程。
巴库里2014年

1
有趣的是,Windows没有包括Unix的这一创新:它与CreateProcess()没什么类似fork()
罗斯兰2014年

2
@Bakuriu-查找有关构建多处理调度程序,维护公平性,避免饥饿,处理优先级等许多文章。正如您所说的,实现用户空间线程不是问题。安排不平凡的例子很困难。
mpez0 2014年

@Ruslan:可以在Windows上使用fork,它不是Win32 API的一部分。阅读Nebbett的“ Windows NT / 2000本机API”。他有一个模仿的实现fork()
0xC0000022L 2014年

3

线程和派生实际上是两个不同的概念,它们都存在于Unix / Linux系统中(并且都可以在C / C ++中使用)。

fork()的想法(基本上是)是创建一个单独的进程,该进程具有与父进程相同的执行代码,并从fork行开始执行。将fork与exec函数一起使用的目的是exec函数会在结束时关闭调用它们的进程。因此,您通常会分叉,获取每个进程的PID(孩子的PID始终为0),并使父进程等待孩子完成执行exec函数。

线程用于并行处理(请记住,父级通常在派生程序中等待子级)。诸如C / C ++中的pthread(执行Google搜索)之类的线程将与主进程并行运行,并且可以与原始程序共享全局变量和全局函数。由于Java线程的行为类似,因此我可以想象它们的行为更像这些线程,而不像分支过程。

基本上,分支和线程之间是有区别的。他们做的事情截然不同(尽管看起来很相似)。这些概念可能很难理解,但是如果您有诚实的了解,可以通过(广泛的)研究来学习它们。

编辑#1

请参阅这些示例,了解如何调用和使用fork和线程。请注意exec函数的行为及其对主程序的影响。

http://www.jdembrun.com:4352/computerScience/forkVSthread.zip


2
Fork(带有或不带有exec)也可以用于并行处理。我不确定“ exec函数在它们结束时关闭调用它们的进程”是什么意思,exec在进程结束时早已完成运行。也是pthreadAPI,不是线程实现。
2014年

关于叉子,我引用了我的操作系统老师。根据他告诉我们的信息,是的,可以将fork用于并行运行,但是,如果使用exec函数,那将是最后一个。至于pthread,它只是作为示例。
jaredad7

Exec将是调用者代码中的最后一个调用,而不是派生进程的最后一条指令。分叉的进程将继续运行执行的代码。
2014年

您的评论促使我测试了这些东西。我编写了一些c ++程序,这些程序演示了exec函数的行为及其在fork与线程中使用时对程序的影响。请参阅上面的编辑。
jaredad7

恐怕大多数人都不会下载它。同样,您的示例并未说明模型之间的有趣差异,这些差异主要与共享(或不共享)地址空间有关。
2014年

1

JVM和Apache MPM都依赖内核获取本机线程。也就是说,他们使用操作系统进行调度。当然,两者都需要使用自己的API来跟踪内容。

Stackoverflow已经有几个问题要解决:

  1. JVM本机线程,请查看此答案以获取更多详细信息。

  2. Apache有两种类型的MPM:Prefork(每个线程一个进程)和Worker(处理多个线程):Apache MPM。查看参考codebucket


1

如果进行分叉,请使用fork + exec生成进程,线程的高级版本是什么?JVM或Worker MPM如何产生线程?

那是特定于平台的,但是在Linux上,我假定许多其他POSIX兼容系统,它们使用pthreads(用户级线程API)的本地实现。例如:

#include <pthread.h>

pthread_t tid;
pthread_create(&tid, NULL, somefunc, NULL);

启动一个新线程,将somefunc其作为第一个执行点。

您还可以创建线程-与分支不同,因为它们共享父进程相同的全局内存空间,而不是获得它的重复副本(但请注意,每个线程都使用自己的独立堆栈内存执行)-与clone()系统调用一起使用,这是pthread在其之上构建的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.