第六周——进程描述和进程控制
【洪韶武 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 】
第六周 进程描述和进程控制
一、进程描述
1.进程控制块PCB和进程描述符struct task_struct
2.pid_t pid又叫进程标识符,唯一地标识进程
3.双向循环链表链接起了所有的进程,也表示了父子、兄弟等进程关系
4.struct mm_struct 指的是进程地址空间,涉及到内存管理(对于X86而言,一共有4G的地址空间)
5. Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info和进程的内核堆栈
6.进程状态
进程描述符中的state域描述了进程的当前状态
- TASK_RUNNING(运行)-进程可执行,进程正在或者等待执行
- TASK_INTERRUPTIBLE(可中断)-进程被阻塞
- TASK_UNINTERRUPTIBLE(不可中断)-对信号不做响应
- _TASK_TRACED-被其他进程跟踪
- _TASK_STOPPED(停止)-进程停止执行
二、进程创建
1.创建一个新进程在内核中的执行过程
(1)fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
(2)Linux通过复制父进程来创建一个新进程,给理解这一个过程提供一个想象的框架:
- 复制一个PCB——task_struct
- err = arch_dup_task_struct(tsk, orig);
- 要给新进程分配一个新的内核堆栈
- ti = alloc_thread_info_node(tsk, node);
- tsk->stack = ti;
- setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈
2.fork代码
3.创建新进程的框架
fork,vfork,clone都可以创建新进程,他们都是通过调用do_fork来实现的
- dup_thread复制父进程的PCB
- copy_process修改复制的PCB以适应子进程的特点,也就是子进程的初始化
- 分配一个新的内核堆栈(用于存放子进程数据)
- 内核堆栈的一部分也要从父进程中拷贝
- 根据拷贝的内核堆栈情况设置eip,esp寄存器的值
三、实验内容
更新menu内核,然后删除test_fork.c以及test.c,编译内核,可以看到fork命令
启动gdb调试,并对主要的函数设置断点
在MenuOS中执行fork,就会发现fork函数停在了父进程中
继续执行之后,停在了do_fork的位置。然后n单步执行,依次进入copy_process、dup_task_struct。按s进入该函数,可以看到dst = src(也就是复制父进程的struct),在copy_thread中,把task_pg_regs(p)也就是内核堆栈特定的地址找到并初始化,到了159、160行的代码就是把压入的代码再放到子进程中:
*children = *current_pt_regs();
childregs->ax = 0;
164行,是确定返回地址p->thread.ip = (unsigned long) ret_from_fork;
此后,可以输入finish使得进程运行完。