Linux中fork()调用的源代码在哪里?

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

有什么想法可以找到吗?我一直在浏览GCC和Linux代码,但仍未设法找到它.

编辑:我正在尝试找到我的系统正在使用的确切实现.正如评论中所提及的,在Link中它显然在glibc的一些包装中.任何想法在glibc我可以找到包装器.我经常搜索但找不到它的定义.

最佳答案
以x86平台和2.6.23 Linux内核为参考:

>创建test-fork.c文件

#include fork();
    return 0;
}

>使用静态链接编译它:gcc -O0 -static -Wall test-fork.c -o test-fork
>拆卸它:objdump -D -S test-fork>测试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与syscall number 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,NULL,NULL);
  }

请注意,仅使用SIGCHLD参数调用do_fork()
>打开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;
  }

>分叉的大部分工作由do_fork()处理,
在kernel / fork.c中定义. do_fork()执行的操作:

>它通过调用alloc_pid()为子节点分配一个新的PID
>它检查父项的ptrace字段(即current-> ptrace)

>如果它不为零,则父进程将被另一个进程跟踪

>它调用copy_process(),它设置进程描述符和子进程执行所需的任何其他内核数据结构

>其参数与do_fork()加上子项的PID相同
>它检查clone_flags参数中传递的标志是否兼容
>它通过调用security_task_create()和security_task_alloc()执行其他安全检查
>它调用dup_task_struct(),它为新进程创建新的内核堆栈,thread_info和task_struct结构.

>新值与当前任务的值相同
>此时,子进程描述符和父进程描述符完全相同
>它执行alloc_task_struct()宏以获取新进程的task_struct结构,并将其地址存储在tsk局部变量中.
>它执行alloc_thread_info宏以获得一个空闲内存区域来存储新进程的thread_info结构和内核模式堆栈,并将其地址保存在ti局部变量中
>它将当前进程描述符的内容复制到tsk指向的task_struct结构中,然后将tsk-> thread_info设置为ti
>它将当前thread_info描述符的内容复制到ti指向的结构中,然后将ti->任务设置为tsk
>它将新进程描述符的使用计数器(即tsk->用法)设置为2,以指定进程描述符正在使用中,并且相应的进程处于活动状态(其状态不是EXIT_ZOMBIE或EXIT_DEAD)
>它返回新进程的进程描述符指针(即tsk)

> copy_process()然后检查是否未超过当前用户的最大进程数(即大于’max_threads)

>它通过清除或初始化task_struct的各个字段来区分子项与父项
>它调用copy_flags()来更新task_struct的flags字段

> PF_SUPERPRIV(表示任务是否使用超级用户权限)和PF_NOFREEZE标志被清除
>设置PF_FORKNOEXEC标志(表示任务是否未调用`exec())
>它调用`init_sigpending()来清除挂起的信号
>根据传递给do_fork()的参数,copy_process()`然后复制或共享资源
>打开文件
>文件系统信息
>信号处理程序
>地址空间
>它调用sched_fork(),它将父级和子级之间的剩余时间片分开
>最后,它返回一个指向新子节点的指针

>然后,do_fork()在设置了CLONE_STOPPED标志或者必须跟踪子进程的情况下添加一个待处理的SIGSTOP信号(即,在p-> ptrace中设置了PT_PTRACED标志)
>如果未设置CLONE_STOPPED标志,则调用wake_up_new_task()函数,该函数执行以下操作:

>它调整父项和子项的调度参数
>如果子进程与父进程在同一cpu上运行,并且父进程和子进程没有共享同一组页表(即CLONE_VM标志已清除),则会强制子进程在父进程之前运行,方法是将其插入到父进程的运行队列中在父母之前.如果子项刷新其地址空间并在分叉后立即执行新程序,则此简单步骤可以获得更好的性能.如果我们让父进程先运行,则copy On Write机制会引起一系列不必要的页面重复.
>否则,如果子节点不与父节点在同一cpu上运行,或者父节点和子节点共享同一组页面表(即
CLONE_VM标志设置),它将子项插入父项运行队列的最后位置

>否则,如果设置了CLONE_STOPPED标志,则将子项置于TASK_STOPPED状态
>如果正在跟踪父进程,它将存储子进程的PID
当前的ptrace_message字段并调用
ptrace_notify(),它基本上停止当前进程并向其父进程发送SIGCHLD信号.孩子的“祖父母”是跟踪父母的调试者; SIGCHLD信号通知调试器当前已经分叉了一个子节点,可以通过查看current-> ptrace_message字段来检索其PID.
>如果指定了CLONE_VFORK标志,它会将父进程插入等待队列并暂停它,直到子进程释放其内存地址空间(即,直到子进程终止或执行新程序)

>它通过返回子的PID来终止.

相关文章

1、安装Apache。 1)执行如下命令,安装Apache服务及其扩展包...
一、先说一下用ansible批量采集机器信息的实现办法: 1、先把...
安装配置 1. 安装vsftpd 检查是否安装了vsftpd # rpm -qa | ...
如何抑制stable_secret读取关键的“net.ipv6.conf.all.stabl...
1 删除0字节文件 find -type f -size 0 -exec rm -rf {} ...
## 步骤 1:安装必要的软件包 首先,需要确保系统已安装 `dh...