fork()如何为子进程返回

Eps*_*tor 12 linux fork

我知道fork()对子进程和父进程的返回方式不同,但我无法找到有关如何发生这种情况的信息.子进程如何从fork接收返回值0?有关调用堆栈的区别是什么?据我了解,对于父母来说,它是这样的:

parent进程 - 调用fork - > system_call - 调用fork - > fork执行 - 返回 - > system_call - 返回 - > parent进程.

儿童过程会发生什么?

Ste*_*ker 23

%man fork

返回值

Upon successful completion, fork() returns a value of 0 to the child
process and returns the process ID of the child process to the parent
process.  Otherwise, a value of -1 is returned to the parent process, no
child process is created, and the global variable [errno][1] is set to indi-
cate the error.
Run Code Online (Sandbox Code Playgroud)

发生的事情是在fork系统调用中,整个过程是重复的.然后,每个fork调用返回.现在这些是不同的上下文,因此它们可以返回不同的返回码.

如果您真的想知道它在低级别的工作原理,您可以随时查看来源!如果你不习惯阅读内核代码,代码有点混乱,但是内联注释提供了一个很好的提示,告诉你发生了什么.

对你的问题有明确答案的源代码中最有趣的部分是fork()定义本身的最后一部分 -

if (error == 0) {
    td->td_retval[0] = p2->p_pid;
    td->td_retval[1] = 0;
}
Run Code Online (Sandbox Code Playgroud)

"td"显然包含不同线程的返回值列表.我不确定这个机制是如何工作的(为什么没有两个独立的"线程"结构).如果错误(从fork1返回,"真正的"分叉函数)为0(无错误),则取"第一个"(父)线程并将其返回值设置为p2(新进程)的PID.如果它是"第二个"线程(在p2中),则将返回值设置为0.


Jon*_*ler 7

fork()系统调用返回两次(除非它失败).

  • 其中一个返回在子进程中,返回值为0.

  • 另一个返回在父进程中,并且返回值为非零(如果fork失败则为负数,或者指示子项的PID的非零值).

父母和孩子之间的主要区别是:

  • 它们是独立的过程
  • PID的值不同
  • PPID(父PID)的值是不同的

POSIX标准中列出了其他更加模糊的差异.

在某种意义上,如何真的不是你的问题.操作系统需要达到结果.然而,O/S的克隆父进程,使得第二子方法,该方法是父的几乎精确的复制品,设定必须是正确的新值的不同的属性,且通常为标记数据页为CoW的(复制上写入)或等效的,以便当一个进程修改一个值时,它获得一个单独的页面副本,以免干扰另一个.这与被弃用的(至少我 - 非标准的POSIX)vfork()系统调用不同,即使它在您的系统上可用,您也应该避免使用它.每个进程fork()在函数返回之后继续- 所以(正如我上面所说的那样),fork()系统调用返回两次,两次进程中的每一次都是一次,这两个进程几乎是彼此相同的克隆.

  • 我希望vfork()粉丝男孩会解释为什么他们投了一票. (4认同)

小智 7

由于在子上下文中操作CPU寄存器,父和子都返回不同的值.

linux内核中的每个进程都由task_struct表示.task_struct在thread_info结构中被包含(指针),该结构位于内核模式堆栈的末尾.所有CPU上下文(寄存器)都存储在此thread_info结构中.

struct thread_info {
    struct task_struct  *task;      /* main task structure */
    struct cpu_context_save cpu_context;    /* cpu context */
}
Run Code Online (Sandbox Code Playgroud)

所有fork/clone()系统调用都调用内核等效函数do_fork().

long do_fork(unsigned long clone_flags,
          unsigned long stack_start,
          struct pt_regs *regs,
          unsigned long stack_size,
          int __user *parent_tidptr,
          int __user *child_tidptr)
Run Code Online (Sandbox Code Playgroud)

这是执行的顺序

do_fork() - > copy_process-> copy_thread() (copy_thread是特定于arch的函数调用)

copy_thread()从父项复制寄存器值并将返回值更改为0(如果是arm)

struct pt_regs *childregs = task_pt_regs(p); 
*childregs = *regs; /* Copy  register value from parent process*/
childregs->ARM_r0 = 0; /*Change the return value*/
thread->cpu_context.sp = (unsigned long)childregs;/*Write back the value to thread info*/
thread->cpu_context.pc = (unsigned long)ret_from_fork;
Run Code Online (Sandbox Code Playgroud)

当孩子被安排时,它执行一个程序集例程ret_from_fork(),它将返回零.对于父级,它从do_fork()获取返回值,该值是进程的pid

nr = task_pid_vnr(p);
return nr;
Run Code Online (Sandbox Code Playgroud)