Mac*_*333 49 c unix fork process internals
请看以下示例:
int main(void)
{
pid_t pid;
pid = fork();
if (pid == 0)
ChildProcess();
else
ParentProcess();
}
Run Code Online (Sandbox Code Playgroud)
所以纠正我,如果我错了,一旦fork()执行子进程被创建.现在通过这个答案 fork()返回两次.这对于父进程来说是一次,对于子进程则是一次.
这意味着在fork调用期间存在两个独立的进程,而不是在结束之后.
现在我不明白它如何理解如何为子进程返回0以及为父进程返回正确的PID.
这让它变得非常混乱.这个回答指出fork()通过复制进程的上下文信息并手动将返回值设置为0来工作.
首先,我说对任何函数的返回都放在一个寄存器中吗?因为在单个处理器环境中,进程只能调用一个只返回一个值的子例程(如果我错了,请纠正我).
假设我在例程中调用函数foo()并且该函数返回一个值,该值将存储在一个名为BAR的寄存器中.每次函数想要返回一个值时,它将使用特定的处理器寄存器.因此,如果我能够手动更改过程块中的返回值,我可以更改返回到函数的值吗?
所以我认为fork()的工作原理是正确的吗?
pax*_*blo 54
如何它的工作原理基本上是无能为力的-作为一个开发者在一定水平的工作(即,编码到UNIX的API),你真的只需要知道的是它的工作原理.
话虽如此但是,并认识到好奇心或需要在一定的深度理解通常是一个很好的特点有,有任意数量的这种方式可以实现.
首先,你认为函数只能返回一个值的论点是正确的,但是你需要记住,在进程拆分之后,实际上有两个函数运行实例,每个进程一个.它们大多是相互独立的,可以遵循不同的代码路径.下图可能有助于理解这一点:
Process 314159 | Process 271828
-------------- | --------------
runs for a bit |
calls fork |
| comes into existence
returns 271828 | returns 0
Run Code Online (Sandbox Code Playgroud)
您可以希望看到单个实例fork
只能返回一个值(根据任何其他C函数)但实际上有多个实例在运行,这就是为什么它会在文档中返回多个值.
下面是它如何一种可能性可以工作.
当fork()
函数开始运行时,它存储当前进程ID(PID).
然后,当返回时,如果PID与存储的PID相同,则它是父项.否则就是孩子.伪代码如下:
def fork():
saved_pid = getpid()
# Magic here, returns PID of other process or -1 on failure.
other_pid = split_proc_into_two();
if other_pid == -1: # fork failed -> return -1
return -1
if saved_pid == getpid(): # pid same, parent -> return child PID
return other_pid
return 0 # pid changed, child, return zero
Run Code Online (Sandbox Code Playgroud)
请注意,在split_proc_into_two()
通话中有很多魔法,它几乎肯定不会在封面(a)下以这种方式工作.它只是为了说明它周围的概念,基本上是:
您可能还想看一下这个答案,它解释了这个fork/exec
理念.
(a)这几乎肯定比我解释的更复杂.例如,在MINIX中,调用fork
最终在内核中运行,该内核可以访问整个进程树.
它只是将父进程结构复制到子进程的空闲槽中,顺序如下:
sptr = (char *) proc_addr (k1); // parent pointer
chld = (char *) proc_addr (k2); // child pointer
dptr = chld;
bytes = sizeof (struct proc); // bytes to copy
while (bytes--) // copy the structure
*dptr++ = *sptr++;
Run Code Online (Sandbox Code Playgroud)
然后它对子结构稍作修改以确保它是合适的,包括以下行:
chld->p_reg[RET_REG] = 0; // make sure child receives zero
Run Code Online (Sandbox Code Playgroud)
所以,基本上与我提出的方案相同,但是使用数据修改而不是代码路径选择来决定返回给调用者的内容 - 换句话说,你会看到如下内容:
return rpc->p_reg[RET_REG];
Run Code Online (Sandbox Code Playgroud)
在结尾处,fork()
以便返回正确的值,具体取决于它是父进程还是子进程.
Ant*_*ala 29
在Linux中fork()
发生在内核中; 实际的地方就在_do_fork
这里.简化后,fork()
系统调用可能就像
pid_t sys_fork() {
pid_t child = create_child_copy();
wait_for_child_to_start();
return child;
}
Run Code Online (Sandbox Code Playgroud)
所以在内核中,fork()
真正返回一次,进入父进程.但是,内核还会将子进程创建为父进程的副本; 但它不是从普通函数返回,而是合成地为子进程的新创建的线程创建新的内核堆栈 ; 然后上下文切换到该线程(和进程); 当新创建的进程从上下文切换函数返回时,它将使子进程的线程最终返回到用户模式,其中0为返回值fork()
.
基本上fork()
在userland中只是一个瘦包装器返回内核放入其堆栈/返回寄存器的值.内核设置新的子进程,以便它通过这个机制从它唯一的线程返回0; 并且子系统pid在父系统调用中返回,作为来自任何系统调用的任何其他返回值read(2)
.
Jea*_*nès 10
您首先需要了解多任务处理的工作原理.理解所有细节是没有用的,但是每个进程都在由内核控制的某种虚拟机中运行:一个进程有自己的内存,处理器和寄存器等.这些虚拟对象映射到真实的虚拟机上(魔法在内核中),并且有一些机器随着时间的推移将虚拟上下文(进程)交换到物理机器.
然后,当内核分叉进程(fork()
是内核的一个条目),并在子进程的父进程中创建几乎所有内容的副本时,它就能够修改所需的所有内容.其中一个是修改相应的结构,为子节点返回0,父节点的pid从当前调用fork返回.
注意:nether说"fork返回两次",函数调用只返回一次.
想想一个克隆机:你单独进入,但两个人退出,一个是你,另一个是你的克隆(非常不同); 克隆机器时可以设置与克隆不同的名称.
fork系统调用创建一个新进程并从父进程复制许多状态.像文件描述符表这样的东西被复制,内存映射和它们的内容等等.这个状态在内核中.
内核跟踪每个进程的一个原因是这个进程需要在系统调用,陷阱,中断或上下文切换返回时恢复的寄存器值(大多数上下文切换发生在系统调用或中断上).这些寄存器保存在系统调用/陷阱/中断中,然后在返回用户空间时恢复.系统调用通过写入该状态返回值.叉子是做什么的.父fork获取一个值,子进程获取另一个值.
由于分叉进程与父进程不同,因此内核可以对其执行任何操作.在寄存器中给它任何值,给它任何内存映射.实际上要确保除了返回值之外的几乎所有内容都与父进程中的相同,这需要更多的努力.