Pro*_*123 7 c linux gcc fork linux-kernel
我花了很多时间试图找到该fork()
函数的源代码.我知道大部分完成的工作都是通过fork()
完成do_fork()
而完成的kernel/fork.c
.但是,我想看到的是该fork()
函数的源代码.
有什么想法可以找到吗?我一直在浏览GCC和Linux源代码,但仍未设法找到它.
编辑:我正在尝试找到我的系统正在使用的确切实现.正如在评论中提到的并且在这个链接中它显然在glibc中的一些包装中.任何想法在glibc我可以找到包装器.我经常搜索但找不到它的定义.
Cla*_*dio 27
以x86平台和2.6.23 Linux内核为参考:
创建test-fork.c
文件:
#include <unistd.h>
int main (void)
{
fork();
return 0;
}
Run Code Online (Sandbox Code Playgroud)使用静态链接编译它: 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
Run Code Online (Sandbox Code Playgroud)然后搜索__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
Run Code Online (Sandbox Code Playgroud)
请注意,此特定硬件/内核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 */
Run Code Online (Sandbox Code Playgroud)打开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);
}
Run Code Online (Sandbox Code Playgroud)
请注意,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;
}
Run Code Online (Sandbox Code Playgroud)分叉的大部分工作由do_fork()
(在中定义)处理kernel/fork.c
.执行的操作do_fork()
:
alloc_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
局部变量中task_struct
指向的结构中tsk
,然后设置tsk->thread_info
为ti
thread_info
描述符的内容复制到指向的结构中ti
,然后设置ti->task
为tsk
tsk->usage
)的使用计数器设置为2,以指定进程描述符正在使用,并且相应的进程是活动的(其状态不是EXIT_ZOMBIE
或EXIT_DEAD
)tsk
)copy_process()
然后检查是否未超过当前用户的最大进程数(即大于'max_threads)
task_struct
它要求copy_flags()
更新该flags
领域task_struct
PF_SUPERPRIV
(这表示如果一个任务使用超级用户权限)和PF_NOFREEZE
标志将被清除PF_FORKNOEXEC
标志(其表示如果一个任务还没有名为`EXEC())被设置do_fork(),
copy_process()的参数,然后复制或共享资源sched_fork()
哪个分隔父母和孩子之间的剩余时间片然后,如果设置了标志或者必须跟踪子进程(即设置了标志),则do_fork()
添加待处理SIGSTOP
信号CLONE_STOPPED
PT_PTRACED
p->ptrace
如果CLONE_STOPPED
未设置该标志,则调用该wake_up_new_task()
函数,该函数执行以下操作:
CLONE_VM
清除了标志),则会强制子进程在父进程之前运行,直到将其插入到父进程的运行队列中父母.如果子项刷新其地址空间并在分叉后立即执行新程序,则此简单步骤可以获得更好的性能.如果我们让父进程先运行,则Copy On Write机制会引起一系列不必要的页面重复.CLONE_VM
标志集),则会将子节点插入父节点运行队列的最后位置CLONE_STOPPED
设置了标志,则将子项置于TASK_STOPPED
状态如果正在跟踪父进程,它会将子进程的PID存储在ptrace_message
字段中current
并调用
ptrace_notify()
,这实际上会停止当前进程并向SIGCHLD
其父进程发送信号.孩子的"祖父母"是跟踪父母的调试者; 该SIGCHLD
信号通知目前已经分叉的一个孩子,他的PID可以通过查看被检索的调试器current->ptrace_message
领域.
如果CLONE_VFORK
指定了该标志,它会将父进程插入等待队列并暂停它,直到子进程释放其内存地址空间(即,直到子进程终止或执行新程序)
来自http://lxr.free-electrons.com/source/kernel/fork.c#L1787,适用于Linux 4.4:
1787 #ifdef __ARCH_WANT_SYS_FORK
1788 SYSCALL_DEFINE0(fork)
1789 {
1790 #ifdef CONFIG_MMU
1791 return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0);
1792 #else
1793 /* can not support in nommu mode */
1794 return -EINVAL;
1795 #endif
1796 }
1797 #endif
Run Code Online (Sandbox Code Playgroud)
我相信这是它定义fork系统调用的地方.在Linux下我相信glibc fork()
函数直接调用这个系统调用而不做任何其他事情.