我知道从一个帖子中调用fork() sys_call
是一个坏主意。
但是,如果线程使用fork()
创建新进程,会发生什么?
新进程将是创建线程的主线程的子进程。我想。
如果其父级首先完成,则新进程将附加到init进程。 它的父节点是主线程,而不是创建它的线程。
如果我错了,请纠正我。
#include <stdio.h>
#include <pthread.h>
int main ()
{
thread_t pid;
pthread_create(&(pid), NULL, &(f),NULL);
pthread_join(tid, NULL);
return 0;
}
void* f()
{
int i;
i = fork();
if (i < 0) {
// handle error
} else if (i == 0) // son process
{
// Do something;
} else {
// Do something;
}
}
答案 0 :(得分:13)
新进程将是创建线程的主线程的子进程。我想。
fork
创建了一个新流程。进程的父进程是另一个进程,而不是线程。因此,新流程的父级是旧流程。
请注意,子进程只有一个线程,因为fork
只复制调用fork
的(堆栈)线程。 (这不完全正确:整个内存重复,但子进程只有一个活动线程。)
如果其父级首先完成,则新进程将附加到init进程。
如果父母先完成,则会向孩子发送SIGHUP
信号。如果孩子因SIGHUP
而未退出,则会以init
为新父母。有关nohup
的更多信息,另请参阅signal(7)
和SIGHUP
的手册页。
它的父亲是主线程,而不是创建它的线程。
进程的父进程是进程,而不是特定的线程,因此说主线程或子线程是父进程是没有意义的。整个过程是父母。
最后要注意的是:必须小心混合线和叉。讨论了一些陷阱here。
答案 1 :(得分:3)
但是,如果线程使用fork()创建新进程会发生什么?
通过复制调用线程的地址空间(而不是进程的整个地址空间),将创建一个新进程。这通常被认为是一个坏主意,因为很难做到正确。 POSIX说子进程(在多线程程序中创建)只能调用异步信号安全函数,直到它调用exec*
函数之一。
如果其父级首先完成,则新进程将附加到init 过程
子进程通常由init进程继承。如果父进程是控制进程(例如shell),那么POSIX requires:
如果该过程是一个控制过程,则应该是SIGHUP信号 发送到前台进程组中的每个进程 控制属于呼叫过程的终端。
然而,对于大多数流程来说并非如此,因为大多数流程都不控制流程。
它的父亲是主线程,而不是创建它的线程。
forked child的父级将始终是调用fork()的进程。所以,PPID是子进程将是你程序的PID。
答案 2 :(得分:2)
如果我错了,请纠正我。
会做:)
由于 fork()
是一个 POSIX 系统调用,它的行为是明确定义的:
应使用单线程创建进程。如果多线程进程调用 fork(),则新进程应包含调用线程及其整个地址空间的副本,可能包括互斥锁和其他资源的状态。因此,为了避免错误,子进程可能只执行异步信号安全操作,直到调用 exec 函数之一。
https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
分叉子进程与其父进程完全相同,但只有在父进程中调用 fork()
的线程仍然存在于子进程中,并且是该子进程的新主线程,直到您调用 exec()
.
POSIX 解密“应使用单个线程创建”具有误导性,因为实际上大多数实现确实会创建父进程的完全副本,因此所有其他线程及其内存也被复制,这意味着线程是事实上,它们只是不能再运行了,因为系统从不为它们分配任何 CPU 时间(它们在调度程序表中丢失)。
一个更简单的心理形象如下:
当父进程调用fork时,整个进程被冻结了一会儿,原子复制,然后父进程整体解冻,而在子进程中,只有调用fork的一个线程被解冻,其他一切都保持冻结状态。
这就是为什么在 fork()
和 exec()
之间执行某些系统调用不是省事的原因,正如 POSIX 标准所指出的那样。理想情况下,除了关闭或复制文件描述符、设置或恢复信号处理程序然后调用 exec()
之外,您不应做更多的事情。
答案 3 :(得分:0)
问题源于fork(2)本身的行为。每当一个新的 使用fork创建子进程(2)新进程获得新进程 内存地址空间但内存中的所有内容都是从旧内存中复制的 进程(写入时写入不是100%真实,而是语义 是一样的。)
如果我们在一个多线程环境中调用fork(2)该线程正在做 该调用现在是新进程中的主线程和所有其他线程 在父进程中运行的线程已经死了。和所有 他们所做的完全就像在调用fork(2)之前一样。
现在想象一下,这些其他线程正在愉快地开展工作 在调用fork(2)之前,几毫秒之后它们就是 死。如果这些现在死去的线程所做的事情并不意味着什么呢 完全离开了吗?
让我举个例子。让我们说我们的主线程(那个 打算叫fork(2))在我们有很多其他的时候正在睡觉 线程愉快地做一些工作。分配内存,写入内存, 从中复制,写入文件,写入数据库等等。 他们可能用malloc(3)之类的东西来分配内存。 好吧,事实证明malloc(3)在内部使用互斥锁来保证 线程安全。而这正是问题所在。
如果其中一个线程使用malloc(3)并获得了 在与主线程调用的完全相同的时刻锁定互斥锁 叉(2)?在新的子进程中,仍然保持锁定 - 由a 现在已死的线程,永远不会归还它。
新的子进程不知道使用malloc是否安全(3) 或不。在最坏的情况下,它将调用malloc(3)并阻塞直到它 获得锁,这将永远不会发生,因为线程是谁 应该归还它已经死了。这只是malloc(3)。想一想 所有其他可能的互斥锁和数据库驱动程序中的锁,文件 处理图书馆,网络图书馆等。
如需完整说明,您可以查看此link。
答案 4 :(得分:0)
Linux内核本身在线程和进程之间没有区别。当进程派生时,它指定与父进程共享哪些内容(内存,打开的文件句柄等)。但这只是一组标志。线程和进程的概念被应用在内核实现之上。
当然,大多数人都是通过libc调用内核的,libc根据线程/进程的通用概念来选择标志。
在操作系统级别,派生线程与派生进程相同。这是UNIX实现之间的(微妙的)差异之一。例如,某些UNIX确实具有线程的概念-然后它们最终出现以下问题:如果派生一个进程,是否要在新进程中复制其所有线程?但是对于Linux,线程和进程本质上是相同的。