我花了很多时间试图找到fork()函数的源代码.我知道fork()完成的大部分工作都是由do_fork()完成的,可以在kernel / fork.c中找到.但是我想看到的是fork()函数的源代码.
有什么想法可以找到吗?我一直在浏览GCC和Linux源代码,但仍未设法找到它.
编辑:我正在尝试找到我的系统正在使用的确切实现.正如评论中所提及的,在Link中它显然在glibc的一些包装中.任何想法在glibc我可以找到包装器.我经常搜索但找不到它的定义.
>创建test-fork.c文件:
#include
>使用静态链接编译它: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,®s,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来终止.