2019-2020-1 20199329《Linux内核原理与分析》第三周作业

《Linux内核原理与分析》第三周作业


一.上周问题总结:

  • 第二周头脑风暴完成较慢
  • 虚拟机libc配置错误
  • 书本知识使用不够熟练

二.本周学习内容:

1.实验楼环境虚拟一个x86的CPU硬件平台

在该环境下输入如下命令:

cd LinuxKernel/linux-3.9.4
qemu -kernel arch/x86/boot/bzImage

实践截图如下:
2019-2020-1 20199329《Linux内核原理与分析》第三周作业_第1张图片

QEMU窗口截图如下:

2019-2020-1 20199329《Linux内核原理与分析》第三周作业_第2张图片

查看mymain.c:
2019-2020-1 20199329《Linux内核原理与分析》第三周作业_第3张图片

查看myinterrupt.c:

2019-2020-1 20199329《Linux内核原理与分析》第三周作业_第4张图片

2.实验楼环境完成一个简单的时间片轮转机制内核:

代码如下:

#define MAX_TASK_NUM 4
#define KERNEL_STACK_SIZE 1024*8
/* CPU-specific state of this task */
struct Thread {
unsigned long   ip;
unsigned long   sp;
};
typedef struct PCB{
int pid;
volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
char stack[KERNEL_STACK_SIZE];
/* CPU-specific state of this task */
struct Thread thread;
unsigned long   task_entry;
struct PCB *next;
}tPCB;
void my_schedule(void);

mymain.c代码如下:

#include 
#include 
#include 
#include 
#include 

#include "mypcb.h"

tPCB task[MAX_TASK_NUM];
tPCB * my_current_task = NULL;
volatile int my_need_sched = 0;

void my_process(void);

void __init my_start_kernel(void)  #进入内核入口,初始化进程各参数
{
    int pid = 0;                                 #初始化0号进程
    int i;
    /* Initialize process 0*/
    task[pid].pid = pid;                    #设置进程id
    task[pid].state = 0;                     #设置进程状态为0
    task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;             #设置进程入口
    task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];            #设置ESP指向栈顶
    task[pid].next = &task[pid];                #设置堆栈指针指向当前堆栈

    for(i=1;ipid);
            if(my_need_sched == 1)   
#判断是否需要切换进程,1需要切换,将状态值赋为0,并执行my_schedule(), 0不需要切换
            {
                my_need_sched = 0;
                my_schedule();
            }
            printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);  #输出this is process 和当前进程任务号
        }     
    }
}

myinterrupt.c代码如下:

#include 
#include 
#include 
#include 
#include 

#include "mypcb.h"

extern tPCB task[MAX_TASK_NUM];
extern tPCB * my_current_task;
extern volatile int my_need_sched;
volatile int time_count = 0;
#时钟中断程序
void my_timer_handler(void)    
{
#if 1
    if(time_count%1000 == 0 && my_need_sched != 1)         
#如果时钟计数可以整出1000,且不需要切换进程,打印输出my_timer_handler here,并切换状态
    {
        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                      #如果当前和下一个进程列表为空,则返回空结束
        || my_current_task->next == NULL)
    {
        return;
    }
    printk(KERN_NOTICE ">>>my_schedule<<<\n");    
    /* schedule */
    next = my_current_task->next;                   #用指针next指向下一个进程
    prev = my_current_task;                   #用指针prev指向当前进程
    if(next->state == 0)                    #判断下一进程曾执行过               
    {
        my_current_task = next; 
        printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);  
        /* switch to next process */
        asm volatile(   
            "pushl %%ebp\n\t"       # 将ebp入栈保存
            "movl %%esp,%0\n\t"     #保存当前进程ESP到PCB
            "movl %2,%%esp\n\t"     #将next进程的栈顶地址存入ESP
            "movl $1f,%1\n\t"       #保存当前EIP寄存器值到PCB    
            "pushl %3\n\t"         #把下一个进程代码入口地址入栈
            "ret\n\t"               #出栈进程的代码入口地址返回到EIP寄存器
            "1:\t"                  #标号1,即next进程开始执行的位置
            "popl %%ebp\n\t"         #恢复EBP寄存器的值
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)  
#将当前和下一个进程栈顶地址和ip地址存入内存寄存器中,并根据变量%0%1%2%3命名
            : "m" (next->thread.sp),"m" (next->thread.ip)
        ); 
    
    }
    else                                                                   #判断下一进程未执行过
    {
        next->state = 0;
        my_current_task = next;
        printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
        /* switch to new process */
        asm volatile(   
            "pushl %%ebp\n\t"       #保存当前进程EBP到堆栈
            "movl %%esp,%0\n\t"     #保存当前进程ESP到PCB
            "movl %2,%%esp\n\t"      #将next进程的栈顶地址保存到ESP寄存器
            "movl %2,%%ebp\n\t"     #将next进程的堆栈基地址保存到EBP寄存器
            "movl $1f,%1\n\t"       #将当前EIP寄存器值存到PCB,标号为1
            "pushl %3\n\t"           #把下一个进程入口地址入栈
            "ret\n\t"               #将出栈进程的代码入口地址存到EIP寄存器
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        );          
    }   
    return; 
}

3.操作系统的工作原理:

计算机三个法宝(3个关键性的方法机制):存储程序计算机、函数调用堆栈、中断机制

3.1 堆栈:

在计算机领域,堆栈是一个不容忽视的概念,堆栈是一种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。

2019-2020-1 20199329《Linux内核原理与分析》第三周作业_第5张图片

堆栈作用:
  • 记录函数调用框架
  • 传递函数参数
  • 保存返回值的地址
  • 提供函数内部局部变量的存储空间
  • ESP:堆栈指针
  • EBP:基址指针
    堆栈操作:
  • push:栈顶地址减少4个字节,并将操作数放进栈顶存储单元
  • pop:栈顶地址增加4个字节,并将栈顶存储单元的内容放入操作数
其他关键寄存器:
  • CS:EIP:总是指向地址连续的下一条指令
  • 跳转/分支:执行这样的命令时,CS:EIP的值会根据程序需要被修改
  • call:将当前CS:EIP的值压入栈顶,CS:EIP指向被调用函数的入口地址
  • ret:从栈顶弹出原来保存在这里CS:EIP的值,放入CS:EIP中

3.2 中断

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

中断分类:
  • 硬件中断(Hardware Interrupt)
  • 可屏蔽中断(maskable interrupt)。硬件中断的一类,可通过在中断屏蔽寄存器中设定位掩码来关闭。
  • 非可屏蔽中断(non-maskable interrupt,NMI)。硬件中断的一类,无法通过在中断屏蔽寄存器中设定位掩码来关闭。典型例子是时钟中断(一个硬件时钟以恒定频率—如50Hz—发出的中断)。
  • 处理器间中断(interprocessor interrupt)。一种特殊的硬件中断。由处理器发出,被其它处理器接收。仅见于多处理器系统,以便于处理器间通信或同步。
  • 伪中断(spurious interrupt)。一类不希望被产生的硬件中断。发生的原因有很多种,如中断线路上电气信号异常,或是中断请求设备本身有问题
  • 软件中断(Software Interrupt)。是一条CPU指令,用以自陷一个中断。由于软中断指令通常要运行一个切换CPU至内核态(Kernel Mode/Ring 0)的子例程,它常被用作实现系统调用(System call)。
中断作用:
  • 提高计算机系统效率。计算机系统中处理机的工作速度远高于外围设备的工作速度。通过中断可以协调它们之间的工作。当外围设备需要与处理机交换信息时,由外围设备向处理机发出中断请求,处理机及时响应并作相应处理。不交换信息时,处理机和外围设备处于各自独立的并行工作状态
  • 维持系统可靠正常工作。现代计算机中,程序员不能直接干预和操纵机器,必须通过中断系统向操作系统发出请求,由操作系统来实现人为干预。主存储器中往往有多道程序和各自的存储空间。在程序运行过程中,如出现越界访问,有可能引起程序混乱或相互破坏信息。为避免这类事件的发生,由存储管理部件进行监测,一旦发生越界访问,向处理机发出中断请求,处理机立即采取保护措施。
  • 满足实时处理要求。在实时系统中,各种监测和控制装置随机地向处理机发出中断请求,处理机随时响应并进行处理。
  • 提供故障现场处理手段。处理机中设有各种故障检测和错误诊断的部件,一旦发现故障或错误,立即发出中断请求,进行故障现场记录和隔离,为进一步处理提供必要的依据。

三.总结与疑难

本次Linux学习主要学习了计算机的三大法宝,清楚它们各自的作用是什么。并且对C语言内嵌入汇编代码有了一定的了解,但距离自己编写代码还需要很长的时间来学习。

QUSTION:

  • 对于进程的切换还不够熟练。

四.下周计划

  • [ ] 完成书本上的课后习题
  • [ ] 自己尝试编写代码

2019 年 09月 29日

你可能感兴趣的:(2019-2020-1 20199329《Linux内核原理与分析》第三周作业)