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

    操作系统是如何工作的

    一:函数调用堆栈

    计算机的三个“法宝”:

    1.存储程序计算机:基本上是所有计算机的基础性框架。
    
    2.函数调用堆栈:堆栈是C语言程序运行时必须使用的记录函数调用路径和参数存储的空间,堆栈的具体作用有:记录函数调用框架、传递函数参数、保存返回值的地址、提供函数内部局部变量的存储空间等。
    
    3.中断机制:中断的流程大概是产生中断、响应中断、关闭中断、保护中断、识别中断源、现场保护、中断服务、现场恢复。
    

    堆栈相关的寄存器:

    1.ESP:堆栈指针
    
    2.EBP:基址指针,在C语言中用作记录当前函数调用基址。
    

    堆栈操作:

    对于x86体系结构来讲,堆栈空间是从高地址向低地址增长的。

    二:借助Linux内核部分源代码模拟存储程序计算机工作模型及时钟中断

    实验步骤:(因为实验楼已经为我们搭配好了qemu环境,并且搭建了虚拟的x86 CPU。所以可以直接进行试验)

    1.使用实验楼的虚拟机打开shell,并且进入到内核目录中:
    
        >cd ~/LinuxKernel/linux-3.9.4
        >rm -rf mykernel
        >patch -p1 < ../mykernel_for_linux3.9.4sc.patch
        >make allnoconfig
        >make
    

    2.查看搭建起来的内核启动效果:
    
        > qemu -kernel arch/x86/boot/bzImage
    

    3.进入到mykernel目录,查看mymain.c和myinterrupt.c:
    
        > cd mykernel
    

    4.查看mymain.c文件:
    
        >vi mymain.c
    

    在mymain.c中,my_start_kernel是操作系统的入口,在该函数体中,有一个循环,每循环100000次,就会打印一条语句。因为如今的CPU速度都很快,所以,呈现在我们眼前就是在很短的时间打印一次。

    5.查看myinterrupt.c文件:
    
        > vi myinterrupt.c
    

    在myinterrupt.c中,每次的始终中断,都会打印语句“my_timer_handler here”。

    6.通过实验所给的代码链接,建立一个简单的时间片轮转多道程序:
    

    7.分析进程的启动和进程的切换机制:
    
    mypcb.h:
    
        #define MAX_TASK_NUM      4                                      //定义总有4个任务/进程
    
        #define KERNEL_STACK_SIZE   1024*2                      //每个进程的堆栈大小为2KB
    
        /*用于保有存当前进程的esp和eip*/
    
        struct Thread {
    
            unsigned long		ip;
    
            unsigned long		sp;
    
        };
    
        /*定义进程结构体*/
    
        typedef struct PCB{
    
            int pid;        //进程id
    
            volatile long state;	//进程状态
    
            unsigned long stack[KERNEL_STACK_SIZE];        //进程堆栈
    
            struct Thread thread;        //保存esp、eip
    
            unsigned long	task_entry;        //进程入口
    
            struct PCB *next;        //在进程链表中指向下一个进程
    
        }tPCB;
    
       /*声明调度函数*/
    
        void my_schedule(void);
    
    
       mymain.c:
        
        #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;
    
            /* 初始化0号进程*/
    
            task[pid].pid = pid;
        
            task[pid].state = 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];
    
            task[pid].next = &task[pid];        //进程链表中,next指针指向自己
    
            /*创建其他进程*/
    
            for(i=1;i<MAX_TASK_NUM;i++)
    
            {
    
                 /*把0号进程的状态拷贝给当前进程,不同成员值再单独赋值*/
                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
    	" 	/*把当前进程的栈指针赋给esp*/ 
    
    	        "pushl %1
    	" 	        /* 把当前进程的栈指保存到栈中 */
    
    	        "pushl %0
    	" 	        /* 保存当前进程的eip到栈中 */
    
    	        "ret
    	" 	            /* 返回操作 */
    
            	: 
            
            	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	/*ecx=当前进程的eip, 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);
                   /* 判断是否需要切换进程,1需要切换,0不需要切换*/
                    if(my_need_sched == 1)
    
                    {
    
                        my_need_sched = 0;
    
        	            my_schedule();        //切换进程
    
        	        }
    
        	        printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
    
                }     
    
            }
    
        }
    
    
    myinterrupt:
        
        #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)
    
            {
    
                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<<<
    ");
    
            next = my_current_task->next;
    
            prev = my_current_task;
    
            if(next->state == 0)        /*state:-1表示不可用,0表示正在执行,>0表示没有运行 */
    
            {        
    
                /*当前进程指针指向下一下进程*/
    
            	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
    	" 	/*保存当前的栈指针到上一个进程的sp中*/
    
        	        "movl %2,%%esp
    	"    /*把下一个进程的sp赋给esp*/
    
        	        "movl $1f,%1
    	"       /*保存当前进程的eip到上进一个进程的ip中*/
    
        	        "pushl %3
    	"         /*将下一个进程的ip压到栈中*/
    
        	        "ret
    	" 	            /* 返回操作 */
    
        	        "1:	"                  /* next process start here */
    
        	        "popl %%ebp
    	"       /* 弹出一个进程的栈基指指针*/
    
        	        : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
    
        	        : "m" (next->thread.sp),"m" (next->thread.ip)
    
    	        ); 
    
            }  
    
            return;	
    
        }
    

    三:总结

       通过本章的学习,我对计算机的三大法宝有了进一步的了解,清楚它们各自的作用是什么。并且对C语言内嵌入汇编代码有了一定的了解,但从解读内嵌代码到自行编写还需要很长的时间来学习。实验楼给我们提供了良好的实验环境,供我们学习如何进行借助Linux内核部分源代码模拟存储程序计算机工作模型及时钟中断,它的重难点就是进程的切换,进程在执行的过程中,当时间片用完需要进行进程切换时,预压先存储当前进程的上下文环境,下次被调度时,需要恢复进程的上下文环境,才可以实现多道程序的并发性执行。
  • 相关阅读:
    数量关系
    笨办法学python问题记录
    CSS布局与定位
    python学习 预备篇
    基于hexo搭建个人博客
    CSS常用样式
    计算机组成原理(期末篇)
    Codeblock错误提示栏隐藏
    Markdown标记语言
    笨办法学python(不同版本的python代码差别)
  • 原文地址:https://www.cnblogs.com/j1551163790/p/11600806.html
Copyright © 2011-2022 走看看