原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/
学号 088
实验要求
- 阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235;
- 分析fork函数对应的内核处理过程do_fork,理解创建一个新进程如何创建和修改task_struct数据结构;
- 使用gdb跟踪分析一个fork系统调用内核处理函数do_fork ,验证您对Linux系统创建一个新进程的理解,特别关注新进程是从哪里开始执行的?为什么从那里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
- 理解编译链接的过程和ELF可执行文件格式;
- 编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接;
- 使用gdb跟踪分析一个execve系统调用内核处理函数do_execve ,验证您对Linux系统加载可执行程序所需处理过程的理解;
- 特别关注新的可执行程序是从哪里开始执行的?为什么execve系统调用返回后新的可执行程序能顺利执行?对于静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时会有什么不同?
- 理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否准确;
- 使用gdb跟踪分析一个schedule()函数 ,验证您对Linux系统进程调度与进程切换过程的理解;
- 特别关注并仔细分析switch_to中的汇编代码,理解进程上下文的切换机制,以及与中断上下文切换的关系;
实验环境
- Ubuntu18
- 内核:Linux Kernel 5.0.1
实验内容
1. 阅读理解task_struct数据结构
task_struct代码:task_struct
由于task_struct代码过多,下面对其部分代码分析:
- 支持对称多处理器方式(SMP)时的数据成员
int processor; //进程正在使用的CPU int last_processor; //进程最后一次使用的CPU int lock_depth; //上下文切换时系统内核锁的深度 unsigned short used_math; //是否使用MPU char comm[16]; //进程正在运行的可执行文件的文件名 int errno; //最后一次出错的系统调用错误号,0表示无错误。系统调用返回时,全程量也拥有该错误。 long debugreg[8]; //保存INTEL CPU调试寄存器的值,在ptrace系统调用中使用。
- 信号处理
unsigned long signal; //进程接收到的信号。每位表示一种信号,共32种。置位有效。 unsigned long blocked; //进程所能接受信号的位掩码。置位表示屏蔽,复位表示不屏蔽。 struct signal_struct *sig; //因为signal和blocked都是32位的变量,Linux最多只能接受32种信号。对每种信号,各进程可以由PCB的sig属性选择使用自定义的处理 函数,或是系统的缺省处理函数。
- 进程队列指针
struct task_struct *next_task,*prev_task; //所有进程(以PCB的形式)组成一个双向链表。next_task和 就是链表的前后指针。链表的头和尾都是init_task(即0号进程)。 struct task_struct *next_run,*prev_run; //由正在运行或是可以运行的,其进程状态均为TASK_RUNNING的进程所组成的一个双向循环链表,即run_queue就绪队列。该链表的前后向指针用next_run和prev_run,链表的头和尾都是init_task(即0号进程)。 struct task_struct *p_opptr,*p_pptr;和struct task_struct *p_cptr,*p_ysptr,*p_osptr; //以上分别是指向原始父进程(original parent)、父进程(parent)、子进程(youngest child)及新老兄弟进程(younger sibling,older sibling)的指针
- 进程标识
unsigned short uid,gid; //uid和gid是运行进程的用户标识和用户组标识 int groups[NGROUPS]; //与多数现代UNIX操作系统一样,Linux允许进程同时拥有一组用户组号。在进程访问文件时,这些组号可用于合法性检查 unsigned short euid,egid; //euid 和egid又称为有效的uid和gid。出于系统安全的权限的考虑,运行程序时要检查euid和egid的合法性。通常,uid等于euid,gid等于 egid。有时候,系统会赋予一般用户暂时拥有root的uid和gid(作为用户进程的euid和egid),以便于进行运作 unsigned short fsuid,fsgid; //fsuid 和fsgid称为文件系统的uid和gid,用于文件系统操作时的合法性检查,是Linux独特的标识类型。它们一般分别和euid和egid一致,但在 NFS文件系统中NFS服务器需要作为一个特殊的进程访问文件,这时只修改客户进程的fsuid和fsgid
操作系统管理进程必须对每个进程所做的事情进行清楚的描述,使用数据结构来代表处理不同的实体,这个数据结构就是通常所说的进程描述符或进程控制块(PCB)。在linux操作系统下这就是task_struct结构 ,所属的头文件#include <sched.h>每个进程都会被分配一个task_struct结构,它包含了这个进程的所有信息,在任何时候操作系统都能够跟踪这个结构的信息.
2.分析fork函数对应的内核处理过程do_fork
- 代码如下
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr