创建test-fork.c
文件:gcc -O0 -static -Wall test-fork.c -o test-fork
拆解:objdump -D -S test-fork > test-fork.dis
打开test-fork.dis
文件和
#include <unistd.h>
int main (void)
{
fork();
return 0;
}
静态链接编译搜索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 */
打开文件
搜索sys_fork
:
asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL);
}
注意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()
- 它检查父的
ptrace
字段
- 它分配一个新的PID为子(即
current->ptrace
)
它调用copy_process()
,其中规定了对孩子的要求的过程描述符和任何其他内核数据结构执行
- 它通过清除或initilizing的
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
信号或子进程必须跟踪(即所述PT_PTRACED
标志在p->ptrace
设置)
如果未设置CLONE_STOPPED
标志,它调用wake_up_new_task()
功能,其执行以下操作:
- 它调整母公司和两者的调度参数孩子
- 如果孩子将相同的CPU作为父母和父母和孩子在跑不共享同一套页表(即
CLONE_VM
标志清零),它然后将其插入迫使孩子家长之前运行父母的跑步权在帕尔之前ENT。如果孩子在分岔后立即刷新地址空间并执行一个新程序,这个简单的步骤会产生更好的性能。如果我们让父代首先运行,那么机制会产生一系列不必要的页面重复。
- 否则,如果孩子不会与父母在同一CPU上运行,或者父母和孩子共享相同的一组页面表(即设置了
CLONE_VM
标志),它会将孩子插入父母的运行队列
- 否则,如果
CLONE_STOPPED
标志设置,它使孩子在TASK_STOPPED
状态
如果父进程正被跟踪,它在 的ptrace_message
字段存储的子进程的PID current
并调用 ptrace_notify()
,它基本上停止当前进程,并向其父节点发送一个SIGCHLD
信号。孩子的“祖父母”是跟踪父母的调试者; SIGCHLD
信号通知调试器当前已经分叉了一个孩子,可以通过查看current->ptrace_message
字段来检索其PID。
如果指定了CLONE_VFORK
标志,它插入父进程处于等待队列并暂停它,直到子释放其存储器地址空间(即,直到子或者终止或者执行一个新的程序)
- 它通过返回孩子的PID终止。
GNU libc围绕内核do_fork使用的“包装器”来自GNU libc。 – alk
@ Programmer123用你的linux发行版的package命令下载你当前的linux内核&glibc的源代码,然后搜索 –
@arsane我已经做到了这一点,并且在过去的几个小时里一直在四处搜寻,但没有任何结果。 – Programmer123