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

    一、课本知识

    1.计算机的3个法宝:存储程序计算机、函数调用堆栈、中断机制
    2.堆栈具体的作用有:记录函数调用框架、传递函数参数、保存返回值的地址、提供函数内部局部变量的存储空间等。
    3.堆栈相关的寄存器:
    ESP:堆栈指针,堆栈空间是从高地址向低地址增长的(X86体系结构)。
    EBP:基址指针,在C语言中用作记录当前函数调用基址。
    4.堆栈操作:
    push:栈顶地址减少4个字节(32位),并将操作数放入栈顶存储单元。
    pop: 栈顶地址增加4个字节(32位),并将栈顶存储单元的内容放入操作数。
    5.其他操作
    call:将当前CS:EIP的值压入栈顶,CS:EIP指向被调用函数的入口地址。
    ret:从栈顶弹出原来保存在这里的CS:EIP的值,放入CS:EIP中。
    6.对于x86 CPU,通过堆栈来传递参数的方法是从右到左依次压栈。

    二、实验内容:完成一个简单的时间片轮转多道程序内核代码

    1.实验代码

    mengning/mykernel下载代码

    mypcb.h 定义进程控制块

    /*
     *  linux/mykernel/mypcb.h
     *
     *  Kernel internal PCB types
     *
     *  Copyright (C) 2013  Mengning
     *
     */
    
    #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);
    

    myinterrupt.c 其中my_schedule(void)函数:切换进程

    /*
     *  linux/mykernel/myinterrupt.c
     *
     *  Kernel internal my_timer_handler
     *
     *  Copyright (C) 2013  Mengning
     *
     */
    #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指针指向了下一号进程,prev指针指向了本进程
        next = my_current_task->next;
        prev = my_current_task;
        if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */  //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
    	" 	    /* save ebp */                  //保存当前进程esp到堆栈,保存进程上下文
            	"movl %%esp,%0
    	" 	/* save esp */                      //保存当前esp到当前进程pcb中
            	"movl %2,%%esp
    	"     /* restore  esp */                  //将next进程的堆栈栈顶的值存入esp寄存器
            	"movl $1f,%1
    	"       /* save eip */	                    //保存当前进程的eip值,下次恢复进程后将在标号1开始执行
            	"pushl %3
    	"                                              //将next进程继续执行的代码位置(标号1)压栈(next进程的堆栈)
            	"ret
    	" 	            /* restore  eip */              //出栈标号1到esp寄存器
            	"1:	"                  /* next process start here */       //标号1,即next进程开始执行的位置
            	"popl %%ebp
    	"                                            //恢复ebp寄存器的值
            	: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            	: "m" (next->thread.sp),"m" (next->thread.ip)
        	); 
     	
        }
        else                                                            //对应next进程第一次执行
        {
            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 */           //保存当前进程esp到堆栈,保存进程上下文
            	"movl %%esp,%0
    	" 	/* save esp */               //保存当前进程esp到pcb,保存进程上下文
            	"movl %2,%%esp
    	"     /* restore  esp */           //载入next进程的栈顶地址到esp寄存器,即将工作堆栈切换到next进程
            	"movl %2,%%ebp
    	"     /* restore  ebp */           //载入next进程的堆栈基址到ebp寄存器,由于next进程为一个新进程,堆栈为空
            	"movl $1f,%1
    	"       /* save eip */	             //保存当前eip寄存器值到pcb,$1f指向上面编号1,堆栈不变
            	"pushl %3
    	"                                       //把即将执行的进程的代码入口地址入栈(next进程的堆栈)
            	"ret
    	" 	            /* restore  eip */       //出栈进程的代码入口地址到EIP寄存器
            	: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            	: "m" (next->thread.sp),"m" (next->thread.ip)
        	);          
        }   
        return;	
    }
    

    mymain.c

    /*
     *  linux/mykernel/mymain.c
     *
     *  Kernel internal my_start_kernel
     *
     *  Copyright (C) 2013  Mengning
     *
     */
    #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];                  //定义4个进程
    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*/            //初始化0号进程
        task[pid].pid = pid;
        task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */             //0号进程为执行状态
        task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;     //进程的入口为my_process
        task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
        task[pid].next = &task[pid];
        /*fork more process */      //创建并初始化1,2,3号进程为就绪状态
        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];
        }
        /* start process 0 by task[0] */      //0号进程开始执行
        pid = 0;
        my_current_task = &task[pid];
    	asm volatile(                     
        	"movl %1,%%esp
    	" 	/* set task[pid].thread.sp to esp */ //esp寄存器指向进程0的栈底
        	"pushl %1
    	" 	        /* push ebp */                       //将当前ebp寄存器的值入栈,因为是空栈,esp与ebp相同
        	"pushl %0
    	" 	        /* push task[pid].thread.ip */       //将当前eip入栈
        	"ret
    	" 	        /* pop task[pid].thread.ip to eip */ //my_process(void)的位置放入eip中,0号进程启动,开始执行my_process(void)
        	"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)      //每当运行10000000 次后就会进行一次输出
            {
                printk(KERN_NOTICE "this is process %d -
    ",my_current_task->pid);
                if(my_need_sched == 1)      //time_count%1000 == 0 && my_need_sched != 1时时间中断调度
                {
                    my_need_sched = 0;
            	    my_schedule();
            	}
            	printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
            }     
        }
    }
    

    2.编译程序并执行

    $ make
    $ qemu -kernel arch/x86/boot/bzImage
    




    三、总结

    Linux操作系统的正常工作可以说有三个非常重要的部分,就是存储程序原理、堆栈以及中断的支持。 操作系统对进程的管理主要就是进程的管理和调度,为每个进程维护一个进程描述和以及进程间的关系。内核的工作主要有两部分组成,首先运行有一个内核线程,然后就是一些中断处理程序的集合,在中断处理程序中要就行进程的调度。

  • 相关阅读:
    398. Random Pick Index
    382. Linked List Random Node
    645. Set Mismatch
    174. Dungeon Game
    264. Ugly Number II
    115. Distinct Subsequences
    372. Super Pow
    LeetCode 242 有效的字母异位词
    LeetCode 78 子集
    LeetCode 404 左叶子之和
  • 原文地址:https://www.cnblogs.com/gann/p/13866170.html
Copyright © 2011-2022 走看看