linux内核计算代码时间,完成一个简单的时间片轮转多道程序内核代码
《Linux 內(nèi)核分析》實(shí)驗(yàn)二:How Does a Operating System Work?
1.函數(shù)調(diào)用堆棧和中斷
在上一節(jié)實(shí)驗(yàn)中我們已經(jīng)明白了,存儲(chǔ)程序計(jì)算機(jī)的運(yùn)行原理,就是通過(guò)不斷的取指執(zhí)行存儲(chǔ)在堆棧上 CPU 指令,而這些二進(jìn)制指令一一對(duì)應(yīng)于匯編函數(shù)指令。因此,對(duì)于操作系統(tǒng),我們引入相應(yīng)的函數(shù)調(diào)用堆棧機(jī)制。
我們都知道 CPU 的運(yùn)算速度是極其快的,很多時(shí)候例如輸入輸出過(guò)程,如果沒(méi)有系統(tǒng)中斷,CPU 就只能等待外設(shè)輸入輸出完成后再工作,這樣會(huì)造成了極大的資源浪費(fèi)。因此,現(xiàn)代操作系統(tǒng)都引入了中斷機(jī)制,CPU 會(huì)根據(jù)當(dāng)前進(jìn)程的執(zhí)行情況,交替執(zhí)行多個(gè)程序,提高運(yùn)行效率。
在本次實(shí)驗(yàn)基于一個(gè)小小的時(shí)間片輪轉(zhuǎn)多道程序 mykernel,分析操作系統(tǒng)的函數(shù)調(diào)用堆棧和中斷的實(shí)現(xiàn)過(guò)程。
2.內(nèi)核代碼分析
mykernel 程序有三個(gè)源文件,分別是 mypcb.h,mymain.c 和 myinterrupt.c,mypcb.h 頭文件,定義了一些結(jié)構(gòu)和函數(shù)。mymain.c 定義了多個(gè)進(jìn)程啟動(dòng)和運(yùn)行的函數(shù)。myinerrupt.c 定義了時(shí)間片和中斷處理的函數(shù)。
2.1>我們先來(lái)看看 mypcb.h 頭文件:
#define MAX_TASK_NUM 10 // max num of task in system
#define KERNEL_STACK_SIZE 1024*8
#define PRIORITY_MAX 30 //priority range from 0 to 30
首先,定義了最大任務(wù)數(shù)量、內(nèi)核棧大小和任務(wù)的優(yōu)先級(jí)序列。
/* CPU-specific state of this task */
struct Thread {
unsigned long ip;//point to cpu run address
unsigned long sp;//point to the thread stack's top address
//todo add other attrubte of system thread
};
然后,定義 Thread 結(jié)構(gòu)體,ip 和 sp 分別表示執(zhí)行指針和棧指針,用于執(zhí)行和保護(hù)現(xiàn)場(chǎng)。
//PCB Struct
typedef struct PCB{
int pid; // pcb id
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
char stack[KERNEL_STACK_SIZE];// each pcb stack size is 1024*8
/* CPU-specific state of this task */
struct Thread thread;
unsigned long task_entry;//the task execute entry memory address
struct PCB *next;//pcb is a circular linked list
unsigned long priority;// task priority
//todo add other attrubte of process control block
}tPCB;
再然后,定義了進(jìn)程管理結(jié)構(gòu)體,包括:1、pid 進(jìn)程號(hào);2、state 進(jìn)程狀態(tài);3、??臻g;4、thread 變量;5、任務(wù)入口地址;6、下一個(gè) PCB 指針地址;7、任務(wù)的優(yōu)先級(jí)。
//void my_schedule(int pid);
void my_schedule(void);
最后,定義了 my_schedule 任務(wù)調(diào)度函數(shù)。
2.2>再來(lái)分析 mymain.c 文件:
tPCB task[MAX_TASK_NUM];
tPCB * my_current_task = NULL;
volatile int my_need_sched = 0;
首先,定義三個(gè)全局變量,一個(gè)是 PCB 數(shù)組,一個(gè)是指向當(dāng)前任務(wù)的指針,my_need_sched 被來(lái)判斷是否調(diào)用中斷處理函數(shù)。
void __init my_start_kernel(void)
{
int pid = 0;
/* Initialize process 0*/
task[pid].pid = pid;
task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
// set task 0 execute entry address to my_process
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(pid=1;pid
{
memcpy(&task[pid],&task[0],sizeof(tPCB));
task[pid].pid = pid;
task[pid].state = -1;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].priority=get_rand(PRIORITY_MAX);//each time all tasks get a random priority
}
task[MAX_TASK_NUM-1].next=&task[0];
printk(KERN_NOTICE "\n\n\n\n\n\n system begin :>>>process 0 running!!!<<
/* 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*/
);
}
my_start_kernel 函數(shù)非常重要,首先初始化任務(wù) 0,設(shè)置為可運(yùn)行狀態(tài),設(shè)置任務(wù)運(yùn)行的 ip,設(shè)置任務(wù)運(yùn)行 sp 空間,設(shè)置下一個(gè) PCB 指向自己。然后,設(shè)置余下的任務(wù)并隨機(jī)分配優(yōu)先權(quán)(我們?cè)O(shè)置最大任務(wù)數(shù)為 10)。最后,通過(guò)內(nèi)聯(lián)匯編把 sp 賦給 esp,ebp 進(jìn)棧,把 ip 保存到棧中,這樣如果任務(wù)切換的話就可以保證下一個(gè)進(jìn)程結(jié)束后回到現(xiàn)場(chǎng)繼續(xù)執(zhí)行。
void my_process(void)
{
int i = 0;
while(1)
{
i++;
if(i%10000000 == 0)
{
if(my_need_sched == 1)
{
my_need_sched = 0;
sand_priority();
my_schedule();
}
}
}
}//end of my_process
my_process 函數(shù)很簡(jiǎn)單,就是建立一個(gè)循環(huán)不斷運(yùn)行進(jìn)程,輸出進(jìn)程信息,同時(shí),如果 my_need_sched = 1,就開(kāi)始中斷并切換進(jìn)程。
2.3>最后分析 myinterrupt.c 文件:
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
// make sure need schedule after system circle 2000 times.
if(time_count%2000 == 0 && my_need_sched != 1)
{
my_need_sched = 1;
//time_count=0;
}
time_count++ ;
#endif
return;
}
my_timer_handler 函數(shù)也非常簡(jiǎn)單,通過(guò)判斷 time_count 時(shí)間片變量的閾值,更改 my_need_sched 值實(shí)現(xiàn)了中斷調(diào)用。
void my_schedule(void)
{
tPCB * next;
tPCB * prev;
// if there no task running or only a task ,it shouldn't need schedule
if(my_current_task == NULL || my_current_task->next == NULL)
{
printk(KERN_NOTICE " time out!!!,but no more than 2 task,need not schedule\n");
return;
}
/* schedule */
next = get_next();
prev = my_current_task;
printk(KERN_NOTICE " the next task is %d priority is %u\n",next->pid,next->priority);
if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
{//save current scene
/* switch to next process */
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)
);
my_current_task = next;//switch to the next task
printk(KERN_NOTICE "switch from %d process to %d process\n >>>process %d running!!!<<pid,next->pid,next->pid);
}
else
{
next->state = 0;
my_current_task = next;
printk(KERN_NOTICE "switch from %d process to %d process\n >>>process %d running!!!<<pid,next->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;
}//end of my_schedule
my_schedule 函數(shù)是這個(gè)內(nèi)核的重點(diǎn),首先初始化 next 和 prev 兩個(gè) PCB 結(jié)構(gòu)體,然后判斷如果任務(wù) state 狀態(tài)是可運(yùn)行時(shí),說(shuō)明這個(gè)任務(wù)正在執(zhí)行,保存 ebp 和 esp,并切換到下一個(gè)任務(wù) ip 執(zhí)行;如果任務(wù) state 狀態(tài)是不可運(yùn)行時(shí),說(shuō)明這個(gè)任務(wù)沒(méi)執(zhí)行過(guò),保存當(dāng)前任務(wù),開(kāi)始執(zhí)行新任務(wù)。
3.總結(jié):
通過(guò)分析實(shí)驗(yàn)代碼,我們學(xué)習(xí)了一個(gè)簡(jiǎn)單的時(shí)間片輪轉(zhuǎn)多道操作系統(tǒng)內(nèi)核,了解了操作系統(tǒng)的中斷上下文和進(jìn)程上下文切換。每個(gè)任務(wù)被分配一定的時(shí)間片執(zhí)行,如果在時(shí)間片結(jié)束后任務(wù)仍在執(zhí)行,CPU 將會(huì)剝奪它的執(zhí)行權(quán)并分配給其他任務(wù);如果任務(wù)在時(shí)間片結(jié)束前完成,CPU 則會(huì)立即進(jìn)行切換,調(diào)度程序就是在維護(hù)一個(gè)就緒進(jìn)程隊(duì)列,當(dāng)進(jìn)程用完屬于它的時(shí)間片后,在隊(duì)列中就會(huì)按照優(yōu)先級(jí)重新排序,這是最簡(jiǎn)單、最公平也最高效的一種方式。
總結(jié)
以上是生活随笔為你收集整理的linux内核计算代码时间,完成一个简单的时间片轮转多道程序内核代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: gui怎么读?
- 下一篇: linux 系统时间是在哪里记录的,Li