• 2019-2020-1 20199305《Linux内核原理与分析》第三周作业


    操作系统的秘密

    (一)计算机的三大法宝

    • 存储程序计算机;

    • 函数调用堆栈机制;

    • 中断机制。

    (二)堆栈

    (1)堆栈的作用

    • 记录函数调用框架;

    • 传递函数参数;

    • 保存返回值的地址;

    • 提供局部变量存储空间。

    (2)堆栈操作

    • push:栈顶地址减少4个字节,然后将操作数亚茹栈顶存储单元;

    • pop:栈顶地址增加4个字节,然后将栈顶存储的内容放回原寄存器。

    (3)相关寄存器

    • ESP:堆栈指针(stack pointer),用于指向栈顶。

    • EBP:基址指针(base pointer),用于记录当前函数调用的基址。

    • EAX:累加寄存器(Extended accumulator register),用于存储返回值。

    • CS:EIP:用于指向下一条指令地址,它的实用有几种不同情形:

      顺序执行:总是指向地址连续的下一条指令;

      跳转/分支:执行这样的指令时,CS:EIP的值会根据程序需要被修改;

      call:将当前存储的值压入栈顶,然后指向被调用函数的入口地址;

      ret:原来保存的值从栈顶弹出,回到CS:EIP中。

    • 现在谈一下个人对调用函数框架的理解

      调用者用call指令来实现函数调用,接下来生成一个新的堆栈给被调用者使用,

      这个过程涵盖两步——建立堆栈框架和清空堆栈拆除框架,它们由enter和leave

      两个指令实现。

    (三)中断

    没有中断机制之前,计算机只能一个个地执行程序(批处理),而无法实现并发工作。

    现在写一个时间片轮转调度的操作系统内核(使用到函数调用堆栈和内嵌汇编):

    (1)使用实验楼的虚拟机打开shell,启动内核

    (2)运行效果如下

    (3)查看mymain.c和myinterrupt.c



    (4)代码分析

    • mymain.c

    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;
        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];/*ESP指向栈顶*/
        task[pid].next = &task[pid];
        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];
        }
        pid = 0;
        my_current_task = &task[pid];
    	asm volatile(
        	"movl %1,%%esp
    	"   
        	"pushl %1
    	" 	   /*EBP入栈*/
        	"pushl %0
    	" 	   /*task[pid].thread.ip入栈*/
        	"ret
    	" 	            
        	"popl %%ebp
    	"   /*EBP出栈*/
        	: 
        	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	
    	);
    }   
    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);  
               /*循环10000000次之后判断是否需要调度*/
                if(my_need_sched == 1)
                {
                    my_need_sched = 0;
            	    my_schedule();
            	}
            	printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
            }     
        }
    }
    

    总结

    当一个中断信号发生时,CPU把当前正在执行的EIP寄存器压栈,后把EIP指向中断程序入口保护

    现场。等结束后在恢复现场,恢复EIP寄存器,继续执行下一条指令,这使多个程序能够实现并发

    工作。套用到实验中就是进程的切换,进程在执行的过程中,当时间片用完需要进行进程切换时,

    需要先保存当前的今晨执行的上下文环境,下次进程被调度时,需要恢复进程,以此实现多道程

    序的并发执行。

  • 相关阅读:
    shell 编程 如何实现 比较两个整数的大小
    从Mysql某一表中随机读取n条数据的SQL查询语句
    AS3中UTF8、GB2312、BIG5、GBK编码转换类
    Google Map API V3 离线版
    linux下解压命令大全
    PHP 5.3无法安装Memcached解决方案
    根据淘宝商品 num_iid 批量生成淘宝客链接的 PHP 函数
    Linux curl使用简单介绍
    TCP/IP UDP用户数据报协议 运输层
    TCP/IP 应用层
  • 原文地址:https://www.cnblogs.com/20199305yizihan/p/11608531.html
Copyright © 2020-2023  润新知