日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内核分析:完成一个简单的时间片轮转多道程序内核代码

發布時間:2025/7/14 linux 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内核分析:完成一个简单的时间片轮转多道程序内核代码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

PS.賀邦 ? 原創作品轉載請注明出處 ?《Linux內核分析》MOOC課程 ? ?http://mooc.study.163.com/course/USTC-1000029000?

1.mykernel實驗指導(操作系統是如何工作的)

使用實驗樓虛擬機打開shell輸入下列代碼

  • 1?cd?LinuxKernel/linux-3.9.4
  • 2?qemu?-kernel?arch/x86/boot/bzImage

可以看到初始的內核運行情況如下:

?

內核不停的執行my_start_kernel(),每隔一段時間被my_timer_handler()中斷,然后執行一條打印語句:printk(KERN_NOTICE “\n>>>>>>>>>>>>>>>>>my_timer_handler here<<<<<<<<<<<<<<<<<<\n\n”);

后,又回到my_start_kernel()繼續執行。

打開mymain.c文件,可以看到其中只有如下這一個函數。它作為內核啟動的起始位置,從這個函數開始運行,并無限循環。

打開myinterrupt.c文件,里面也只有一個my_timer_handler(),它被Linux內核周期性調用,從而產生了一個周期性的中斷機制。

2.修改內核代碼,使之成為一個簡單的時間片輪轉多道程序內核,然后重新編譯運行。

從https://github.com/mengning/mykernel上下載mypcb.h;mymain.c;myinterrupt.c;?
然后替換位于home/shiyanlou/LinuxKernel/linux-3.9.4/mykernel/中的mymain.c;myinterrupt.c;?
將mypcb.h也放在這里。?

然后執行make,重新編譯內核。效果如下:?

然后再次輸入:

  • qemu -kernel arch/x86/boot/bzImage 命令

啟動內核。 可以發現跳轉和時間片都有了明顯的改變。

實驗結果與預期結果相符,實驗成功。

3.重點代碼理解

mymain.c

void__init?my_start_kernel(void)?{

?Int?pid?=?0;?

?Int?i;?

/*?Initialize?process?0*/

task[pid].pid?=?pid;//task[0].pid=0;

ask[pid].state?=?0;/*?-1?unrunnable,?0?runnable,?>0?stopped?*/

task[pid].task_entry?=?task[pid].thread.ip?=?(unsignedlong)my_process;//0號進程的入口地址為my_process();

task[pid].thread.sp?=?(unsignedlong)&task[pid].stack[KERNEL_STACK_SIZE-1];//0號進程的棧頂為stack[]數組的最后一個元素

task[pid].next?=?&task[pid];//next指針指向自己/*fork?more?process?*/

for(i=1;i<MAX_TASK_NUM;i++)//根據0號進程,復制出幾個只是編號不同的進程

{?

memcpy(&task[i],&task[0],sizeof(tPCB));//void?*memcpy(void?*dest,?const?void?*src,?size_t?n);從源src所指的內存地址的起始位置開始拷貝n個字節到目標dest所指的內存地址的起始位置中。

task[i].pid?=?i;?task[i].state?=?-1;//這些進程的狀態都設置為未運行。

task[i].thread.sp?=?(unsignedlong)&task[i].stack[KERNEL_STACK_SIZE-1];?task[i].next?=?task[i-1].next;//新創建的進程的next指向0號進程的首地址

task[i-1].next?=?&task[i];//前一個進程的next指向最新創建的進程的首地址,從而成為一個循環鏈表。

?

mypcb.h

#define?MAX_TASK_NUM?4?//最大進程數,這里設置為了4個。

#define?KERNEL_STACK_SIZE?1024*8?//每個進程的內核棧的大小。

/*?CPU-specific?state?of?this?task?*/

structThread?{?unsignedlongip;//用于保存進程的eip

unsignedlongsp;//用戶保存進程的esp};?

typedefstructPCB{?intpid;//進程的id

volatilelongstate;?/*?進程的狀態:-1?unrunnable,?0?runnable,?>0?stopped?*/

charstack[KERNEL_STACK_SIZE];//進程的棧,只有一個核心棧。/*?CPU-specific?state?of?this?task?*/

structThread?thread;//每個進程只有一個線程。

unsignedlongtask_entry;//進程的起始入口地址。

myinterrupt.c

if(next->state?==?0)/*如果下一個將要運行的進程已經處于運行狀態?-1?unrunnable,?0?runnable,?>0?stopped?*/

????{

????????/*?switch?to?next?process?*/

????????asm?volatile(???

????????????"pushl?%%ebp\n\t"???????/*?保存當前進程的ebp到自己的棧中。????save?ebp?*/

????????????"movl?%%esp,%0\n\t"?????/*?保存當前進程的esp到自己的棧中。????save?esp?*/

????????????"movl?%2,%%esp\n\t"?????/*?next->thread.sp中彈出下一個進程的esp。與第二句相對應。???restore??esp?*/

????????????"movl?$1f,%1\n\t"???????/*?將下一個進程的eip設置為1f$1f就是指標號1:的代碼在內存中存儲的地址??save?eip?*/???

????????????"pushl?%3\n\t"??????????/*?next->thread.ip壓入當前進程的棧中。*/

????????????"ret\n\t"???????????????/*?從當前進程的棧中彈出剛剛壓入的next->thread.ip。完成進程切換。??restore??eip?*/

????????????"1:\t"??????????????????/*?$1f指向的位置。next?process?start?here?*/

????????????"popl?%%ebp\n\t"????????/*?切換到的進程把ebp從棧中彈出至ebp寄存器。與第一句相對應。*/

????????????:?"=m"?(prev->thread.sp),"=m"?(prev->thread.ip)

????????????:?"m"?(next->thread.sp),"m"?(next->thread.ip)

????????);?

????????my_current_task?=?next;?//當前進程切換為next

????????printk(KERN_NOTICE?">>>switch?%d?to?%d<<<\n",prev->pid,next->pid);?//打印切換信息?????

????}

????else//如果下一個將要運行的進程還從未運行過。

????{

????????next->state?=?0;//將其設置為運行狀態。

????????my_current_task?=?next;當前進程切換為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"???????/*?將要被切換出去的進程的ip設置為$1f。這樣等一下它被切換回來時(一定是運行狀態)肯定會進入if判斷分支,可以從if中的標號1處繼續執行。??save?eip?*/????

????????????"pushl?%3\n\t"??????????/*?next->thread.ip(因為它還沒有被運行過,所以next->thread.ip現在仍處于初始狀態,即指向my_process(),壓入將要被切換出去的進程的堆棧。*/

????????????"ret\n\t"???????????????/*?將剛剛壓入的next->thread.ip出棧至eip,完成進程切換。???restore??eip?*/

?

4.分析進程的啟動和進程的切換機制。

?

首先,內核啟動__init my_start_kernel(void),創建了4個進程,分別是0,1,2,3號,設置0號為運行態,其它3個進程為未運行態。

0號進程的入口都被初始化為 task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;即指向my_process()。?
0號進程的棧頂被初始化為 task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];?
之后的進程也都是根據0號進程復制得到,所以它們的起始入口也是my_process(),初始棧頂也是指向了自己的stack[KERNEL_STACK_SIZE-1];

my_current_task = &task[pid];將當前進程設置為0號進程。然后從0號進程開始運行。?
“movl %1,%%esp\n\t”?
將0號進程的棧頂放入esp寄存器。?
“pushl %1\n\t” /* push ebp */?
當前esp指向了stack數組的末尾,這里也是棧頂,因為棧是空的,所以esp==ebp。?
“pushl %0\n\t” /* push task[pid].thread.ip */?
“ret\n\t” /* pop task[pid].thread.ip to eip */?
切換到0號進程的入口地址開始執行。?
“popl %%ebp\n\t”?
這句多余,在ret之后,不會被執行。?
:?
: “c” (task[pid].thread.ip),”d” (task[pid].thread.sp)

之后0號進程不斷執行my_process()。一段時間后,my_timer_handler()被內核調用,觸發中斷, my_need_sched = 1;將全局變量my_need_sched 設置為了1。

此后,當0號進程執行到了if(my_need_sched == 1)時就會進入這個if條件分支中,執行 my_schedule();執行進程調度。

0號進程的next指針指向的是1號進程,所以在my_schedule()中的next指針指向了1號進程,prev指針指向了0號進程。?
因為1號進程當前還未被運行過,所以會執行else條件分支:next->state = 0;//將1號進程設置為運行狀態。?
my_current_task = next;//當前進程切換為1號進程printk(KERN_NOTICE “>>>switch %d to %d<<<\n”,prev->pid,next->pid);//打印switch 0 to 1?
“pushl %%ebp\n\t” /* save ebp */?
“movl %%esp,%0\n\t” /* save esp */?
將0號進程的ebp和esp都保存到0號進程的棧上。?
“movl %2,%%esp\n\t” /* restore esp */?
“movl %2,%%ebp\n\t” /* restore ebp */?
將1號進程的存在1號進程結構體中next->thread.sp保存的esp的值存入esp寄存器和ebp寄存器,因為1號進程還未被運行過,所以esp仍指向了1號棧的stack[KERNEL_STACK_SIZE-1]。?
“movl $1f, %1\n\t” 將0號進程的eip設置為if。?
“pushl %3\n\t”?
“ret\n\t”?
將1號進程的eip加入0號進程的棧中,然后通過ret指令,將這個eip從0號進程的棧中彈出,存入eip寄存器,完成從0號進程到1號進程的切換。此后類似。

5.對“操作系統是如何工作的”理解。

操作系統的內核有一個起始位置,從這個起始位置開始執行。開始工作時,CPU分配給第一個進程,開始執行第一個進程,然后通過一定的調度算法,比如時間片輪轉,在一個時間片后,發生中斷,

第一個進程被阻塞,在完成保存現場后將CPU分配給下一個進程,執行下一個進程。這樣,操作系統就完成了基本的進程調度的功能。

轉載于:https://www.cnblogs.com/L1nke/p/5247152.html

總結

以上是生活随笔為你收集整理的Linux内核分析:完成一个简单的时间片轮转多道程序内核代码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。