Linux中fork()调用的源在哪里? [英] Where is the source for the fork() call in Linux?
问题描述
我花了相当长的时间试图找到fork()
函数的源代码。我知道fork()
完成的大部分工作都是由do_fork()
完成的,这可以在kernel/fork.c
中找到。但是,我希望看到的是fork()
函数的源代码。
你知道在哪里可以找到它吗?我翻了GCC和LINUX的源码,还是没找到。
编辑:我正在尝试找到我的系统正在使用的确切实现。正如在注释和Link中提到的,它显然位于glibc的某个包装器中。知道在glibc的什么地方可以找到包装器吗?我找遍了,但找不到它的定义。
推荐答案
参考x86平台和2.6.23内核:
创建
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, ®s, 0, NULL, NULL); }
请注意,仅使用
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; }
中的大部分工作由处理, 定义于
执行的操作kernel/fork.c
。do_fork()
:- 它通过调用
alloc_pid()
为孩子分配新的ID
- 它检查父级的
ptrace
字段(即current->ptrace
)- 如果不为零,则另一个进程正在跟踪父进程
它调用
copy_process()
,设置进程描述符和子进程执行所需的任何其他内核数据结构- 其参数与
do_fork()
加下级的ID相同 - 它检查
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->task
设置为tsk
- 它将新的进程描述符(即
tsk->usage
)的使用计数器设置为2,以指定该进程描述符正在使用中,并且对应的进程处于活动状态(其状态不是EXIT_ZOMBIE
或EXIT_DEAD
) - 返回新进程的进程描述符指针(即
tsk
)
copy_process()
然后检查是否未超过当前用户的最大进程数(即,大于`max_线程)
- 其参数与
- 它通过清除或初始化
task_struct
的各个字段来区分子级和父级
它调用
的copy_flags()
来更新task_struct
flags
字段- 清除
PF_SUPERPRIV
(表示任务是否使用超级用户权限)和PF_NOFREEZE
标志 - 设置
PF_FORKNOEXEC
标志(表示任务是否尚未调用`exec()) - 它调用清除挂起信号的`init_sigending()
- 根据传递给
do_fork(),
Copy_Process()`的参数,复制或共享资源 - 打开文件
- 文件系统信息
- 信号处理程序
- 地址空间
- 它调用
sched_fork()
,在父进程和子进程之间分配剩余的时间片 - 最后,它返回一个指向新子对象的指针
- 清除
然后,
do_fork()
在设置了CLONE_STOPPED
标志或子进程必须被跟踪的情况下添加挂起的SIGSTOP
信号(即在p->ptrace
中设置了PT_PTRACED
标志)如果未设置
CLONE_STOPPED
标志,它将调用wake_up_new_task()
函数,该函数执行以下操作:- 同时调整父级和子级的调度参数
- 如果子进程将在与父进程相同的CPU上运行,并且父进程和子进程不共享同一组页表(即
CLONE_VM
标志被清除),则它会强制子进程在父进程之前运行,方法是将其插入父进程的运行队列中,紧靠父进程的前面。如果子进程刷新其地址空间并在派生之后立即执行新程序,则这一简单步骤会产生更好的性能。如果让父进程先运行,写入时拷贝机制将产生一系列不必要的页面复制。 - 否则,如果子进程不会在与父进程相同的CPU上运行,或者父进程和子进程共享同一组页表(即
CLONE_VM
标志集),它将子级插入到父级运行队列的最后位置
- 否则,如果设置了
CLONE_STOPPED
标志,则会将子级置于TASK_STOPPED
状态 如果正在跟踪父进程,则它将子进程的ID存储在 的字段和调用
ptrace_notify()
,它实质上停止当前进程并向其父进程发送SIGCHLD
信号。子对象的"祖父母"是跟踪父对象的调试器;SIGCHLD
信号通知调试器当前已派生了一个子对象,其ID可以通过查看current->ptrace_message
字段来检索。如果指定了
CLONE_VFORK
标志,它会将父进程插入等待队列,并将其挂起,直到子进程释放其内存地址空间(即,直到子进程终止或执行新程序)
- 它通过调用
- 它通过返回子对象的ID而终止。
do_fork()
这篇关于Linux中fork()调用的源在哪里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!