您的位置:首页 > 运维架构 > Linux

分析Linux内核创建一个新进程的过程

2016-03-30 08:59 453 查看
姓名:王晨光

学号:20133232

王晨光 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

一、实验过程

此次实验依旧是在实验楼进行的。

1、首先进入虚拟机,打开终端,这命令行依次敲入以下命令:

cd LinuxKernel

rm menu -rf //强制删除

git clone https://github.com/mengning/menu.git //将menu更新

cd menu

mv test_fork.c test.c //更新test.c

make rootfs //运行脚本,自动编译和自动生成根文件系统,同时启动,输入fork命令,发现子进程和父进程都输出来了

一切正常的话,这时候我们简易的内核系统就启动起来了,输入help,就可以看到新添加的命令fork,输入fork,看看效果吧,有图为证:



2. gdb上述的fork命令

关闭QEMU窗口,中命令行中输入:

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

再次启动MenuOS,并暂停等待gdb调试。

然后水平分割命令行窗口,这新窗口中依次输入以下命令,启动调试:

gdb

file linux-3.18.6/vmlinux

target remote:1234



然后再设置以下断点:

b sys_clone

b do_fork

b dup_task_struct

b copy_process

b copy_thread

b ret_from_fork



在这之后想进行单步调试和单步追踪,由于网速问题或者是操作问题,并没有达成,卡住了。

二、实验总结

(一)进程的创建:
fork()在用户态用于创建一个子进程的系统调用,fork系统调用在父进程和子进程各返回一次,在父进程的返回值是子进程中的id,在子进程的返回值是0,在shell命令行下两个都输出了是因为两个进程,子进程copy了父进程所有的信息。

fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建。
创建新进程是通过复制当前进程来实现的。

(二)分析fork函数对应的内核处理过程sys_clone:
从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread

*childregs = *current_pt_regs(); //复制内核堆栈
childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址


Linux通过复制父进程来创建一个新进程。

最终调用do_fork

p = copy_process(clone_flags, stack_start, stack_size,child_tidptr, NULL, trace);    // 创建一个进程的主要代码
1240    p = dup_task_struct(current);    // 复制PCB
316ti = alloc_thread_info_node(tsk, node);    // 实际分配内存地址空间


刚fork出来的子进程是从ret_from_fork开始执行的,然后跳转到syscall_exit。fork()函数返回了两次,即在父子进程中各返回一次!但是子进程的返回值是0,从返回值我们就可以判断出返回后接下来要执行的代码是中父进程还是子进程中。

总结:

在这次实验中,实验过程比较曲折,由于自己网速和操作的问题,在单步调试和单步追踪中并没有完成就卡住了。通过查阅linux的资料和观看163的视频我对如何在内核中创建新进程的过程有了初步的认识。

创建一个新进程在内核中的执行过程大致如下:

使用系统调用SyS_clone(或fork,vfork)系统调用创建一个新进程,而且都是通过调用do_fork来实现进程的创建;

Linux通过复制父进程PCB--task_struct来创建一个新进程,要给新进程分配一个新的内核堆栈;

要修改复制过来的进程数据,比如pid、进程链表等等执行copy_process和copy_thread

p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶

p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: