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

    《庖丁解牛Linux内核分析》第二章

    • 计算机“三大法宝”
      • 存储程序计算机:将程序存放在计算机存储器中,然后按照存储器中的存储程序的首地址执行程序的第一条指令,以后就按照该程序中编写好的指令执行,直至程序执行结束。
      • 函数调用堆栈:C语言程序运行时必须使用的记录函数调用路径和参数存储的空间。
      • 中断:计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
    • 函数调用堆栈
      • 堆栈是C语言程序运行时必须使用的记录函数调用路径和参数存储的空间,堆栈具体的作用有:记录函数调用框架、传递函数参数、保存返回值的地址、提供函数内部局部变量的存储空间等。
      • 堆栈相关的寄存器
        • ESP:堆栈指针
        • EBP:基址指针,在C语言中用作记录当前函数调用基质。
        • 堆栈空间是从高地址向低地址增长的。
      • 堆栈操作
        • push:栈顶地址减少4个字节(32位),并将操作数放入栈顶存储单元。
        • pop:栈顶地址增加4个字节(32位),并将栈顶存储单元的内容放入操作数。
      • 其他关键寄存器
        • CS:EIP总是指向下一条的指令地址
      • 程序用EAX寄存器来保存返回值。如果有多个返回值,EAX寄存器返回的是一个内存地址。
      • 函数体内的局部变量是通过堆栈来存储的。

    • 内嵌汇编
    
    asm volatile(                       /*__asm__是GCC关键字asm的宏定义,是内嵌汇编的关键字,表示这是一条内嵌汇编语句。__volatile__的宏定义,告诉编译器不要优化代码,汇编指令保留原样。*/
    "movl $0,%%eax
    	"          /* clear %eax to 0。将EAX清0 */
    "addl %1,%%eax
    	"          /* %eax+=val1。%1指下面的输出和输入的部分,此处是指val1,该句意为把val1的值放到ECX里面,然后把ECX的值与EAX寄存器求和放入EAX寄存器中。 */
    "addl %2,%%eax
    	"          /* %eax+=val2。该句意为把val2的值放到ECX里面,然后把ECX的值与EAX寄存器(val1的值)求和放入EAX寄存器中。*/
    "movl %%eax,%0
    	"         /* val2=%eax。把val1加上val2的值存储的地方放到%0,即val3。 */
    : "=m" (val3)                        /* =m意思是写到内存变量里面去 */
    : "c" (val1),"d" (val2)            /* c指用ECX寄存器存储val1的值 */
    );
    
    
    • 时钟中断
      • 当一个中断信号发生时,CPU把当前正在执行的程序的CS:EIP寄存器和ESP寄存器等都压到了一个叫内核堆栈的地方,然后把CS:EIP指向一个中断处理程序的入口,做保存现场的工作,之后执行其他程序,等重新回来时再恢复现场,恢复CS:EIP寄存器和ESP寄存器等,继续执行程序。

    实验楼实验二

    下载linux-3.9.4.tar.xz,并搭建虚拟的x86CPU实验平台。

    
    $ sudo apt-get install qemu # install QEMU
    $ sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu
    $ wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.9.4.tar.xz # download Linux Kernel 3.9.4 source code
    $ wget https://raw.github.com/mengning/mykernel/master/mykernel_for_linux3.9.4sc.patch # download mykernel_for_linux3.9.4sc.patch
    $ xz -d linux-3.9.4.tar.xz
    $ tar -xvf linux-3.9.4.tar
    $ 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
    
    

    在Linux-3.9.4内核源代码根目录下cd进入mykernel目录,可以看到QEMU窗口输出的内容的代码mymain.c和myinterrupt.c。

    myinterrupt.c和mymain.c代码分别如下。

    mykernel/mypcb.h:

       
    /*
     *  linux/mykernel/mypcb.h
     *
     *  Kernel internal PCB types
     *
     *  Copyright (C) 2013  Mengning
     *
     */
    
    #define MAX_TASK_NUM        4              /* 最大任务数定义为4 */
    #define KERNEL_STACK_SIZE   1024*2 # unsigned long       /* 内核堆栈数目定义为1024*2 */
    /* 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 */
        unsigned long 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);
    
    

    mykernel/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];
    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*/
        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 more process */
        for(i=1;i<MAX_TASK_NUM;i++)       /* 复制进程信息,并将其加入进程列表尾部 */
        {
            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
    	" 	/* 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 */
        	: 
        	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	/* input c or d mean %ecx/%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);
                if(my_need_sched == 1)
                {
                    my_need_sched = 0;
            	    my_schedule();
            	}
            	printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
            }     
        }
    }
    
    

    mykernel/myinterrupt.c

    
    /*
     *  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 = 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);  
        	/* switch to next process */
        	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)
        	); 
        }  
        return;	
    }
    
    
    

    分析mymain.c中的内嵌汇编部分

    
    asm volatile(                                /* %0表示参数thread.ip,%1表示参数thread.sp */
            "movl %1,%%esp
    	"          /* 将task[pid].thread.sp移入ESP */
            "pushl %1
    	"                      /* 将EBP入栈 */
            "pushl %0
    	"                      /* 将task[pid].thread.ip入栈 */
            "ret
    	"                                 /* 将task[pid].thread.ip返回赋入eip */
            "popl %%ebp
    	"                 /* 将EDP压出栈 */
            : 
            : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)          /* 设置ECX和EDX存储不同参数 */
        );
    
    

    分析myinterrupt.c中的内嵌汇编部分

    
    asm volatile(   
                "pushl %%ebp
    	"                   /* 将EBP入栈保存 */
                "movl %%esp,%0
    	"              /* 把当前进程的esp赋给%0(thread.sp),保存当前进程的ESP到PCB */
                "movl %2,%%esp
    	"              /* 将下一进程的栈顶地址存入ESP */
                "movl $1f,%1
    	"                      /* 保存当前EIP寄存器值到PCB */
                "pushl %3
    	"                           /* 把下一个进程代码入口地址入栈 */
                "ret
    	"                                     /* 出栈进程的代码入口地址返回到EIP寄存器 */
                "1:	"                                         /* 标号1,即下一进程开始执行的位置 */
                "popl %%ebp
    	"                     /* 恢复EBP寄存器的值 */
                : "=m" (prev->thread.sp),"=m" (prev->thread.ip)           /* 将当前和下一个进程栈顶地址和IP地址存入内存寄存器中,并根据变量%0%1%2%3命名 */
                : "m" (next->thread.sp),"m" (next->thread.ip)
            ); 
    
    
    
    asm volatile(   
                "pushl %%ebp
    	"               /* 保存当前进程EBP到堆栈 */
                "movl %%esp,%0
    	"          /* 保存当前进程ESP到PCB */
                "movl %2,%%esp
    	"           /* 将next进程的栈顶地址保存到ESP寄存器 */
                "movl %2,%%ebp
    	"           /* 将next进程的堆栈基地址保存到EBP寄存器 */
                "movl $1f,%1
    	"                  /* 将当前EIP寄存器值存到PCB,标号为1 */
                "pushl %3
    	"                      /* 把下一个进程入口地址入栈 */
                "ret
    	"                                 /* 将出栈进程的代码入口地址存到EIP寄存器 */
                : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
                : "m" (next->thread.sp),"m" (next->thread.ip)
            );
    
    

    此为本人Linux学习第三周的内容,如有不足,还请批评指正,不胜感激。

    以上

  • 相关阅读:
    HDU2027 统计元音 一点点哈希思想
    湖南工业大学第一届ACM竞赛 数字游戏 字符串处理
    湖南工业大学第一届ACM竞赛 我素故我在 DFS
    HDU3293sort
    HDU2082 找单词 母函数
    HDU1018 Big Number 斯特林公式
    湖南工业大学第一届ACM竞赛 分糖果 位操作
    UVA 357 Let Me Count The Ways
    UVA 147 Dollars
    UVA 348 Optimal Array Multiplication Sequence
  • 原文地址:https://www.cnblogs.com/qianxiaoxu/p/11597458.html
Copyright © 2011-2022 走看看