“Linux内核分析”实验二报告

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了“Linux内核分析”实验二报告相关的知识,希望对你有一定的参考价值。

张文俊 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

一、第二周学习内容总结

1、计算机工作“三大法宝”

首先,计算机工作原理最重要的三个内容就是:存储程序计算机工作模型、中断机制和函数调用堆栈。

存储程序计算机工作模型是计算机系统最最基础性的逻辑结构;

中断机制是多道程序操作系统的基点,没有中断机制程序只能从头一直运行结束才有可能开始运行其他程序;

函数调用堆栈是高级语言得以运行的基础,有了高级语言及函数后,堆栈成为了计算机的基础功能,函数参数传递机制和局部变量存储。

2、详细介绍函数调用堆栈

首先,堆栈是C语言程序运行时必须的一个记录调用路径和参数的空间,即CPU内已经集成好了很多功能。

堆栈含以下元素:

函数调用框架、传递参数、保存返回地址、提供局部变量空间、C语言编译器对堆栈的使用有一套的规则。

了解堆栈存在的目的和编译器对堆栈使用的规则是理解操作系统一些关键性代码的基础。

 

其次,堆栈相关的寄存器常用的共有四种,esp、ebp、push、pop。详细解释如下:

esp,堆栈指针,指向栈顶;

ebp,基址指针,指向栈底,在C语言中用作记录当前函数调用基址;

push,栈顶地址减少4个字节,从低地址向高地址;

pop,栈顶地址增加4个字节,从高地址向低地址;

 

最后,课程介绍了函数参数的传递与框架——

(1)建立框架,即call指令

     相当于

      push %ebp

      movl %esp,%ebp

     cs:eip原来的值指向call下一条指令,该值被保存到栈顶cs:eip的值指向function的入口地址

(2)执行函数主体代码

(3)拆除框架

     相当于

     movl %ebp,%esp

     pop %ebp

     ret

函数的返回值通过eax寄存器传递

3、参数传递

这里以老师在课堂上举出的例子:

#include <stdio.h>

void p1(char c)

{

printf("%c",c);

}

int p2(int x,int y)

 {

return x+y;

}

int main(void)

{

char c =‘a‘;

int x,y;

x =1;

y =2;

p1(c);

z = p2(x,y);

printf("%d = %d+%d",z,x,y);

}

首先,观察P2的堆栈框架:

技术分享

 

这里,使用变址寻址方式,将x+y的值赋给eax。

然后,我们观察main是如何传递参数给P2的:

技术分享

 

 

和main中的局部变量:

技术分享

可以看到,汇编代码中用变址寻址把y的值和x的值存放到堆栈中,然后进行局部变量调用。

4、C代码中嵌入汇编代码

____asm____

(

汇编语句模板:

输入部分:

输出部分:

破坏描述部分:

);

二、实验二内容

本次实验内容是在mykernel基础上构造一个简单的操作系统内核。

首先我们cd进入LinuxKernel/linux-3.9.4文件,执行qemu -kernel arch/x86/boot/bzImage,可以看到窗口弹出如下:

技术分享

 

 

然后,我们查看mymain.c和myinterrrupt.c文件:

技术分享

技术分享

可以发现,mymain是系统中唯一的进程,函数主要部分是my_start_kernel。每循环10万次,就打印一次my_start_kernel here.

myinterrupt是时间中断处理程序,每进行一次就会发生一次时钟处理中断,每次时钟中断都调用printk并输出。

 

程序分析:

mypcb.h

#define MAX_TASK_NUM 4

#define KERNEL_STACK_SIZE 1024*8

/* CPU-specific state of this task */

struct Thread {

unsigned long ip;//保存eip

unsigned long sp;//保存esp

};

typedef struct PCB{

//用于表示一个进程,定义了进程管理相关的数据结构

int pid;

volatile long state; /* 定义进程的状态:-1 不可运行, 0 可运行, >0 停止 */

char stack[KERNEL_STACK_SIZE];

//内核堆栈

struct Thread thread;

unsigned long task_entry; //指定进程入口

struct PCB *next;//进程链表

}tPCB;

void my_schedule(void);//调用了my_schedule,表示调度器

mymain.c

void my_timer_handler(void)

{

#if 1

if(time_count%1000 == 0 && my_need_sched != 1)//设置时间片的大小,时间片用完时设置一下调度标志。当时钟中断发生1000次,并且my_need_sched!=1时,把my_need_sched赋为1。当进程发现my_need_sched=1时,就会执行my_schedule。

{

printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");

my_need_sched = 1;

}

time_count ++ ;

#endif return;

}

void my_schedule(void)

{

tPCB * next; //下一个进程

tPCB * prev; //当前进程

if(my_current_task == NULL //task为空,即发生错误时返回

|| my_current_task->next == NULL)

{

return;

}

printk(KERN_NOTICE ">>>my_schedule<<<\n

以上是关于“Linux内核分析”实验二报告的主要内容,如果未能解决你的问题,请参考以下文章

“linux系统内核分析”实验报告1

“Linux内核分析”实验报告

“Linux内核分析”实验三报告

跟踪分析Linux内核的启动过程--实验报告 分析 及知识重点

Linux内核分析实验——结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程

Linux内核分析实验二:mykernel实验指导(操作系统是如何工作的)