Chen Tie + Original works reproduced please specify the source + "Linux kernel analysis" MOOC course http://mooc.study.163.com/course/USTC-1000029000 ". In particular, all the code from Monensin Teacher's Mykernel, perhaps for the purpose of the exercise has been modified, can also be ignored.
The process of learning is in fact a constant imitation, repeating the content of the teacher's presentation, and practicing continuously until it becomes the knowledge that can be expressed independently. I am really stupid, homework reluctantly completed, fortunately, also work hard, spend more time, after all, is the process of their own hard study embodies. So put out to give Fang home a smile, at least is also the harvest of their own learning.
First, the experiment is the experimental building environment, the virtual machine environment is as follows: Linux d0c756f6c18a 3.13.0-30-generic #55-ubuntu SMP Fri Jul 4 21:40:53 UTC x86_64 x86_64 x86_64 Gnu/linux. The experiment starts with simple code and you can see the interrupt schedule demo.
CD linuxkernel/linux-3.9.4 Qemu-kernel Arch/x86/boot/bzimage |
Second, copy the teacher's code MYPCH.B,MYMAIN.C,MYINTERRUPT.C to the Mykernel directory. Go back to the kernel directory:
Make all Qemu-kernel Arch/x86/boot/bzimage |
You can see that process scheduling is reflected in the virtual machine. Following:
650) this.width=650; "src=" http://s3.51cto.com/wyfs02/M00/5B/32/wKioL1UCNgqRaXrtAAHEoAz_Ako469.jpg "style=" float: none; "Title=" Qh12. PNG "alt=" Wkiol1ucngqraxrtaaheoaz_ako469.jpg "/>
650) this.width=650; "src=" http://s3.51cto.com/wyfs02/M00/5B/38/wKiom1UCNOrRFLJiAAPPgY7b62w412.jpg "style=" float: none; "Title=" qh30. PNG "alt=" Wkiom1ucnorrfljiaappgy7b62w412.jpg "/>
Third, the following analysis of the code execution process, describe the modern operating system working mechanism.
1. In order to achieve efficient implementation in the Linux core, a large number of inline compilations are used, so let's start with the knowledge of inline assembly. (1) Although modern compiler optimization code, but still compared to handwritten assembly code, (2) some platform-related instructions must be handwritten, there is no equivalent syntax in C language, such as x86 is Port I/O.
gcc
Provides an extension syntax to use inline assembly in C code. The simplest format is __asm__("assembly code");
, for example, __asm__("nop");
just executing an empty instruction. To execute multiple assembly instructions, you should \n\t
separate the instructions with each one.
Inline assembly to be associated with a variable of C, using the full inline assembly format:
__ASM__ (Assembler template:output operands/* Optional */: Input operands/* Optional */: List of clobbered registers/* optional */);
This format consists of four parts, the first part is the assembly instruction, the second part and the third part is the constraint condition, the second part indicates the output of the assembly instruction to which C operand, the C operand should be an lvalue expression, the third part indicates which C operand the assembly instruction needs to get input from, Part IV is a list of registers that have been modified in the assembly instruction, indicating which register values of the compiler __asm__
will change when the statement is executed. The latter three parts are optional, if you have to fill out, there is no space to write only a :
number.
The 2.mypcb.h code is as follows:
/** linux/mykernel/mypcb.h** kernel internal pcb types** copyright (C) 2013 mengning**/#define MAX_TASK_NUM 4 //defines the maximum number of processes that the system executes. #define  KERNEL_STACK_SIZE 1024*8     //kernel stack size/* cpu-specific state of this task */struct thread {               //Defining Structure Body thread unsigned long ip; //storing instruction pointers and stack pointers unsigned long sp;}; typedef struct pcb{ //struct type Process Control block pcb int pid; //Process id volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ //Process Status char stack[kernel_stack_size]; //Process Stack /* CPU-specific state of this task */ struct thread thread; unsigned long task_ entry; //entrance struct PCB *next; //forms a linked list, the next process}tpcb;void my_schedule (void ); //Dispatch function
3. The following MYMAIN.C main program code
/** 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]; //define the process array Tpcb * my_ current_task = null; //current process pointer, starting from process No. 0 volatile int my_need_sched = 0; The //0 process does not need to dispatch void my_process (void); Void __init my_start_kernel (void) //kernel creation process, starting from process No. 0 to initialize { int pid = 0; int i; /* initialize process 0*/ task[pid].pid = pid; task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */ //instruction Pointer to self task[pid].task_entry = task[pid].thread.ip = (Unsigned long) my_process; The //stack points to the defined kernel stack 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++) //to start more processes through the fork function, this example 0,1,2,3 { //We are a simple demo, here directly copy the status of Process No. 0 as a new process 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; //processes form a linked list task[i-1].next = &task[i]; } /* start process 0 by task[0] */ //START Process # No. 0 pid = 0; my_current_task = &task[pid]; /* Inline assembly,%0,%1 represents the variable "C" for the input and output section for ECX, "D" for edx, "=m "Indicates that the memory %%reg represents the register. The \n\t means the end. The following assembly code is not difficult to understand, is for efficiency. Build up the operating environment of the CPU and start the process of No. No. 0. */ asm volatile ( "Movl %1,%%esp\n\t" &NBSP;/*&NBSP;SET&NBSP;TASK[PID].THREAD.SP&NBSP;TO&Nbsp;esp */ "pushl %1\n\t" &NBSP;/*&NBSP;PUSH&NBSP;EBP */ "pushl %0\n\t" /* push task[pid]. thread.ip */ "ret\n\t" /* pop task[pid]. thread.ip to eip */ "popl %%ebp\n\t" : : "C" ( TASK[PID].THREAD.IP), "D" (TASK[PID].THREAD.SP) /* input c or d mean % ecx/%edx*/ ); } /* The following is the code that our simple process executes, Used to let humans know which process the CPU is performing. In fact, many operating system processes just perform in the background, do not need human interaction, but we do not ignore them. */ void my_process (void) { int i&nbSp;= 0; while (1) { i++; if (i%10000000 == 0) //loop 10 million times, output a process ID, active scheduling, to avoid the message mechanism. { &NBSP;&NBSP;PRINTK (kern_notice "this is process %d -\n", my_current_task->pid); if (my_need_sched == 1) { my_need_sched = 0; my_schedule (); &nBSP;&NBSP;&NBSP;&NBSP;}&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;PRINTK (KERN_ notice "this is process %d +\n", My_current_task->pid); } }}
4. The following is the code for MYINTERRUPT.C and a brief description:
/** 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; //time count has been implemented actively, our simple code does not accept input/** 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 //Count 1000 times and do not switch the process to output a line of reminders if (time_count% 1000 == 0 && my_need_sched != 1) &NBSP;&NBSP;&NBSP;&NBSP;{&NBSP;&NBSP;&NBSP;&NBSP;PRINTK (KERN_NOTICE ">>>my_timer_handler here<<<\n"); 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; //error Handling &NBSP;&NBSP;&NBSP;&NBSP;}&NBSP;&NBSP;&NBSP;&NBSP;PRINTK (kern_notice ">>>my_schedule <<<\n "); /* schedule */ next = my_ CurRent_task->next; prev = my_current_task; if (next- >state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */ { /* switch to next process */ The key code for the //process switch is essentially the same as the analysis function call, saving the current context asm volatile ( "pushl %%ebp\n\t" /* save ebp */ "Movl %%esp, %0\n\t " /* save esp */ " movl %2,%%esp\n\t " /* restore esp */ "movl $1f,%1\n\t" /* save eip */ "pushl %3\n\t" "ret\n\t" /* restore eip */ "1:\t" /* next process start here */ " Popl %%ebp\n\t " : " =m " (PREV->THREAD.SP)," =m " (prev-> THREAD.IP) : "M" (NEXT->THREAD.SP), "M" (NEXT->THREAD.IP) ); &NBSP;&NBSP;&NBSP;&NBSP;MY_CURRENT_TASK&NBSP;=&NBSP;NEXT;&NBSP;&NBSP;&NBSP;&NBSP;PRINTK ( kern_notice ">>>switch %d to %d<<<\n", Prev->pid,next->pid); } else { next->state &NBSP;=&NBSP;0;&NBSP;&NBSP;&NBSP;&NBSP;MY_CURRENT_TASK&NBSP;=&NBSP;NEXT;&NBSP;&NBSP;&NBSP;&NBSP;PRINTK (KERN_ notice ">>>switch %d to %d<<<\n", Prev->pid,next->pid); /* switch to new process */ //set up a new operating environment, Start the new process by starting from the newly-executed line of code. asm volatile ( "pushl %%ebp\n\t" /* save ebp */ "movl %%esp,%0\n\t" /* save esp */ "MOVL %2,%%esp\n\t " /* restore esp */ " movl %2,%%ebp\n\t " /* restore ebp */ "movl $1f,%1\n\t" /* save eip */ "pushl %3\n\t" "ret\n\t " /* restore eip */ : " =m " (PREV->THREAD.SP)," =m " (PREV->THREAD.IP) : "M" (NEXT->THREAD.SP), "M" (next-> THREAD.IP) ); } return;}
Four, the experimental summary, the teacher simplified code is not difficult to understand, but to write their own do not have this ability, so directly copied down their own understanding, the implementation of the process did not appear error. While simplifying the code, it is helpful to understand how the operating system works. The first is the kernel bootstrap, after all, all the programs are just memory code, the kernel is just that the designated privileges, NO. 0 process, start running, and build their own environment. Second, after all, the operating system is for the actual program services, the next is responsible for creating other process execution environment, resource allocation, the use of linked list mechanism to switch to the new process, and execution. Finally, the kernel is responsible for managing the state of the process, using the interrupt mechanism to implement process switching and control program execution. In summary, the operating system does the processing of the interrupt context and the process switching context.
This article is from the "Studypark" blog, make sure to keep this source http://swordautumn.blog.51cto.com/1485402/1619999
A brief analysis of the process scheduling of operating system