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

    实验:基于kernel的简单的时间片轮转多道程序内核

    1、实验要求

    • 完成一个简单的时间片轮转多道程序内核代码

    2、实验过程

    • 进入实验楼的linux环境,打开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的基础上添加mypcb.h,修改mymain.c和myinterrupt.c文件,实现一个简单的操作系统内核,实现效果如下:

    3、mykernel时间片轮转代码分析

    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;                 //定义进程id
        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); //声明调度函数
    

    本mypcb.h头文件主要定义了程序控制块PCB,包括:
    pid:定义进程id
    state:进程状态标记,-1是未运行,0为运行,>0为终止
    stack:定义使用的堆栈
    thread:定义线程
    task_entry:进程入口
    next:链表指向下一个PCB

    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引用全局变量
    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%100 == 0 && my_need_sched != 1) //当时钟中断发生100次,并且my_need_sched不为1时,赋值为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);  
            /* 切换进程 */
            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)
            ); 
        
        }
        else
        {
            next->state = 0;
            my_current_task = next;
            printk(KERN_NOTICE ">>>switch %d to %d<<<
    ",prev->pid,next->pid);
            /* switch to new process */
            asm volatile(   
                "pushl %%ebp
    	"       /* save ebp */
                "movl %%esp,%0
    	"     /* save esp */
                "movl %2,%%esp
    	"     /* restore  esp */
                "movl %2,%%ebp
    	"     /* restore  ebp */
                "movl $1f,%1
    	"       /* save eip */  
                "pushl %3
    	" 
                "ret
    	"               /* restore  eip */
                : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
                : "m" (next->thread.sp),"m" (next->thread.ip)
            );          
        }   
        return; 
    }
    

    本c文件中,定义了my_timer_handler和my_schedule两个函数调用,前者是当时钟中断发生100次,并且my_need_sched不为1时,赋值为1,是mymain.c中my_process函数判定主动调度的标志;后者是执行调度的具体过程,下面对切换进程的汇编代码进行分析:
    "pushl %%ebp " /* save ebp / ebp入栈
    "movl %%esp,%0 " /
    save esp / 保存当前esp到进程的sp中
    "movl %2,%%esp " /
    restore esp / esp指向下一进程
    "movl $1f,%1 " /
    save eip / 将1f存储到进程的ip中,$1f是标号“1: ”处,再次调度到该进程时就会从1:开始执行
    "pushl %3 " 下一进程的ip入栈
    "ret " /
    restore eip / eip指向下一进程的起始地址
    "1: " /
    next process start here */ 下一进程从此处开始执行
    "popl %%ebp " 执行完后出栈释放空间
    : "=m" (prev->thread.sp),"=m" (prev->thread.ip) 分别对于上面的%0,%1
    : "m" (next->thread.sp),"m" (next->thread.ip) 分别对应上面的%2,%3

    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];  //PCB的数组task
    tPCB * my_current_task = NULL; //当前task指针
    volatile int my_need_sched = 0; //是否需要调度
    
    void my_process(void);  //my_process函数声明
    
    void __init my_start_kernel(void) //mykernel内核代码的入口
    {
        int pid = 0;
        int i;
        /* 初始化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其他进程 */
        for(i=1;i<MAX_TASK_NUM;i++)
        {
            memcpy(&task[i],&task[0],sizeof(tPCB));
            task[i].pid = i;
            task[i].state = -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];
        }
        
        /* 用task[0]开始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 */
            "popl %%ebp
    	"
            : 
            : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   /* input c or d mean %ecx/%edx*/
        );
    }   
    
    void my_process(void)
    {
        int i = 0;
        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);
            }     
        }
    }
    

    本c文件中,有my_start_kernel和my_process两个函数,其中,前者为mykernel内核代码的入口函数,后者为进程的入口函数,进程在运行中输出当前进程号,并通过my_need_sched变量判断是否需要调度。
    其中对0号进程的启动汇编代码进行分析:
    "movl %1,%%esp " /* set task[pid].thread.sp to esp / 将当前进程0的sp赋给esp
    "pushl %1 " /
    push ebp / 进程0的sp入栈
    "pushl %0 " /
    push task[pid].thread.ip / 进程0的ip入栈
    "ret " /
    pop task[pid].thread.ip to eip / 将进程0的ip赋给eip
    "popl %%ebp " 执行完其他进程,回到0号进程,出栈
    :
    : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /
    input c or d mean %ecx/%edx*/ 输入,将0号进程的ip、sp值分别存入ecx、edx寄存器中,分别对应上面的%0,%1

    4、问题与总结

    本次实验没有遇到什么重大的问题,但是小毛病犯了一堆,比如在将代码拷入实验楼linux环境的vim中时,实验楼的粘贴板不知为何复制粘贴会缺失一段代码,后在编译c文件的时候总是报错,这个问题找了好久,后来发现是粘贴板粘贴的代码缺失。还有一个是在make时找不到文件,后来发现修改代码是在/mykernel目录下进行修改的,make编译内核需要在LinuxKernel/linux-3.9.4目录下进行,需要返回上一级菜单进行make。
    总共来讲,本周各种事情比较多,学习的计划一拖再拖,推迟了好久才完成,以后一定要合理分配时间,这一点尤为重要。
    还有,学习要认真仔细,尽量避免因为犯低级错误而白白消耗大量学习时间。

  • 相关阅读:
    CtsVerifier-Bluetooth-LE-SEcure-ClientServer-Test测试pass但是无法选择PassButton
    error: 'LOG_TAG' macro redefined
    AndroidBuildSystem资料
    [dumpsys input]dumpsys input没有KeyEvent-KeyCode-MotionEvent键值
    [GTS]GtsSecurityHostTestCases#testNoExemptionsForSocketsBetweenCoreAndVendorBan
    [Gts]Fail-GtsTvTestCases#testLauncherChange
    [Cts-Verifier]waiver-Camera-ITS-Test
    历史角度看Support Library 到 AndroidX
    基于大疆无人机SDK二次开发
    关于Support库28及以上版本无法查看源码问题
  • 原文地址:https://www.cnblogs.com/intoxication/p/9866849.html
Copyright © 2011-2022 走看看