2019-2020-1 20199324 "Linux kernel principle and Analysis" in the third week of work

The second chapter how the operating system works

A. Knowledge summary

1. The three computer magic

  • Stored program computer
  • Function call stack mechanism. Stack: is the path and call records function parameter storage space C language runtime must use.
  • Interrupt

2. Stack registers and stack operations associated

  • ESP (stack pointer register): stack pointer (pointing top of the stack )
  • EBP (base pointer register): base pointer (pointing to the top of the stack ), the base address of the current record is used as function calls in C language.
  • EAX: for temporarily storing a number of values, the function returns the value stored default EAX register and returned to the calling function a.
  • EIP: indicates the next instruction to be executed is the address in memory. (Always point to a instruction address)
  • CS (code segment register)
  • push onto the stack, the stack address is four bytes reduced
  • pop the stack, the stack address is incremented four bytes
  • call function call, a call address. The current CS: EIP values ​​in onto stack, CS: EIP points to the entry address of the called function.
  • ret function returns, from the top of the stack pop in here to save the original CS: EIP value, and put CS: EIP to go.
  • enter the function used to create a stack
  • to leave the function stack revocation

3. inline assembly

/*
**通过例子来熟悉内嵌汇编的语法规则
*/

#include <stdio.h>

int main(){

    unsigned int val1 = 1;
    unsigned int val2 = 2;
    unsigned int val3 = 0;
    printf("val1:%d,val2:%d,val3:%d\n",val1,val2,val3);
    

    asm volatile(
    "movl $0,%%eax\n\t"        //把EAX清0
    "addl %1,%%eax\n\t"        //把ECX的值与EAX寄存器求和,然后放到EAX寄存器中去。%eax += val1
    "addl %2,%%eax\n\t"        //把val2的值加上val2的值再放到EAX中。%eax += val2
    "movl %%eax,%0\n\t"        //val1+val2的值存储的地方放到内存val3中。val3 = %eax
    :  "=m" (val3)                      
    :  "c" (val1),"d" (val2)
    );


    printf("val1:%d+val2:%d=val3:%d\n",val1,val2,val3);

    return 0;
}
  • Inline assembly common qualifier

Qualifier description
"a" The input variables into EAX
"b" The input variables into EBX
"c" The input variables into ECX
"d" The input variables into EDX
"s" ESI input variables into
"D" The input variables into EDI
"r" The general-purpose register into the input variables, i.e. EAX, EBX, ECX, EDX, ESI, EDI one
"eax" Destruction description section
"m" Memory variable
"=" Operand in the instruction just written (output operands)
"+" Operand is (the number of input and output operations) read in the instruction type
  • summary

    • Register will be more than one percent in front escape character, there are two percent
    • Output of the input number are respectively 1%, 2%, 3%, ......
    • Embedded assembly when the front portion of each input or output may add a qualifier

4. a virtual x86-CPU hardware platform

Important instructions

$ 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 /*查看搭建起来的内核启动效果*/

The kernel starts to build up the following effects:

Entering mykernel Linux-3.9.4 directory under the root directory kernel source code, and see mymain.c myinterrupt.c the QEMU window output code.

Function can be seen my_start_kernel (void) mymain.c the function, performed every 100,000 secondary outputs "my_start_kernel here i".

mymain.c code is executed constantly while an interrupt handler context, periodically generates an interrupt the clock signal, it can be triggered in mykernel.c my_timer_hander (void) function. This simulates x86CPU with a clock interrupt.

The key code analysis

  • 1.mypcb.h header, used to define the process control block.

  • 2.mymain.c entrance mykernel kernel code responsible for initializing the various components of the kernel.

asm volatile(
        "movl %1,%%esp\n\t"     /*将进程原堆栈的栈底的地址存入ESP寄存器中*/
        "pushl %1\n\t"          /*将当前ESP寄存器的值入栈*/
        "pushl %0\n\t"          /*将当前进程的EIP寄存器的值入栈*/
        "ret\n\t"               /*让入栈的进程EIP保存到EIP寄存器中*/
        "popl %%ebp\n\t"        /*这里不会被执行,只是一种编码习惯,与前面的push结对出现*/
        : 
        : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   /* input c or d mean %ecx/%edx*/
    );

Stack changes:

  • 3.myinterrupt.c clock interrupt handling and process scheduling

 if(next->state == 0)
    {        
        /*进程调度关键代码*/
        asm volatile(   
            "pushl %%ebp\n\t"       /*保存当前EBP到堆栈*/
            "movl %%esp,%0\n\t"     /*保存当前ESP到当前PCB中*/
            "movl %2,%%esp\n\t"     /*将next进程的堆栈栈顶的值存到ESP寄存器*/
            "movl $1f,%1\n\t"       /*保存当前进程的EIP值,下次回复进程后将在标号1开始执行*/    
            "pushl %3\n\t"          /*将next进程继续执行的代码位置(标号1)压栈*/
            "ret\n\t"               /*出栈标号1到eip寄存器*/
            "1:\t"                  /*标号1,也就是next进程开始执行的位置*/
            "popl %%ebp\n\t"        /*恢复EBP寄存器的值*/
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        ); 
        
        my_current_task = next;
        printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
    } 
    
    else /*next该进程第一次被执行*/
    {
        next->state = 0;
        my_current_task = next;
        printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
        asm volatile(   
            "pushl %%ebp\n\t"       /*保存当前EBP到堆栈*/
            "movl %%esp,%0\n\t"     /*保存当前ESP到当前PCB中*/
            "movl %2,%%esp\n\t"     /*载入next进程的栈顶地址到ESP寄存器*/ 
            "movl %2,%ebp\n\t"      /*载入next进程的堆栈基地址到EBP寄存器*/   
            "movl $1f,%1\n\t"       /*保存当前EIP寄存器值到PCB,这里$1f是指上面的标号1*/
            "pushl %3\n\t"          /*把即将执行的进程的代码入口地址入栈*/
            "ret\n\t"               /*出栈进程的代码入口地址到EIP寄存器*/
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        ); 
    }

Assume that the system only two processes are processes 0 and 1 process. 0 initialization process executed by the kernel at startup, a process scheduling, to start the implementation process 1. 1 because the process has never been executed, was the first to be executed, so the execution of the code else. To analyze the change in the scheduled process stack 1 below.

At this point, the beginning of the implementation process 1, if the process of scheduling occurs during execution of a process, the process of re-0 is scheduled for execution at this time if the code should be in the execution. if the execution code embedded assembly stack change as follows:

Guess you like

Origin www.cnblogs.com/yangdd/p/11600828.html