328
原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/
一.实验要求
1.阅读理解task_struct数据结构;
2.分析fork函数对应的内核处理过程do_fork,使用gdb跟踪分析一个fork系统调用内核处理,函数do_fork;
3.理解编译链接的过程和ELF可执行文件格式;
4.使用gdb跟踪分析一个execve系统调用内核处理函数do_execve;
5.使用gdb跟踪分析一个schedule()函数;
6.分析switch_to中的汇编代码,理解进程上下文的切换机制,以及与中断上下文切换的关系;
二.实验步骤
1.阅读理解task_struct数据结构
为了管理进程,操作系统必须对每个进程所做的事情进行清楚的描述,为此,操作系统使用数据结构来代表处理不同的实体,这个数据结构就是通常所说的进程描述符或进程控制块(PCB)。
在linux操作系统下这就是task_struct结构 ,所属的头文件#include <sched.h>每个进程都会被分配一个task_struct结构,它包含了这个进程的所有信息,在任何时候操作系统都能够跟踪这个结构的信息。保存进程信息的数据结构叫tast_struct,进程的信息可以通过/proc系统文件夹查看。
下图是tast_struct各个字段的介绍。
2.分析fork函数对应的内核处理过程do_fork
传统的UNIX中用于复制进程的系统调用是fork。但它并不是Liunx为此实现的唯一的调用,实际上Linux实现了3个:
(1)fork是重量级调用,它建立了父进程的一个完整副本,然后为子进程执行。
(2)vfork类似于fork,但并不创建父进程数据的副本。相反,父子进程之间共享数据。
(3)clone产生线程,可以对父子进程之间的共享、复制进行精确控制。
fork、vfork和close系统调用的入口分别是sys_fork、sys_vfork和sys_clone函数。以上函数从寄存器中取出由用户定义的信息,并调用与体系结构无关的do_fork函数进行进程的复制。
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long nr;
// ...
// 复制进程描述符,返回创建的task_struct的指针
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace);
if (!IS_ERR(p)) {
struct completion vfork;
struct pid *pid;
trace_sched_process_fork(current,