如何在unix中使用fork()?为什么不用fork(pointerToFunctionToRun)形式的东西?

dev*_*ium 8 unix multithreading fork process

我在理解如何使用Unix方面遇到了一些麻烦fork().我习惯于在需要并行化时,在我的应用程序中生成线程.它始终是形式的东西

CreateNewThread(MyFunctionToRun());

void myFunctionToRun() { ... }
Run Code Online (Sandbox Code Playgroud)

现在,在学习Unix时fork(),我得到了表单的例子:

fork();
printf("%d\n", 123);
Run Code Online (Sandbox Code Playgroud)

其中fork之后的代码是"拆分"的.我无法理解fork()如何有用.为什么fork()与上面的CreateNewThread()没有类似的语法,你传递它想要运行的函数的地址?

为了完成类似于CreateNewThread()的事情,我必须要有创造力并做类似的事情

//pseudo code
id = fork();

if (id == 0) { //im the child
    FunctionToRun();
} else { //im the parent
    wait();
}
Run Code Online (Sandbox Code Playgroud)

也许问题是我习惯于以.NET方式生成线程,我无法清楚地思考这个问题.我在这里错过了什么?有什么优势fork()CreateNewThread()

PS:我知道fork()会产生一个新进程,同时CreateNewThread()会产生一个新线程.

谢谢

Jer*_*man 9

fork()他说:"将当前流程状态复制到一个新流程中,并从此处开始运行." 因为代码然后在两个进程中运行,它实际上返回两次:一次在父进程中(在那里它返回子进程的进程标识符),一次在子进程中(在它返回零的情况下).

在子进程之后调用的安全性有很多限制fork()(见下文).期望是fork()调用是产生运行具有其自身状态的新可执行文件的新进程的第一部分.此过程的第二部分是execve()对其变体的调用,或者其中一个变体,它指定要加载到当前正在运行的进程中的可执行文件的路径,要提供给该进程的参数以及围绕该进程的环境变量.(没有什么可以阻止你重新执行当前正在运行的可执行文件,并提供一个标志,使其在父节点停止的地方进行提取,如果这是你真正想要的那样.)

UNIX fork()-exec()舞蹈大致相当于Windows CreateProcess().更新的功能更像是:posix_spawn().

作为使用的实际例子fork(),考虑一个shell,例如bash.fork()由命令shell一直使用.当你告诉shell运行程序(例如echo "hello world")时,它会自行分配然后执行该程序.管道是与分叉进程的集合stdoutstdin由之间的父适当装配了fork()exec().

如果要创建新线程,则应使用Posix线程库.您使用创建一个新的Posix线程(pthread)pthread_create().您的CreateNewThread()示例如下所示:

#include <pthread.h>

/* Pthread functions are expected to accept and return void *. */ 
void *MyFunctionToRun(void *dummy __unused);

pthread_t thread;
int error = pthread_create(&thread,
        NULL/*use default thread attributes*/,
        MyFunctionToRun,
        (void *)NULL/*argument*/);
Run Code Online (Sandbox Code Playgroud)

在线程可用之前,fork()UNIX是最接近多线程的东西.现在线程可用,使用fork()几乎完全限于生成执行不同可执行文件的新进程.

下面:限制是因为fork()早于多线程,所以只有调用的线程fork()继续在子进程中执行.按POSIX:

应使用单个线程创建进程.如果多线程进程调用fork(),则新进程应包含调用线程的副本及其整个地址空间,可能包括互斥锁和其他资源的状态.因此,为了避免错误,子进程可能只执行异步信号安全操作,直到调用其中一个exec函数为止.[THR] [Option Start]可以通过pthread_atfork()函数建立fork处理程序,以便跨fork()调用维护应用程序不变量.[选项结束]

当应用程序从信号处理程序调用fork()并且由pthread_atfork()注册的任何fork处理程序调用不是异步信号安全的函数时,行为是未定义的.

因为您调用的任何库函数都可能代表您生成一个线程,所以偏执的假设是您始终只能在调用fork()和调用之间的子进程中执行异步信号安全操作exec().


Kim*_*ard 7

除了历史之外,在资源所有权和流程与线程之间的生命周期方面存在一些根本差异.

fork时,新进程占用一个完全独立的内存空间.这与创建新线程非常重要.在多线程应用程序中,您必须考虑如何访问和操作共享资源.已经分叉的已处理必须使用进程间方式显式共享资源,例如共享内存,管道,远程过程调用,信号量等.

另一个区别是fork()'ed的子节点可以比它们的父节点寿命更长,因为当进程终止时所有线程都会死掉.

在客户端 - 服务器架构中,预期非常非常长的正常运行时间,使用fork()而不是创建线程可能是一种有效的策略来对抗内存泄漏.您不必担心清除线程中的内存泄漏,而只是分叉一个新的子进程来处理每个客户端请求,然后在完成时杀死它.然后,唯一的内存泄漏源是调度事件的父进程.

类比:您可以将产生线程视为在单个浏览器窗口中打开选项卡,而分叉就像打开单独的浏览器窗口一样.


Ton*_*roy 6

要问为什么CreateNewThread不仅仅返回一个类似于fork()... 的线程ID 会更有效.毕竟fork()设置一个先例.你的意见只是在你看到一个在另一个之前.退后一步,考虑fork()复制过程并继续执行......比下一条指令更好的地方?为什么通过在讨价还价中添加一个函数调用来复杂化(然后只需要一个void*)?

你对Mike的评论说:"我无法理解你在哪种情况下想要使用它." .基本上,您可以在需要时使用它:

  • 使用exec系列函数运行另一个进程
  • 独立进行一些并行处理(在内存使用,信号处理,资源,安全性,健壮性方面),例如:
    • 每个进程可能会限制他们可以管理的文件描述符的数量,或者在32位系统上 - 内存量:第二个进程可以在获取自己的资源时共享工作
    • Web浏览器倾向于分叉不同的进程,因为它们可以进行一些初始化,然后调用操作系统函数来永久地减少它们的权限(例如,更改为不太可信的用户ID,更改可以访问文件的"root"目录,或者制作一些内存页面只读); 大多数操作系统不允许在每个线程的基础上进行相同程度的细粒度权限设置; 另一个好处是,如果一个子进程出现seg-faults或类似的父进程可以处理并继续,而多线程代码中的类似错误会引发关于内存是否已被损坏 - 或者锁定已被保留 - 的问题,以致崩溃线程剩余的线程受到损害

BTW /使用UNIX/Linux并不意味着你必须放弃fork()进程的线程...... pthread_create()如果你对线程范例更加熟悉,你可以使用和相关的函数.

  • @devoured elysium:当然不是:camel_case对Unix来说是可恶的. (4认同)
  • @devoured elysium,不是真的.它提供了exec(programPath ..)和类似的,但是它不会创建一个新进程,它会用programPath的图像替换现有进程.win32中的CreateProcess()创建一个新进程并加载程序映像.在*nix中你分两步完成,fork()创建一个新进程,exec()加载程序. (2认同)