Linux系统调用创建进程和线程

25

我在一篇文章中读到,创建进程和线程的底层系统调用实际上是相同的,因此创建进程的成本并不比创建线程高。

  • 首先,我想知道创建进程/线程的系统调用是什么(可能需要示例代码或链接)?
  • 其次,作者是否正确地认为创建进程而不是线程不费力?

编辑:
引用文章:

在Linux上,用进程替换pthread的成本意外地低,因为两者都使用相同的底层系统调用。


1
创建进程而不是线程吗?不是的。一般来说,线程是轻量级进程。但由于诸如写时复制之类的原因,界限有些模糊。 - Kristopher Micinski
2个回答

37

进程通常是用fork创建的,线程(轻量级进程)现在通常使用clone创建。但是,有时也存在1:N线程模型,它们两者都不使用。

无论是fork还是clone在内部映射到相同的内核函数do_fork。这个函数可以创建一个共享旧地址空间的轻量级进程,或者一个单独的进程(以及许多其他选项),具体取决于您向其提供的标志。 clone系统调用更或多或少直接转发该内核函数(并由更高级别的线程库使用),而fork则将do_fork包装到50年前传统Unix功能的功能中。

重要的区别是fork保证复制整个、单独的地址空间。正如Basil正确指出的那样,现在使用copy-on-write来完成,因此不像人们想象的那么昂贵。
当您创建线程时,它仅重用原始地址空间和相同的内存。

但是,人们不应该认为在类unix系统上创建进程通常是"轻量级"的,因为有了复制写入技术。它比Windows下的情况稍微轻一些,但它不是完全免费的。
一个原因是虽然实际页面没有复制,但新进程仍然需要页面表的副本。对于使用大量内存的进程,这可能是几千字节到几兆字节的内存。 另一个原因是尽管复制写入技术是看不见的且是一个聪明的优化,但它不是免费的,也不能做魔术。当数据被任一进程修改时(这不可避免地会发生),受影响的页面会出现故障。

Redis是一个很好的例子,您可以在其中看到fork远非轻量级(它使用fork进行后台保存)。


谢谢。我搜索了 do_fork,找到了它的源代码。有没有关于如何使用它的文档? - atoMerz
1
除非您编写内核代码,否则根本不会直接调用 do_fork。一般情况下,您可能也不想使用 clone(建议使用在其上构建的 pthreads 库)。无论如何,如果您确实想要使用 clone,可以查看文档这里。另一方面, fork 是您真正想使用的东西,文档位于同一网站上。 - Damon
我正在对这篇论文进行审查,我想知道事情实际上是如何工作的。我找到了forkpthread_create的源代码。但我找不到任何对do_fork的调用。 - atoMerz
不幸的是,Google Code Search已经关闭,而Koders使用起来很麻烦...这里有一些实现(在不同的arch子文件夹中有不同的实现,但可能没有太大区别),包括sys_fork和sys_clone,我可以通过快速搜索找到。 - Damon

6

创建线程的底层系统调用是clone(2)(它是特定于Linux的)。顺便说一下,Linux系统调用列表在syscalls(2)上,您可以使用strace(1)命令了解某些进程或命令执行的系统调用。进程通常使用fork(2)(或vfork(2),这在现今不太有用)。但是,您可以(某些C标准库可能会这样做)使用某种特殊形式的clone来创建它们。我想内核正在共享一些代码来实现clonefork等功能(因为一些功能,例如虚拟地址空间的管理是共同的)。

事实上,在大多数Unix系统上,进程创建(以及线程创建)通常非常快(因为它们使用写时复制机制来处理虚拟内存),通常只需几毫秒的时间。但是您可能会遇到病态情况(例如抖动),这会使得创建进程或线程需要更长的时间。

由于大多数Linux上的C标准库实现都是自由软件,因此您可以研究您系统上的源代码(通常使用GNU glibc,但有时也会使用musl-libc或其他库)。


所以它们是不同的系统调用? - atoMerz
1
它们是不同的,但据我所知 fork 可以使用 clone 实现(但前者比后者早了十几年)。 - Basile Starynkevitch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接