分析Linux内核创建一个新进程的过程
实验:分析Linux内核创建一个新进程的过程
姓名:李冬辉
学号:20133201
注: 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
云课堂笔记:
进程的描述和进程的创建有关内容
1、进程描述符task_struct数据结构
(1)操作系统的三大功能
进程管理、内存管理和文件系统
(2)进程控制块PCB——task_struct
1)进程在TASK_RUNNING下是可运行的,但它有没有运行取决于它有没有获得cpu的控制权,即这个进程有没有在cpu上实际的执行
2)进程的标示pid
3)程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系
2、进程的创建
(1)0号进程的创建是由人工写进去的,1号进程是复制了0号进程的pcb,再根据1号进程的需要对其进行修改,再加载一个init可执行程序
(2)fork():是用于用户态创建一个子进程的系统调用
进程状态转化:
实验步骤:
1.删除原来的menu,并clone新的menu,用test_fork.c覆盖test.c
2. make rootfs之后新的内核启动,测试fork功能
3.使用-s -S冷冻内核,准备调试
4.设置断点
5.根据断点,进行跟踪,得到结果
实验截图:
总结:
(1)分析内核处理过程sys_clone:
fork、vfork和clone三个系统调用实际上都是通过do_fork
来实现进程的创建.
而do_fork函数真正实现复制是copy_process
- copy_process函数
copy_process()主要完成进程数据结构,各种资源的初始化。
p = dup_task_struct(current);
- 检查clone_flags参数,防止无效的组合进入
p = dup_task_struct(current);
调用dup_task_struct()为新进程创建一个内核栈- 判断权限及允许范围的代码
- 对子进程的描述符初始化和复制父进程的资源给子进程
retval = sched_fork(clone_flags, p);
- 完成调度相关的设置,将这个task分配给CPU。
if (retval)
- 语句群,复制共享进程的的各个部分
retval = copy_thread(clone_flags, stack_start, stack_size, p);
- 复制父进程堆栈的内容到子进程的堆栈中去.这其中,copy_thread()函数中的语句
p->thread.ip = (unsigned long) ret_from_fork;
- 决定了新进程的第一条指令地址。
dup_task_struct()
tsk = alloc_task_struct_node(node);
为task_struct开辟内存ti = alloc_thread_info_node(tsk, node);
ti指向thread_info的首地址,同时也是系统为新进程分配的两个连续页面的首地址。err = arch_dup_task_struct(tsk, orig);
复制父进程的task_struct信息到新的task_struct里, (dst = src;)tsk->stack = ti;
task的对应栈setup_thread_stack(tsk, orig);
初始化thread info结构set_task_stack_end_magic(tsk);
栈结束的地址设置数据为栈结束标示(for overflow detection)
(2)新进程是从哪里开始执行的
在之前的分析中,谈到copy_process中的copy_thread()
函数,正是这个函数决定了子进程从系统调用中返回后的执行.
子进程执行ret_from_fork
(3)执行起点与内核堆栈如何保证一致?
- 在ret_from_fork之前,也就是在copy_thread()函数中
*childregs = *current_pt_regs();
该句将父进程的regs参数赋值到子进程的内核堆栈, - *childregs的类型为pt_regs,里面存放了SAVE ALL中压入栈的参数
- 故在之后的RESTORE ALL中能顺利执行下去。