c - Linux 中 fork() 调用的来源在哪里?

标签 c linux gcc linux-kernel fork

关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。












想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。

5年前关闭。




Improve this question




我花了很多时间试图找到 fork() 的源代码。功能。我知道大部分工作都是由 fork() 完成的由 do_fork() 完成可以在 kernel/fork.c 中找到.但是我想看到的是 fork() 的源代码功能。

任何可以找到它的想法?我一直在浏览 GCC 和 Linux 源代码,但仍然没有找到它。

编辑:我试图找到我的系统正在使用的确切实现。正如评论和此 Link 中所述它显然在 glibc 中的一些包装器中。任何想法我可以在 glibc 中找到包装器。我已经彻底搜索过,但找不到它的定义。

最佳答案

以x86平台和2.6.23 Linux内核为引用:

  • 创建 test-fork.c文件:
    #include <unistd.h>
    
    int main (void)
    {
        fork();
        return 0;
    }
    
  • 使用静态链接编译它:gcc -O0 -static -Wall test-fork.c -o test-fork
  • 拆机:objdump -D -S test-fork > test-fork.dis
  • 打开 test-fork.dis文件并搜索 fork :
            fork();
     80481f4:       e8 63 55 00 00          call   804d75c <__libc_fork>
            return 0;
     80481f9:       b8 00 00 00 00          mov    $0x0,%eax
    }
     80481fe:       c9                      leave  
     80481ff:       c3                      ret    
    
  • 然后搜索__libc_fork :
     0804d75c <__libc_fork>:
     804d75c:       55                      push   %ebp
     804d75d:       b8 00 00 00 00          mov    $0x0,%eax
     804d762:       89 e5                   mov    %esp,%ebp
     804d764:       53                      push   %ebx
     804d765:       83 ec 04                sub    $0x4,%esp
     804d768:       85 c0                   test   %eax,%eax
     804d76a:       74 12                   je     804d77e <__libc_fork+0x22>
     804d76c:       c7 04 24 80 e0 0a 08    movl   $0x80ae080,(%esp)
     804d773:       e8 88 28 fb f7          call   0 <_init-0x80480d4>
     804d778:       83 c4 04                add    $0x4,%esp
     804d77b:       5b                      pop    %ebx
     804d77c:       5d                      pop    %ebp
     804d77d:       c3                      ret    
     804d77e:       b8 02 00 00 00          mov    $0x2,%eax
     804d783:       cd 80                   int    $0x80
     804d785:       3d 00 f0 ff ff          cmp    $0xfffff000,%eax
     804d78a:       89 c3                   mov    %eax,%ebx
     804d78c:       77 08                   ja     804d796 <__libc_fork+0x3a>
     804d78e:       89 d8                   mov    %ebx,%eax
     804d790:       83 c4 04                add    $0x4,%esp
     804d793:       5b                      pop    %ebx
     804d794:       5d                      pop    %ebp
     804d795:       c3                      ret    
    

    请注意,在此特定硬件/内核上 fork 相关联系统调用号 2
  • 下载 Linux 内核的副本:wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.23.tar.bz2
  • 打开 linux-2.6.23/arch/x86/kernel/syscall_table_32.S文件
  • 请注意,系统调用编号 2 与
    sys_fork:
         .long sys\_fork   /* 2 */
    
  • 打开 linux-2.6.23/arch/x86/kernel/process.c文件
  • 搜索 sys_fork :
      asmlinkage int sys_fork(struct pt_regs regs)
      {
              return do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
      }
    

    请注意 do_fork()仅使用 SIGCHLD 调用参数
  • 打开 linux-2.6.23/kernel/fork.c文件。这里是do_fork()被定义为!
  • do_fork()然后拨打 copy_process() :
      /*
       *  Ok, this is the main fork-routine.
       *
       * It copies the process, and if successful kick-starts
       * it and waits for it to finish using the VM if required.
       */
      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)
      {
              struct task_struct *p;
              int trace = 0;
              struct pid *pid = alloc_pid();
              long nr;
    
              if (!pid)
                      return -EAGAIN;
              nr = pid->nr;
              if (unlikely(current->ptrace)) {
                      trace = fork_traceflag (clone_flags);
                      if (trace)
                              clone_flags |= CLONE_PTRACE;
              }
    
              p = copy_process(clone_flags, stack_start, regs, stack_size, \
                               parent_tidptr, child_tidptr, pid);
    
    
           /*
             * Do this prior waking up the new thread - the thread 
             * pointer might get invalid after that point, 
             * if the thread exits quickly.
             */
            if (!IS_ERR(p)) {
                    struct completion vfork;
    
                    if (clone_flags & CLONE_VFORK) {
                            p->vfork_done = &vfork;
                            init_completion(&vfork);
                    }
    
                    if ((p->ptrace & PT_PTRACED) || \
                        (clone_flags & CLONE_STOPPED)) {
                            /*
                             * We'll start up with an immediate SIGSTOP.
                             */
                            sigaddset(&p->pending.signal, SIGSTOP);
                            set_tsk_thread_flag(p, TIF_SIGPENDING);
                    }
    
                    if (!(clone_flags & CLONE_STOPPED))
                            wake_up_new_task(p, clone_flags);
                    else
                            p->state = TASK_STOPPED;
    
                    if (unlikely (trace)) {
                            current->ptrace_message = nr;
                            ptrace_notify ((trace << 8) | SIGTRAP);
                    }
    
                     if (clone_flags & CLONE_VFORK) {
                              freezer_do_not_count();
                              wait_for_completion(&vfork);
                              freezer_count();
                              if (unlikely (current->ptrace & \
                                            PT_TRACE_VFORK_DONE)) {
                                      current->ptrace_message = nr;
                                      ptrace_notify \
                                        ((PTRACE_EVENT_VFORK_DONE << 8) | \
                                          SIGTRAP);
                              }
                      }
              } else {
                      free_pid(pid);
                      nr = PTR_ERR(p);
              }
              return nr;
      }
    
  • fork 中的大部分工作由 do_fork() 处理。 ,
    定义于 kernel/fork.c
    .由 执行的操作do_fork() :
  • 它通过调用 alloc_pid() 为子进程分配一个新的 PID。
  • 它检查 ptrace父字段(即 current->ptrace )
  • 如果它不为零,则另一个进程正在跟踪父进程
  • 它调用 copy_process() ,它设置进程描述符和子进程执行所需的任何其他内核数据结构
  • 其参数同do_fork()加上 child 的PID
  • 它检查标志是否通过 clone_flags参数兼容
  • 它通过调用 security_task_create() 执行额外的安全检查。和 security_task_alloc()
  • 它调用 dup_task_struct() 创建新的内核堆栈,thread_infotask_struct新流程的结构。
  • 新值与当前任务的值相同
  • 此时子进程和父进程描述符是相同的
  • 它执行 alloc_task_struct()宏获取 task_struct新进程的结构,并将其地址存储在 tsk局部变量。
  • 它执行 alloc_thread_info宏来获得一个空闲内存区域来存储 thread_info结构和新进程的内核模式堆栈,并将其地址保存在ti局部变量
  • 它将当前进程描述符的内容复制到 task_struct tsk指向的结构,然后设置 tsk->thread_infoti
  • 它复制当前 thread_info 的内容描述符到 ti 指向的结构中,然后设置 ti->tasktsk
  • 它将新进程描述符(即 tsk->usage)的使用计数器设置为 2 以指定进程描述符正在使用中并且相应的进程处于事件状态(其状态不是 EXIT_ZOMBIEEXIT_DEAD )
  • 它返回新进程的进程描述符指针(即 tsk )
  • copy_process()然后检查是否没有超过当前用户的最大进程数(即大于`max_threads)
  • 它通过清除或初始化 task_struct 的各个字段来区分子进程和父进程。
  • 它调用 copy_flags()更新 flags领域task_struct
  • PF_SUPERPRIV (表示任务是否使用 super 用户权限)和 PF_NOFREEZE标志被清除
  • PF_FORKNOEXEC设置标志(表示任务是否未调用 `exec())
  • 它调用`init_sigpending() 来清除挂起的信号
  • 取决于传递给 do_fork(), 的参数copy_process()` 然后复制或共享资源
  • 打开文件
  • 文件系统信息
  • 信号处理程序
  • 地址空间
  • 它调用 sched_fork()它在父子之间分割剩余的时间片
  • 最后,它返回一个指向新子节点
  • 的指针。
  • 然后,do_fork()添加待处理 SIGSTOP信号以防万一CLONE_STOPPED设置标志或必须跟踪子进程(即 PT_PTRACED 标志设置在 p->ptrace 中)
  • 如果CLONE_STOPPED标志未设置,它调用 wake_up_new_task()函数,执行以下操作:
  • 调整父子进程的调度参数
  • 如果子进程与父进程在同一个 CPU 上运行,并且父进程和子进程不共享同一组页表(即 CLONE_VM 标志已清除),则它会通过将子进程插入父进程来强制子进程在父进程之前运行在父级之前运行队列。如果子进程在 fork 后立即刷新其地址空间并执行新程序,则此简单步骤会产生更好的性能。如果我们让父进程先运行,Copy On Write 机制会导致一系列不必要的页面重复。
  • 否则,如果子进程不会与父进程在同一个 CPU 上运行,或者父进程和子进程共享同一组页表(即CLONE_VM标志设置),它将子进程插入到父进程运行队列的最后位置
  • 否则,如果 CLONE_STOPPED标志被设置,它把 child 放在 TASK_STOPPED状态
  • 如果正在跟踪父进程,它将子进程的 PID 存储在ptrace_message领域current并调用ptrace_notify() ,它基本上停止当前进程并发送 SIGCHLD向其 parent 发出信号。 child 的“祖 parent ”是跟踪 parent 的调试器; SIGCHLD信号通知调试器当前已经 fork 了一个 child ,其PID可以通过查看current->ptrace_message来检索。 field 。
  • 如果CLONE_VFORK指定标志,它将父进程插入等待队列并挂起它,直到子进程释放其内存地址空间(即,直到子进程终止或执行新程序)
  • 它通过返回子进程的 PID 终止。
  • 关于c - Linux 中 fork() 调用的来源在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34871103/

    相关文章:

    c - C 中带有 termios : unreliable output capitalization 的串行 I/O

    linux - 终端中的重定向顺序是什么?

    linux - strace epoll_wait 时间高

    c - 链接器在节中添加额外的填充?

    c++ - 未使用的类已删除

    c - 使用函数加载数组元素

    c - 在哪里复制 mpi 的可执行文件?

    c - scanf 不读取 mychars

    php - 如何在不授予完全公开写入权限的情况下允许 PHP 中的文件上传?

    android - Android中的ffmpeg + libfaac编译错误