• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            統(tǒng)計

            • 隨筆 - 50
            • 文章 - 42
            • 評論 - 147
            • 引用 - 0

            留言簿(6)

            隨筆分類

            文章分類

            Link

            搜索

            •  

            積分與排名

            • 積分 - 164731
            • 排名 - 159

            最新評論

            閱讀排行榜

            評論排行榜

            深入分析任務切換與堆棧
            深入分析任務切換與堆棧 by Liu Wanli

            關鍵字:時間中斷、任務切換、堆棧、LINUX0.01

            引言:

            任務切換與堆棧的關系怎樣?很多朋友可能不知道她們之間有什么關系,還有一些朋友可能認為他們之間不會有太大的關系(文獻4)。而我認為:任務切換跟堆棧有著密切的關系!下面是我對它們之間關系進行的探討,這里的任務切換我指的是發(fā)生時間中斷時進行強制調度發(fā)生的任務切換,所以下面考慮堆棧時我是從中斷開始探討的。當然,我在進行這方面分析的時候,也愈感它們的復雜性,錯誤之處在所難免,望各位朋友多多指正。建議讀者水平:* * *


            一、時間中斷。

            假設一個進程在用戶空間執(zhí)行時(這時CPL=3),發(fā)生了時間中斷。這時的中斷處理過程為(文獻1:P438):
            1、根據中斷向量號找到中斷門描述符;
            2、從描述符中分解出選擇子、偏移量、屬性字段并進行相應的特權檢查;
            3、根據描述符類型轉入相應中斷處理程序中去執(zhí)行。
            好象太膚淺了一些?再看看(文獻1:P439圖10.20):
            1、選擇子為空?no繼續(xù);
            2、取得對應描述符;(描述符中DPL屬性應該為0,文獻3中斷向量初始化部分)
            3、存儲段描述符?yes繼續(xù);
            4、非一致代碼段且DPL<CPL且段存在?yes繼續(xù);根據假設CPL=3,DPL=0,所以到5!
            5、切換成內層堆棧!
            如何切換??因為一個進程有用戶空間堆棧和系統(tǒng)空間(也叫內核空間)堆棧,用戶空間堆棧在哪兒我不管,它應該是由該進程的任務狀態(tài)段TSS中SS2指定,SS0指定系統(tǒng)空間堆棧,它和該進程任務結構task_struct共占一頁空間(見文獻3:sched.c)。所以這里的切換成內層堆棧應該是將該進程的TSS中SS0的值賦給SS寄存器。
            6、使RPL=0;
            7、把描述符裝入CS;
            8、入口偏移越界?no繼續(xù);
            9、EFLAG、CS、EIP入棧;呵,開始棧的改變了喲!
            10、TF=0、NT=0、IF=0;這里考慮的是中斷門。
            11、轉入處理程序。

            別急,先看看現(xiàn)在的堆棧情況:

            | 外層EIP |
            | 外層CS |
            | EFLAG |
            | 外層ESP |
            | 外層SS |
            -----------
            這個棧在什么地方呢?這相當重要!這是在當初切換至內層堆棧時進行的,即已經到了當前進程的系統(tǒng)空間堆棧,也就是跟task_struct共占一頁的那個堆棧。而這里保存的就是該進程在用戶空間的堆棧和代碼信息,以便中斷完成后恢復進程執(zhí)行。

            二、中斷處理程序。
            這里指的是時間中斷。(文獻3:system_call.c: timer_interrupt:)

            timer_interrupt:
            1. push %ds
            2. push %es
            3. push %fs
            4. pushl %edx
            5. pushl %ecx
            6. pushl %ebx
            7. pushl %eax
            8. movl $0x10,%eax
            9. mov %ax,%ds
            10. mov %ax,%es
            11. movl $0x17,%eax
            12. mov %ax,%fs
            13. incl jiffies
            14. movb $0x20,%al
            15. outb %al,$0x20
            16. movl CS(%esp),%eax
            17. andl $3,%eax
            18. pushl %eax
            19. call do_timer
            20. andl $4,%esp
            21. jmp ret_from_sys_call

            1-7行為壓棧操作,這是我們所關心的!16-18即是將CPL(CPL=CS&3)壓棧,目的是用于do_tiemr(long cpl)函數(shù)。那么在執(zhí)行到do_timer里面時的堆棧怎么樣呢?看看:


            |返回地址 |
            -----------
            | CPL |
            | eax |
            | ebx |
            | ecx |
            | edx |
            | fs |
            | es |
            | ds |
            -----------
            | 外層EIP |
            | 外層CS |
            | EFLAG |
            | 外層ESP |
            | 外層SS |
            -----------

            上面的返回地址當然就是調用do_timer后的那條語句,即20行的andl $4,%esp語句。那么是不是do_timer函數(shù)執(zhí)行完就返回到這兒呢,也是,當然要復雜得多,因為在do_timer()函數(shù)中調用了schedule()并且發(fā)生了任務切換!哎,好麻煩,也不知道什么時候才能返回到這兒來呢,還是一步一步來看吧。

            三、do_timer()(文獻3:sched.c: do_timer())

            void do_timer(long cpl)
            {
            ...
            if ((--current->counter)>0) return;
            current->counter=0;
            if(!cpl)return;
            schedule();
            }

            省略號為無關緊要的兩條語句,進行進程的計時。如果時間片沒有用完(counter>0)或CPL為0,不發(fā)生調度直接返回,當然這里也不是就直接返回到以前執(zhí)行的進程空間,而是返回到do_timer()中,注意開始的返回地址,然后再通過iret指令從中斷處理返回到進程中去。當然,根據我們的假設,這兒CPL應該為3,因為是在用戶空間發(fā)生中斷的。我們要從最復雜的情況來討論這個問題。好了,就讓我們進入到中心點吧,請進schedule()。

            四、schedule()。 (文獻3:sched.c: schedule())

            void schedule(void )
            {
            int next;
            ...
            switch_to(next);
            }

            呵,這里我又省略了幾句代碼,它執(zhí)行的是調度算法,即從所有狀態(tài)為‘運行’的進程中找出下一個要執(zhí)行的進程,然后將編號賦給next。進行切換!

            switch_to()是一個宏,它在(文獻3: sched.h)中定義:

            #define switch_to(n) { \
            struct (long a,b;} __tmp; \
            __asm__("cmpl %%ecx,current \n\t" \
            "je 1f\n\t" \
            "xchgl %%ecx, current\n\t" \
            "movw %%dx, %1\n\t" \
            "ljmp *%0\n\t" \
            "cmpl %%ecx, %2\n\t" \
            "jne 1f\n\t" \
            "clts\n" \
            "1:" \
            ::"m" (*&__tmp.a), "m" (*&__tmp.b), \
            "m" (last_task_used_math),"d" _TSS(n), "c" ((long) task[n])); \
            }

            這是任務切換的關鍵代碼,原理是直接通過TSS來進行任務的切換(文獻1:P420)。那我就將這段關鍵代碼逐行解說一下吧。cmpl %%ecx, current,比較任務n是不是當前進程,如果是當然就不用切換了,直接結束schedule()。xchgl %%ecx,current,current指針指向任務n的任務結構,ecx寄存器保存當前進程的任務結構指針。movw %%dx, %1, 使__tmp.b=‘GDT中第n個任務的TSS選擇子’,注意_TSS(n)是求選擇子的宏!ljmp *%0,這句代碼就是真正的任務切換羅, AT&T語法的ljmp相當于INTEL的jmp far SECTION:OFFSET指令格式,它的絕對地址前加*號。這里引用(文獻1:P420)一段話:當段間轉移指令JMP所含指針的選擇子指示一個可用任務狀態(tài)段TSS描述符時,正常情況下就發(fā)生從當前任務到由該可用任務的切換。目標任務的入口點由目標任務TSS內的CS和EIP字段所規(guī)定的指針確定,這樣的JMP指令內的偏移被丟棄。再具體的任務切換你也許得翻翻(文獻1:P421),這里我只講有關堆棧的處理,那就是把寄存器現(xiàn)場保存到當前任務的TSS。把通用寄存器、段寄存器、EIP及EFLAGS的當前值保存到當前的TSS中。保存的EIP的值是返回地址,指向引起任務切換指令的下一條指令;恢復目標任務的寄存器現(xiàn)場,根據保存在TSS中的內容恢復各通用寄存器、段寄存器、EFLAG、EIP。好了,基本概念就引用這么多,那么,剛才提到的進程馬上要被切換出去了,它保存TSS中EIP是什么呢?顯然,根據剛才的分析應該是cmpl %%ecx, %2這條指令。這意味著什么呢?這就是說,如果下次這個任務要被切換成運行狀態(tài)時,它將從cmpl %%ecx, %2這條指令開始執(zhí)行!那么,由彼任務推到此任務,也就是說我們切換至任務next時,它也是從這條指令開始執(zhí)行的!于是我們進入到任務next的堆??臻g,并開始執(zhí)行,但由于任務next和當前的任務有著相同的堆棧路徑(這和LINUX中的內核控制路徑是不是一回事呢?),所以我們還是引用當前的堆棧來繼續(xù)分析。
            哦,有點糊涂了,好象是。休息一下,再參考一下(文獻2:上冊P373)。專家也是這樣說的;)
            要不,我們這么理解,剛才被中斷的進程發(fā)生了強制調度,且也發(fā)生了任務切換,只不過是切換到它自己,實際上不是喲。好吧,JMP成功,開始執(zhí)行。

            五、轉折點,從schedule()返回。

            cmpl %%ecx, %2;jne 1f; clts;1: 這幾句是與協(xié)處理器有關,還有TS標志,我們就直接到1:吧,開始從schedule()返回,注意switch_to()是宏,它在schedule()末端。返回到哪兒去了呢?跟蹤一下,看看上面的堆棧示意圖,返回地址就是調用do_timer后的那條語句,
            addl $4, %esp
            jmp ret_from_sys_call
            這兒esp加4就是把堆棧中的CPL去掉,因為我們不用了,跳轉到ret_from_sys_call。哦,剩下的處理與系統(tǒng)調用返回共用代碼。

            六、ret_from_sys_call (文獻3,kernel/system_call.s)
            先看看我們的焦點,堆棧怎么樣了呢?

            | eax |
            | ebx |
            | ecx |
            | edx |
            | fs |
            | es |
            | ds |
            -----------
            | 外層EIP |
            | 外層CS |
            | EFLAG |
            | 外層ESP |
            | 外層SS |
            -----------

            ret_from_sys_call:
            movel current, %eax
            cmpl task, %eax
            je 3f
            movl CS(%esp), %ebx
            testl $3, %ebx
            je 3f
            cmpw $0x17, OLDSS(%esp)
            jne 3f
            2:
            ....
            3:
            popl %eax
            popl %ebx
            popl %ecx
            popl %edx
            pop %fs
            pop %es
            pop %ds
            iret

            2標號處我省略了一些有關信號及其它一些處理。讓我們分析一下,如果當前任務是0號進程,或是任務先前的CPL為3(即用戶態(tài)),或是任務先前的堆棧段為LDT中指定的堆棧,JMP到3標號處。由先前的假設可知,此任務的CPL為3,那就跳吧。把eax, ebc, ecx, edx, fs, es, ds寄存器從堆棧中恢復出來。
            現(xiàn)在堆棧如下:

            | 外層EIP |
            | 外層CS |
            | EFLAG |
            | 外層ESP |
            | 外層SS |
            -----------
            記得我們還有最后一條語句喲,iret。這條指令大家想必已經很熟悉了,它恢復EIP、CS、EFLAG、ESP、SS。記得不,這是不是已經恢復到了最初的時間中斷時進程被中斷的那一刻?恭喜!你終于可以繼續(xù)做你需要做的事情了!小心,還有下一個時間中斷,哦,你不怕?因為它不會影響你的連貫性。

            結束語:
            我們走過了一段艱辛的歷程,但我們走的是一段近乎直線的路徑,并沒有分析到其它各個方面的情況。不過我相信,通過這段路程,會讓我們對于任務切換機制有一個更深入的認識。希望對大家有所幫助,這也是我寫出來與大家分享的緣故。

            參考文獻:
            1、80X86匯編語言程序設計教程 楊季文編著 清華大學出版社
            2、LINUX內核源代碼情景分析 毛德操 胡希明著 浙江大學出版社
            3、LINUX0.01源代碼
            4、http://www.linuxforum.net/ LINUX內核分析-->精華篇-->進程調度

            posted on 2011-01-29 22:50 pear_li 閱讀(947) 評論(0)  編輯 收藏 引用 所屬分類: Linux/Unix

            久久中文字幕人妻熟av女| 久久综合久久综合久久综合| 四虎久久影院| 久久综合亚洲色一区二区三区| 2019久久久高清456| 久久精品国产男包| 99久久精品影院老鸭窝| 亚洲AⅤ优女AV综合久久久| 精品久久久久久无码中文野结衣| 99久久精品国产一区二区三区| 精品久久国产一区二区三区香蕉| 久久国产成人精品国产成人亚洲| 久久综合色老色| 久久99热狠狠色精品一区| 久久久久国产一区二区| 色狠狠久久综合网| 久久香蕉综合色一综合色88| 国产精品久久久久a影院| 日本免费一区二区久久人人澡| 久久99热这里只有精品国产| 精品久久久久久久| 久久综合色老色| 久久亚洲电影| AA级片免费看视频久久| 久久国产欧美日韩精品| 久久只有这里有精品4| 久久精品国产只有精品66| 久久精品草草草| 91久久婷婷国产综合精品青草| 99精品国产免费久久久久久下载| 国产精品免费久久久久电影网| 无码国内精品久久人妻蜜桃 | 久久久久久噜噜精品免费直播 | 久久99精品久久久久子伦| 一97日本道伊人久久综合影院| 亚洲国产精品久久久久网站| 伊人久久大香线焦AV综合影院| 久久久这里有精品中文字幕| 久久最近最新中文字幕大全| 99久久精品毛片免费播放| 久久国产亚洲精品无码|