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

How the operating system works

Basics

1, three magic computer: stored program computer, the function call stack mechanism is interrupted.
2, the specific role of the stack: the frame recorded as a function call, the transfer function parameters, save the address of the return value, provide storage of local variables inside a function.
3, the stack-related registers:
the ESP: Stack pointer stack.
EBP: base address pointer to the stack bottom. C language base address current call record function.
CS: EIP: points to the address of the next instruction.
EAX: return value or return address.
3, stack operations:
Push: the stack, the stack address decrease of 4 bytes (32 bits), the operand stack into a storage unit.
pop: the stack, the stack address is increased by 4 bytes (32 bits), the contents out into the top of the stack memory cell operands.
4, in Linux, you can use objdump tools for disassembly.
5, interrupt mechanism: when an interrupt signal occurs, CPU stores the currently executed program EIP and ESP pressed into the kernel stack, and then the EIP to the interrupt handler entry, saving the scene after other programs, executing the re-site restoration, restoration EIP and ESP, continue to implement the program before the interruption.
6, the former will be more inline assembly register an escape symbol%. % Plus a digital representation of the second output section, and a third portion of the input portion of the fourth number of damage described. This sentence always felt a little abstract, is not very understanding, examples watching video is also clear about, hope that teachers can explain (〃 '▽' 〃)

Construct a simple operating system kernel based on the mykernel

1, the first part of the experiment

  • Open a shell on a virtual machine laboratory building, and the following steps:
cd LinuxKernel/linux-3.9.4
qemu -kernel arch/x86/boot/bzImage

And outputs the result found in the expected time to time does not appear, as shown in FIG.

After then re-enter the following command to make the compiler perform the correct result.

cd LinuxKernel/linux-3.9.4
make allnoconfig
make
qemu -kernel arch/x86/boot/bzImage

After the successful launch of the kernel out, found not exit QEMU (T_T)
found that after a lot of people say to find information using Ctrl + A press X, but I tried it and did not react. Then I press Ctrl + Alt + A + X to quit, do not know is not right!

  • Mymain.c and view the contents of myinterrupt.c

Kernel stop execution my_start_kernel (), from time to time be my_timer_handler () interrupt, and then execute a print statement: printk (KERN_NOTICE "\ n >>>>>>>>>>>>>>>>> my_timer_handler here <<<<<<<<<<<<<<<<<< \ n \ n "). Here we find inline assembly statement is printk output instead of printf.

2, the second part of the experiment

  • Mypcb.h, mymain.c, myinterrupt.c the reference code modifications https://github.com/mengning/mykernel
    re make compiled code is modified, the following results:

3, the second part of the experiment code analysis

The main achievement in mykernel scheduling based on the structure of the operating system kernel mainly by the completion of three files: mypcb.h, mymain.c, myinterrupt.c.
mypcb.h: i.e. the definition of the process control block PCB process definition structure. mymain.c: the various components of the kernel initialization process and starts 0. myinterrupt.c: clock interrupt handling and process scheduling algorithm.

mymain.c

#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];                /*pcb的数组*/
tPCB * my_current_task = NULL;
volatile int my_need_sched = 0;              /*是否需要调度的标志*/
void my_process(void)
void __init my_start_kernel(void)            /*内核入口。初始化并启动0号进程*/
{
    int pid = 0;
    int i;
    /* Initialize process 0*/
    task[pid].pid = pid;
    task[pid].state = 0;                               /* -1 未运行, 0 正在运行, >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指向自己*/
    /*fork more process */                         /*初始化更多进程*/
    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];
    }
    /* start process 0 by task[0] */
    pid = 0;                                                /*开始执行0号进程*/
    my_current_task = &task[pid];
    asm volatile(                                      
        "movl %1,%%esp\n\t"                       /*将ESP指向进程0的堆栈栈底,输出部分和输入部分从0开始编号,所以1%指task[pid].thread.sp*/
        "pushl %1\n\t"                                   /*将EBP的值入栈,因为是空栈,EBP=ESP,所以将task[pid].thread.sp入栈即可*/
        "pushl %0\n\t"                                   /*将EIP的值入栈,这里是初始化的值即my_process(void)的位置*/
        "ret\n\t"                                              /*将my_process(void)的位置放入EIP*/   
        "popl %%ebp\n\t"     
        : 
        : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)  
    );
} 

void my_process(void)
{    
    int i = 0;
    while(1)
    {
        i++;
        if(i%10000000 == 0)                        /*循环1000万次才有一次机会判断是否需要调度*/
        {
            printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);
            if(my_need_sched == 1)
            {
                my_need_sched = 0;
                my_schedule();
            }
            printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);
        }     
    }
}
  • The key to start the code above first process execution assembler code asm volatile (), the stack and registers the change process:

myinterrupt.c

#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;

void my_timer_handler(void)      /*设置时间片大小,时间片用完时设置一下调度标志*/
{
#if 1
    if(time_count%1000 == 0 && my_need_sched != 1)
    {
        printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
        my_need_sched = 1;          /*调度执行my_schedule(void)*/
    } 
    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<<<\n");
    /* schedule */
    next = my_current_task->next;
    prev = my_current_task;
    if(next->state == 0)                  /* next对应进程曾经执行过 */
    { 
        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 */
            "pushl %3\n\t"                /* 把即将进行的进程的代码位置标号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);
        /* switch to new process */
        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 */ 
            "pushl %3\n\t"                  /* 把即将进行的进程的代码入口地址入栈 */ 
            "ret\n\t"                             /* 把即将进行的进程的代码入口地址存入EIP */
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
        );          
    }   
    return; 
}
  • Suppose there are two processes, one process is scheduled change process from the beginning, and the stack registers:

to sum up

Operating system kernel has a starting position, an initialization operation performed from this start position. Operation then start a process, to switch between processes will scheduling algorithms (such as round-robin), i.e. the use of stored-site% esp, register values% eip kernel stack save process corresponding to the process of switching, then the CPU assigned to the next process and begin execution.

Guess you like

Origin www.cnblogs.com/fanxiaonan/p/11605208.html