ato*_*erz 11 linux multithreading process system-calls
我在一篇论文中读到,创建进程和线程的底层系统调用实际上是相同的,因此在线程上创建进程的成本并不是那么高.
编辑:
引用文章:
用进程替换pthread是非常便宜的,特别是在使用相同底层系统调用调用pthread和进程的Linux上.
Dam*_*mon 20
通常使用fork线程(轻量级进程)创建进程,clone现在通常创建进程.然而,有趣的是,存在1:N线程模型,它们也没有.
双方fork并clone映射到相同的内核函数do_fork内部.此函数可以创建一个轻量级进程,该进程与旧的共享地址空间,或者单独的进程(以及许多其他选项),具体取决于您向其提供的标志.该clone系统调用是那个,而核函数(和更高级别的线程库使用)或多或少直接转发fork包裹do_fork到50岁传统的Unix函数的功能.
重要的区别在于fork保证完成单独的地址空间副本.正如巴兹尔所指出的那样,现在这种写作是通过写时复制完成的,因此并不像人们想象的那么昂贵.
创建线程时,它只是重用原始地址空间和相同的内存.
但是,不应该假设在类似unix的系统上创建进程通常是"轻量级"的,因为写时复制.它比Windows下的重量要轻一些,但它远没有那么自由.
一个原因是虽然没有复制实际页面,但新进程仍然需要页表的副本.对于使用大量内存的进程,这可能是几千字节到几兆字节的内存.另一个原因是虽然写时复制是不可见的并且是一种巧妙的优化,但它并不是免费的,它也不能做到魔术.当任何一个过程都不可避免地修改数据时,受影响的页面就会出错.
Redis是一个很好的例子,你可以看到fork除了轻量级之外的一切(它用于fork进行后台保存).
创建线程的底层系统调用是clone(2)(它是Linux 特定的)。顺便说一句,Linux 系统调用列表位于syscalls(2)上,您可以使用strace(1)命令来了解某些进程或命令完成的系统调用。进程通常是使用fork(2)(或vfork(2)来创建的,现在它没有多大用处)。但是,您可以(某些 C 标准库可能会这样做)使用某种特定形式的clone. 我猜想内核正在共享一些代码来实现clone等fork...(因为某些功能,例如虚拟地址空间的管理,是常见的)。
事实上,在大多数 Unix 系统上,进程创建(以及线程创建)通常都非常快(因为它们对虚拟内存使用写时复制机制),通常只有毫秒的一小部分。但你可能会遇到病态的情况(例如殴打),这会使时间更长。
由于大多数C 标准库实现都是Linux 上的免费软件,因此您可以研究系统上的库的源代码(通常是GNU glibc,但有时是 musl-libc或其他东西)。