中科大SA***243-石润成
一、预备阶段
实验环境:Ubuntu10.04
分析内核版本:Linux-2.6.11
1.编写一个fork和exec程序
myfork.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t pid;
pid = fork();
if(0 == pid)
{
execl("./son","son",NULL);
}
else
if(pid > 0)
{
execl("./parent","parent",NULL);
}
else
{
fprintf(stderr, "fork failure!\n");
return -1;
}
return 0;
}
myparent.c
#include<stdio.h>
int main()
{
printf("I am parent process!\n");
return 0;
}
myson.c
#include<stdio.h>
int main()
{
printf("I am son process!\n");
return 0;
}
各自生成myfork、myparent、myson的ELF格式文件。打印结果:
I am parent process!
I am son process!
二、分析fork系统调用在内核中的执行过程
1)关于系统调用fork()、vfork()、clone()
系统调用__clone()的主要用途是创建一个线程,这个线程可以使内核线程,也可以是用户线程。创建用户空间线程是,可以给定线程用户空间堆栈的位置,还可以指定子进程运行的起点。同时还可以用__clone()创建进程,有选择地复制父进程的资源。而fork(),则是全面地复制。还有一个系统调用vfork(),其作用也是创建一个线程,但主要只是作为创建进程的中间步骤,目的在于提高创建时的效率,减少系统开销,其设计接口与fork相同。这几个系统调用的代码在arch/i386/kernel/process.c中。
asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL);
}
asmlinkage int sys_clone(struct pt_regs regs)
{
unsigned long clone_flags;
unsigned long newsp;
int __user *parent_tidptr, *child_tidptr;
clone_flags = regs.ebx;
newsp = regs.ecx;
parent_tidptr = (int __user *)regs.edx;
child_tidptr = (int __user *)regs.edi;
if (!newsp)
newsp = regs.esp;
return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr);
}
/*
* This is trivial, and on the face of it looks like it
* could equally well be done in user mode.
*
* Not so, for quite unobvious reasons - register pressure.
* In user mode vfork() cannot have a stack frame, and if
* done by calling the "clone()" system call directly, you
* do not have enough call-clobbered registers to hold all
* the information you need.
*/
asmlinkage int sys_vfork(struct pt_regs regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL);
}
可以看出,三个系统调用的实现都是通过do_fork来实现的,只是对do_fork()调用的参数不同。
2)下面分析fork()开始系统调用的
:当从用户态调用fork()函数进入系统调用时,CPU切换到内核态并开始执行一个内核函数,详情请见这里,这里笔者简单用个图来描述一下。
关键之处:
fork调用调用0x80中断,寄存器eax中的值为_NR_fork这是fork产给int $0x80唯一的参数。可以看到_NR_fork的调用号是2,如下所示,然后根据系统调用表sys_call_table找到偏移地址找到对应的函数,fork系统调用的对应函数时不带参数的,所以对应的调用是syscall0。
查看”include/asm-i386/unistd.h”可以看到各函数对应的系统调用号:
#ifndef _ASM_I386_UNISTD_H_
#d