学号:351 
原创作品转载请注明出处
本实验来源 https://github.com/mengning/linuxkernel/

实验要求

从整理上理解进程创建、可执行文件的加载和进程执行进程切换,重点理解分析fork、execve和进程切换:

实验内容

阅读理解task_struct数据结构

task_struct:

操作系统使用数据结构来代表处理不同的实体,这个数据结构就是通常所说的进程描述符或进程控制块(PCB)。

而在linux操作系统下这就是task_struct结构 ,所属的头文件#include   <sched.h>每个进程都会被分配一个task_struct结构,它包含了这个进程的所有信息,在任何时候操作系统都能够跟踪这个结构的信息。

包括这个进程的主要信息:

1、与进程相关的唯一标识符,区别正在执行的进程和其他进程

2、状态:描述进程的状态,因为进程有阻塞、挂起、运行等好几个状态,所以都有个表示符来记录进程的执行状态。

3、优先级:如果有好几个进程正在执行,就涉及到进程的执行的先后顺序,这和进程的优先级这个标识符有关。

4、程序计数器:程序中即将被执行指令的下一条地址。

5、内存指针:程序代码和进程相关数据的指针。

6、上下文数据:进程执行时处理器的寄存器中的数据。

7、I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

8、记账信息:包括处理机的时间总和,记账号等等。

使用gdb跟踪分析一个fork系统调用内核处理函数do_fork

1,先编写proces.c查看fork()运行结果

从运行结果中,可以看出父进程和子进程各返回一次。pid == 0时,子进程运行,pid == 其他值,父进程运行,

2,使用gdb跟踪fork系统调用

1.使用内核5.0.1启动Menu OS

cd LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_fork.c test.c

设置断点

观察运行结果

理解编译链接的过程和ELF可执行文件格式

预处理:主要是做一些代码文本的替换工作。(该替换是一个递归逐层展开的过程。)

(1)将所有的#define删除,并展开所有的宏定义

(2)处理所有的条件预编译指令,如:#if  #ifdef #elif #else #endif

(3)处理#include预编译指令,将被包含的文件插进到该指令的位置,这个过程是递归的

(4)删除所有的注释//与/* */

(5)添加行号与文件名标识,以便产生调试用的行号信息以及编译错误或警告时能够显示行号

(6)保留所有的#pragma编译器指令,因为编译器需要使用它们

编译

把预处理完的文件进行一系列词法分析lex)、语法分析yacc)、语义分析优化后生成汇编代码,这个过程是程序构建的核心部分。

汇编

将汇编代码转成机器指令。

链接

此时的链接,严格说应该叫静态链接。将多个目标文件、库拼合成最终的可执行文件。

ELF文件在Linux下,可执行文件/动态库文件/目标文件(可重定向文件)都是同一种文件格式,将其称之为ELF文件格式。 虽然它们三个都是ELF文件格式但都各有不同:可执行文件没有section header table 。 目标文件没有program header table。 动态库文件俩个 header table 都有,

整体结构  :

ELF头部(ELF_Header): 每个ELF文件都必须存在一个ELF_Header,这里存放了很多重要的信息用来描述整个文件的组织,如: 版本信息,入口信息,偏移信息等。程序执行也必须依靠其提供的信息。

程序头部表(Program_Header_Table): 可选的一个表,用于告诉系统如何在内存中创建映像,在图中也可以看出来,有程序头部表才有段,有段就必须有程序头部表。其中存放各个段的基本信息(包括地址指针)。

节区头部表(Section_Header_Table): 类似与Program_Header_Table,但与其相对应的是节区(Section)。

节区(Section): 将文件分成一个个节区,每个节区都有其对应的功能,如符号表,哈希表等。

段(Segment):就是将文件分成一段一段映射到内存中。段中通常包括一个或多个节区

使用exec*库函数加载一个可执行文件

exec()族函数功能是将当前的进程替换成一个新的进程,执行到exec()函数时当前进程就会结束新进程则开始执行。但新进程保留之前进程的进程号,本文以execl()函数为例来进行讲解。

execl()函数原型

#include <unistd.h>

int execl(const char *path, const char *arg, .../* (char *) NULL */);

参数
path - 执行文件的路径
arg - 执行文件的参数,可以比作arg0,,arg1,,arg2,…,argn
最后一个参数必须赋值为NULL

返回值
execl()函数只有在失败时才会返回,返回值为-1并设置errno信息。

修改之前process.c的代码

执行结果:pid == 0 时,子进程并不会执行,而是被替换成一个新的进程,相当于执行了 "ls -l"命令,执行结果如下:

Linux系统中进程调度

调度的实际

  • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();

  • 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;

  • 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

gdb跟踪分析一个schedule()函数 

schecule函数的所作的工作:

确定当前就绪队列, 并在保存一个指向当前(仍然)活动进程的task_struct指针

检查死锁, 关闭内核抢占后调用__schedule完成内核调度

恢复内核抢占, 然后检查当前进程是否设置了重调度标志TLF_NEDD_RESCHED, 如果该进程被其他进程设置了TIF_NEED_RESCHED标志, 则函数重新执行进行调度

分析switch_to中的汇编代码,理解进程上下文的切换机制,以及与中断上下文切换的关系

schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换
next = pick_next_task(rq, prev);//进程调度算法都封装这个函数内部
context_switch(rq, prev, next);//进程上下文切换
switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程
#define switch_to(prev, next, last) \do { \
/* \
* Context-switching clobbers all registers, so we clobber \
* them explicitly, via unused output variables. \
* (EAX and EBP is not listed because EBP is saved/restored \
* explicitly for wchan access and EAX is the return value of \
* __switch_to()) \
*/ \
unsigned long ebx, ecx, edx, esi, edi; \
\
asm volatile("pushfl\n\t" /* save flags */ \
"pushl %%ebp\n\t" /* save EBP */ \ 当前进程堆栈基址压栈
"movl %%esp,%[prev_sp]\n\t" /* save ESP */ \ 将当前进程栈顶保存prev->thread.sp
"movl %[next_sp],%%esp\n\t" /* restore ESP */ \ 讲下一个进程栈顶保存到esp中
"movl $1f,%[prev_ip]\n\t" /* save EIP */ \ 保存当前进程的eip
"pushl %[next_ip]\n\t" /* restore EIP */ \ 将下一个进程的eip压栈,next进程的栈顶就是他的的起点
__switch_canary \
"jmp __switch_to\n" /* regparm call */ \
"1:\t" \
"popl %%ebp\n\t" /* restore EBP */ \
"popfl\n" /* restore flags */ \ 开始执行下一个进程的第一条命令
\
/* output parameters */ \
: [prev_sp] "=m" (prev->thread.sp), \
[prev_ip] "=m" (prev->thread.ip), \
"=a" (last), \
\
/* clobbered output registers: */ \
"=b" (ebx), "=c" (ecx), "=d" (edx), \
"=S" (esi), "=D" (edi) \
\
__switch_canary_oparam \
\
/* input parameters: */ \
: [next_sp] "m" (next->thread.sp), \
[next_ip] "m" (next->thread.ip), \
\
/* regparm parameters for __switch_to(): */ \
[prev] "a" (prev), \
[next] "d" (next) \
\
__switch_canary_iparam \
\
: /* reloaded segment registers */ \
"memory"); \
} while ()

所谓的进程上下文,就是一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容,当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。

同理,硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理,中断上下文就可以理解为硬件传递过来的这些参数和内核需要保存的一些环境,主要是被中断的进程的环境。

Linux内核工作在进程上下文或者中断上下文。提供系统调用服务的内核代码代表发起系统调用的应用程序运行在进程上下文;另一方面,中断处理程序,异步运行在中断上下文。中断上下文和特定进程无关。

参考

Linux内核分析--进程创建,执行,切换的更多相关文章

  1. 20135239 益西拉姆 linux内核分析 进程的切换和系统的一般执行过程

    week 8 进程的切换和系统的一般执行过程 [ 20135239 原文请转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...

  2. Linux内核分析——进程描述与创建

    20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验内容 ...

  3. 20135239益西拉姆 Linux内核分析 进程的描述和进程的创建

    [益西拉姆 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] 第六周 进程的描述 ...

  4. Linux内核分析——汇编代码执行及堆栈变化

    张潇月<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验步骤 首先借助实验楼这个平台进入Linux ...

  5. Linux内核分析——进程的切换和系统的一般执行过程

    进程的切换和系统的一般执行过程 一.进程切换的关键代码switch_to分析 (一)进程调度与进程调度的时机分析 1.不同类型的进程有不同的调度需求 第一种分类: (1)I/O-bound:频繁进行I ...

  6. Linux内核分析— —进程的切换和系统的一般执行过程

    进程调度的时机 linux进程调度是基于分时和优先级的 中断处理过程(包括时钟中断.I/O中断.系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用s ...

  7. Linux内核分析——进程的描述和进程的创建

    进程的描述和进程的创建 一. 进程的描述 (一)进程控制块PCB——task_struct 1.操作系统的三大管理功能包括: (1)进程管理 (2)内存管理 (3)文件系统 2.PCB task_st ...

  8. 《Linux内核分析》第八周 进程的切换和系统的一般执行过程

    [刘蔚然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK EIGHT ...

  9. LINUX内核分析第八周学习总结:进程的切换和系统的一般执行过程

    韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.进程切换的关 ...

随机推荐

  1. sql的优化

    适当的索引 尽量不要有空判断的语句 尽量不要使用不等于条件 尽量不要使用or条件 尽量不要使用左右模糊查询 尽量使用exists代替in 尽量避免一次性返回大数据量

  2. Vue/Egg大型项目开发(二)数据库设计

    项目Github地址:前端(https://github.com/14glwu/stuer)后端(https://github.com/14glwu/stuer-server) 项目线上预览:http ...

  3. Price Channel Breakout 交易系统简价及源码

    Price Channel Breakout 交易系统简价及源码 既然这个版有交易系统的模型报告,小弟先在这裡野人献曝一下,把目前正在用的系统拿来请大家批评指教一下. ================ ...

  4. Bigger-Mai 养成计划,Python基础巩固一

    本日复习内容 Py2与Py3的区别:Py2:print()直接写字符串,不用加括号Py3:print()必须加括号,某些库改名了.还有谁不支持Py3:Twisted:具体能感知的大改动并不多 老生常谈 ...

  5. vue踩坑记

    vue踩坑记 易错点 语法好难啊qwq 不要把'data'写成'date' 在v-html/v-bind中使用vue变量时不需要加变量名 在非vue事件中使用vue中变量时需要加变量名 正确 < ...

  6. Android Studio3.1.2运行模拟器出错Emulator:emulator:ERROR:x86 emulation currently requires hardware acceleration!

    出错截图如下所示: 解决方法: 由于我是已经解决了这个问题,安装了Intel x86 Emulator Accelerator,所以显示后面显示的是 Installed,如果报错和我贴的图一样,就可以 ...

  7. unittest同时支持参数化和生成html报告

    最近在用python3.6+unittest+requests做自动化接口测试.发现一个问题,unittest中使用第3方插件parameterized进行参数化,再生成html报告时,运行就会失败. ...

  8. ssm框架中文请求乱码get

    <bean id="utf8Charset" class="java.nio.charset.Charset" factory-method=" ...

  9. web 页面间传值 js 封装方法

    用法 var id = getParam("id"); function getParam(strKey) { var url=document.URL; //var url=&q ...

  10. vue--vant组件库field输入框

    安装vant UI框架: cnpm install vant –-save-dev 导入组件-在main.js里: import Vant from 'vant'; import'vant/lib/v ...