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内核部分源代码模拟存储程序计算机工作模型及时钟中断,它的重难点就是进程的切换,进程在执行的过程中,当时间片用完需要进行进程切换时,预压先存储当前进程的上下文环境,下次被调度时,需要恢复进程的上下文环境,才可以实现多道程序的并发性执行。
  • 相关阅读:
    Go 语言简介(下)— 特性
    Array.length vs Array.prototype.length
    【转】javascript Object使用Array的方法
    【转】大话程序猿眼里的高并发架构
    【转】The magic behind array length property
    【转】Build Your own Simplified AngularJS in 200 Lines of JavaScript
    【转】在 2016 年做 PHP 开发是一种什么样的体验?(一)
    【转】大话程序猿眼里的高并发
    php通过token验证表单重复提交
    windows 杀进程软件
  • 原文地址:https://www.cnblogs.com/j1551163790/p/11600806.html
Copyright © 2011-2022 走看看