zoukankan      html  css  js  c++  java
  • 2018-2019-1 20189203《Linux内核原理与分析》第三周作业

    一、课程学习

    • 计算机的三个法宝:存储程序计算机、函数调用堆栈、中断。
    • 堆栈相关的寄存器:ESP(堆栈指针)、EBP(基址指针)。
    • 堆栈操作:push:栈顶地址减少4个字节,并将操作数放入栈顶存储单元。
    • Pop:栈顶地址增加4个字节,并将栈顶存储单元的内容放入操作数。
      对于X86体系结构来说,栈是从高地址向低地址增加的。
    • 其他关键寄存器:CS:EIP总是指向下一条的指令地址,这里用到了CS寄存器,也就是代码寄存器和EIP总是指向下一条的指令地址。
    • 用堆栈来传递函数的参数:对32位的X86CPU来说,通过堆栈来传递函数的方法是从右到左依次压栈。
    • 内嵌汇编语言模板:asm volatile
      汇编语句模板:
      输出部分:
      输入部分:
      破坏描述部分:
      );
    • 中断:有了中断才有了多道程序,在没有中断的机制之前,计算机只能一个程序一个程序地执行,也就是批处理,而无法多个程序并发工作。有了中断机制的CPU帮我们做了一件事情,就是当一个中断信号发生时,CPU把当时正在执行的程序地CS:EIP寄存器和ESP寄存器等都压到了一个叫做内核堆栈的地方,然后把CS:EIP指向一个中断处理程序的入口,做保存现场的工作,之后执行其他程序,等重新回来时再恢复现场,恢复CS:EIP寄存器和ESP寄存器等,继续执行程序。

    二、实验

    • 根据实验楼中实验二的作业要求,我做了如下操作:
    • 首先使用实验楼的虚拟机打开shell,输入实验楼中的命令:
    cd LinuxKernel/linux-3.9.4
    rm -rf mykernel
    patch -p1 < ../mykernel_for_linux3.9.4sc.patch
    make allnoconfig
    make #编译内核请耐心等待
    qemu -kernel arch/x86/boot/bzImage
    

    随后出现了一个内核启动程序,如图所示:

    这个程序在不停的输出一些字符。关闭程序窗口,打开mykernel目录,查看目录下的文件,找到mymain.c文件和myinterrupt.c文件。

    打开上述两个文件查看代码。
    mymain.c代码如下:

    Myinterrupt.c代码如下:

    • 在上面的代码中可以看到,mymain.c中有一个循环在持续输出“my_start_kernel here”,myinterrupt.c中有一个循环在持续输出“my_timer_handler here”。由此可以得知,myinterrupt.c中可以完成中断程序调用。
      下面对my_start_kernel和my_timer_handler函数进行修改,模拟基于时间片轮转的多道程序。三个文件mymain.c,myinterrupt.c,mypcb.h的代码如下:
      mypcb.h代码:
    #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, 0runnable, >0 stopped */
    unsigned long 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);
    
    • 这段代码主要定义了进程控制块PCB,包括:
      pid:进程号
      state:进程状态,在模拟系统中,所有进程控制块信息都会被创建出来,其初始化值就是-1,如果被调度运行起来,其值就会变成0
      stack:进程使用的堆栈
      thread:当前正在执行的线程信息
      task_entry:进程入口函数
      next:指向下一个PCB,模拟系统中所有的PCB是以链表的形式组织起来的。

    mymain.c代码:

    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/ctype.h>
    #include <linux/tty.h>
    #include <linux/vmalloc.h>
    #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;
    int i;
    
    /* Initialize process 0*/
    task[pid].pid = pid;
    task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
    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];
    task[pid].next = &task[pid];
    
    /*fork more process */
    for(i=1;i<MAX_TASK_NUM;i++)
    {
        memcpy(&task[i],&task[0],sizeof(tPCB));
        task[i].pid = i;
    //*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
    task[i].thread.sp = (unsigned long)(&task[i].stack[KERNEL_STACK_SIZE-1]);
        task[i].next = task[i-1].next;
        task[i-1].next = &task[i]; 
    }
    
    /* start process 0 by task[0] */
    pid = 0;
    my_current_task = &task[pid];
    asm volatile(
        "movl %1,%%esp
    	"     /* set task[pid].thread.sp to esp */
        "pushl %1
    	"          /* push ebp */
        "pushl %0
    	"          /* push task[pid].thread.ip */
        "ret
    	"               /* pop task[pid].thread.ip to eip */
        : 
        : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   /* input c or d mean %ecx/%edx*/
    );
    } 
    
    int i = 0;
    
    void my_process(void)
    {
    while(1)
    {
        i++;
        if(i%10000000 == 0)
        {
            printk(KERN_NOTICE "this is process %d -
    ",my_current_task->pid);
            if(my_need_sched == 1)
            {
                my_need_sched = 0;
                my_schedule();
            }
            printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
        }     
    }
    }
    
    • 这个函数 my_start_kernel是系统启动后,最先调用的函数,负责初始化内核的各个组成部分。在模拟系统里,每个进程的函数代码都是一样的,即 my_process函数,my_process函数在执行的时候会打印出当前进程的id号,方便我们知道是哪个进程在运行。而且在该函数中还定义了my need sched变量,若它的值为1,就调用my schedule()来完成进程的调度
      0号线程的启动,采用了内嵌汇编代码完成:
    asm volatile(  
        "movl %1,%%esp
    	"     /*将进程原堆栈栈顶的地址(这里是初始化的值)存入ESP寄存器 */  
        "pushl %1
    	"          /* 将当前EBP寄存器值入栈 */  
        "pushl %0
    	"          /* 将当前进程的EIP(这里是初始化的值)入栈*/  
        "ret
    	"               /* ret命令正好可以让入栈的进程EIP保存到EIP寄存器中*/  
        "popl %%ebp
    	"       /*这里永远不会被执行,知识与前面push指令结对出现,是一种编码习惯*/
        :   
        : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   /* input c or d mean %ecx/%edx*/  
    ); 
    
    • 由于开始栈为空,所以esp,ebp指向同一位置,之后esp,eip依次压栈,pop eip进程0开始启动,之后清空栈,指针esp,ebp又同时指向栈顶(也是栈底,空栈)。
      myinterrupt.c代码如下:
    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/ctype.h>
    #include <linux/tty.h>
    #include <linux/vmalloc.h>
    
    #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;
    
    /*
     * Called by timer interrupt.
     * it runs in the name of current running process,
     * so it use kernel stack of current running process
     */
    void my_timer_handler(void)
    {
    
    #if 1
    if(time_count%1000 == 0 && my_need_sched != 1)
    {
        printk(KERN_NOTICE ">>>my_timer_handler here<<<
    ");
        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<<<
    ");
    /* schedule */
    next = my_current_task->next;
    prev = my_current_task;
    if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
    {        
        my_current_task = next; 
        printk(KERN_NOTICE ">>>switch %d to %d<<<
    ",prev->pid,next->pid);  
        /* switch to next process */
        asm volatile(   
            "pushl %%ebp
    	"       /* save ebp */
            "movl %%esp,%0
    	"     /* save esp */
            "movl %2,%%esp
    	"     /* restore  esp */
            "movl $1f,%1
    	"       /* save eip */  
            "pushl %3
    	" 
            "ret
    	"               /* restore  eip */
            "1:	"                  /* next process start here */
            "popl %%ebp
    	"
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        ); 
    }  
    return; 
    }
    
    • myinterrupt.c包含my_timer_handler和my_schedule两个函数。 my_timer_handler每隔1000次将my_need_sched置1,调用进程的调度函数。my_schedule保存恢复进程上下文。
      进行进程调度的关键代码:
    if(next->state == 0)/* next->state==0对应进程next对应进程曾执行过 */
    {        
        my_current_task = next; 
        printk(KERN_NOTICE ">>>switch %d to %d<<<
    ",prev->pid,next->pid);  
        /* switch to next process */
        asm volatile(   
            "pushl %%ebp
    	"       /* 保存 当前ebp到堆栈中 */
            "movl %%esp,%0
    	"     /* 保存当前ESP到当前进程PCB中*/
            "movl %2,%%esp
    	"     /* 将next进程的堆栈栈顶的值存到esp寄存器*/
            "movl $1f,%1
    	"       /* 保存当前进程的EIP值 */  
            "pushl %3
    	" 
            "ret
    	"               /* 出栈标号1到EIP寄存器*/
            "1:	"                  /* 标号1,即next进程开始执行的位置*/    
            "popl %%ebp
    	"          /* 恢复EBP寄存器的值*/   
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        ); 
    }  
    
  • 相关阅读:
    动态封杀与解封IP
    潜谈IT从业人员在传统IT和互联网之间的择业问题(下)-互联网公司
    潜谈IT从业人员在传统IT和互联网之间的择业问题(上)-传统乙方形公司
    博主2000年真实遇鬼记-仅作记录以供后人参考
    异步请求引发的Chrome死锁
    jboss规则引擎KIE Drools 6.3.0 Final 教程(3)
    jboss规则引擎KIE Drools 6.3.0 Final 教程(2)
    jboss规则引擎KIE Drools 6.3.0 Final 教程(1)
    商品搜索引擎---推荐系统设计
    大道理很多人都懂,只是坚持不下去(浅谈坚持)
  • 原文地址:https://www.cnblogs.com/23du/p/9846556.html
Copyright © 2011-2022 走看看