学号:384
原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/
实验目标
1.分析fork函数对应的内核处理过程do_fork,理解创建一个新进程如何创建和修改task_struct数据结构
2.使用gdb跟踪分析一个fork系统调用内核处理函数do_fork
3.理解编译链接的过程和ELF可执行文件格式
实验环境
ubuntu系统(ubuntu-16.04.2-desktop-amd64)+ VMware Workstation Pro
一、阅读理解task_struct数据结构
代码来源:http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235
该结构部分代码:
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;
atomic_t usage;
unsigned int flags; /* per process flags, defined below */
unsigned int ptrace;
#ifdef CONFIG_SMP
struct llist_node wake_entry;
int on_cpu;
struct task_struct *last_wakee;
unsigned long wakee_flips;
unsigned long wakee_flip_decay_ts;
int wake_cpu;
#endif
int on_rq;
int prio, static_prio, normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
#ifdef CONFIG_CGROUP_SCHED
struct task_group *sched_task_group;
#endif
struct sched_dl_entity dl;
#ifdef CONFIG_PREEMPT_NOTIFIERS
/* list of struct preempt_notifier: */
struct hlist_head preempt_notifiers;
#endif
#ifdef CONFIG_BLK_DEV_IO_TRACE
unsigned int btrace_seq;
#endif
unsigned int policy;
int nr_cpus_allowed;
cpumask_t cpus_allowed;
...
}
在阅读这个结构体之前,我们必须了解进程与程序的区别,进程是程序的一个执行的实例,为了管理进程,操作系统必须对每个进程所做的事情进行清楚的描述,为此,操作系统使用数据结构来代表处理不同的实体,这个数据结构就是通常所说的进程描述符或进程控制块(PCB),在linux操作系统下这就是task_struct结构 ,它包含了这个进程的所有信息,在任何时候操作系统都能够跟踪这个结构的信息。该结构定义位于/include/linux/sched.h
对于进程控制块PCB—task_struct:
状态信息:如就绪、执行等状态
链接信息:用来描述进程之间的家庭关系,例如指向父进程、子进程、兄弟进程等PCB的指针
各种标识符:如进程标识符、用户及组标识符等
时间和定时器信息:进程使用CPU时间的统计等
调度信息:调度策略、进程优先级、剩余时间片大小等
处理机环境信息:处理器的各种寄存器以及堆栈情况等
虚拟内存信息:描述每个进程所拥有的地址空间
文件系统信息:记录进程使用文件的情况
PCB几个重要参数
volatile long state;//表示进程的当前状态
unsigned long flags; //进程标志
long priority; //进程优先级。 Priority的值给出进程每次获取CPU后可使用的时间(按jiffies计)。优先级可通过系统调用sys_setpriorty改变(在kernel/sys.c中)。
long counter; //在轮转法调度时表示进程当前还可运行多久。
unsigned long policy; //该进程的进程调度策略,可以通过系统调用sys_sched_setscheduler()更改(见kernel/sched.c)。
二、分析fork函数对应的内核处理过程do_fork
fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
具体过程如下:fork() -> sys_clone() -> do_fork() -> dup_task_st