Wusi
Deep understanding of the function call stack
A stack is a space that the C language program must run with a record call path and parameters
The role of the stack
- Function call Framework
- Passing parameters
- Save return address
- Provides local variable space
Stack-related registers
- ESP, stack pointer, pointing to the top of the stack
- EBP, base point pointer, to the bottom of the stack, used in C to record the current function call base.
Other key registers
CS (Code Snippet Register): EIP: Always point to the next instruction address
- Sequential execution: Always points to the next instruction in consecutive addresses
- Jump/Branch: When executing such an instruction, the value of CS:EIP will be modified according to the program needs
Parameter passing and local variables
- Establish framework (equivalent to call command)
- Push%EBP
MOVL%ESP,%EBP
- Frame removal (equivalent to RET Directive)
- MOVL%ebp,%esp
Pop%EBP
When the function returns, the frame must be removed, and the build and dismantle are one by one corresponding.
Before the framework of a child function is established, the value of the local variable is saved in the caller's stack frame, so the value of the variable can be put into the stack before the child function framework is established.
The return value of the function is passed through the EAX register
with Linux Kernel part source code simulating storage program computer working model and clock interrupt
Mykernel Experimental Ideas
The interrupt implements multi-channel program design, and then switches back and forth between the execution streams of the program, the CPU pushes the program's EBP into the stack, and points to an interrupt handler, which is implemented by the CPU and kernel code together to save the site and restore the scene.
C How to embed assembly code in code
__asm__ (
Assembly Statement Template:
Input section:
Output section:
Destruction Description section:);
Experiment -- in the Mykernel construct a simple operating system kernel based on the
Experimental process
This experiment is to understand how the operating system works by analyzing a simple time-slice rotation multi-channel program kernel source code.
Run this kernel first, and you can see a context that provides a code to run in the kernel.
Then CD Mykernel find the MYMAIN.C and myinterrupt.c two source code, enter Https://github.com/mengning/mykernel/blob/master can find several important source code that this experiment needs. The above two code is then modified to the code found in the site, in addition to adding mypcb.h.
Return to run again and see 0, 1, 2, and 3 processes switching to each other.
Source Code Analysis
Mypcb.h
The purpose of this code is to define a Process control block (PCB).
/*
* Linux/mykernel/mypcb.h
*
* Kernel Internal PCB types
*
* Copyright (C) mengning
*
*/
#define MAX_TASK_NUM 4
#define Kernel_stack_size 1024*8
/* Cpu-specific State of this task */
struct Thread {
unsigned long ip;//for the preservation of EIP
unsigned long sp;//for ESP saving
};
typedef struct PCB{//is used to represent a process that defines a process-management-related data structure
int pid;
volatile long state; /*-1 unrunnable, 0 runnable, >0 stopped */
Char 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);//called My_schedule, which represents the scheduler
Mymain.c
/*
* LINUX/MYKERNEL/MYMAIN.C
*
* Kernel Internal My_start_kernel
*
* Copyright (C) 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;//defines a flag that is used to determine whether a dispatch is required
void my_process (void);
void __init My_start_kernel (void)
{
int PID = 0;//initialization of a process 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;
Defines the entry for process 0 as My_process
TASK[PID].THREAD.SP = (unsigned long) &task[pid].stack[KERNEL_STACK_SIZE-1];
Task[pid].next = &task[pid];
Since there is only process 0 in the system at the beginning, this line of code represents the PID of next or point to itself
/*fork More Process */
Create additional processes that can directly copy code for process No. 0 when initializing these processes
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];
Each process has its own stack, putting a new process created into the tail of the process list, which completes the creation
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\n\t"/* Set TASK[PID].THREAD.SP to ESP */
"PUSHL%1\n\t"/* Push 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*/
);
}
/*%0 indicates that the parameter thread.ip,%1 represents the parameter thread.sp.
MOVL%1,%%esp means to put the parameters thread.sp in the ESP;
Next push% 1, also because the current stack is empty, esp=ebp, so equivalent to the push ebp;
Then push Thread.ip;ret is equivalent to pop thread.ip; last pop EBP */
void my_process (void)//defines the work of all processes, if statements represent loops 10 million times before the opportunity to determine whether a dispatch is required.
{
int i = 0;
while (1)
{
i++;
if (i%10000000 = = 0)
{
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);
}
}
}
Myinterrupt.c
/*
* LINUX/MYKERNEL/MYINTERRUPT.C
*
* Kernel Internal My_timer_handler
*
* Copyright (C) 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 the current running process,
* So it with kernel stack of current running process
*/
void My_timer_handler (void)
/* for setting the size of the time slice, set the dispatch flag when the time slice runs out.
When the clock interrupted 1000 times, and My_need_sched!=1, the my_need_sched was assigned to 1.
My_schedule is executed when the process discovers my_need_sched=1. */
{
#if 1
if (time_count%1000 = = 0 && my_need_sched! = 1)
{
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//task is empty, that is, returns when an error occurs
|| My_current_task->next = = NULL)
{
Return
}
PRINTK (Kern_notice ">>>my_schedule<<<\n");
/* Schedule */
Next = my_current_task->next;//assigns the next process of the current process to next
Prev = my_current_task;//The current process is prev
if (next->state = = 0)/*-1 unrunnable, 0 runnable, >0 stopped * *
{
/* Switch to Next process */
/* If the status of the next process is executing, use the method represented by the code in the IF statement to toggle the process */
ASM volatile (
"PUSHL%%ebp\n\t"/* Save EBP Saves the ebp*/of the current process
"Movl%%esp,%0\n\t"/* Save ESP Saves the esp*/of the current process
"Movl%2,%%esp\n\t"/* Restore ESP places the SP of the next process in ESP */
"Movl $1f,%1\n\t"/* Save Eip saved 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)
);
My_current_task = Next;
PRINTK (kern_notice ">>>switch%d to%d<<<\n", prev->pid,next->pid);
}
Else
/* Unlike the previous code, if the next process is a new process, this piece of code in else is used.
This process is first set to the runtime state, which is used as the currently executing process. */
{
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"/* 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
}
Linux Kernel section II