亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 10620 | 回復(fù): 6
打印 上一主題 下一主題

linux2.6.29 swtich_to 詳細分析(一) [復(fù)制鏈接]

論壇徽章:
2
2015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:57:09
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2009-05-05 08:52 |只看該作者 |倒序瀏覽
linux內(nèi)核進程切換最重要的一個部分就是宏定義switch_to,下面從幾個方面來詳細講解一下:
(1)內(nèi)嵌匯編
(2)memory 破壞描述符(編譯器優(yōu)化)
(3)進程切換的標志是什么?
(4)堆棧切換的標志是什么?
(5)為什么switch_to 提供了三個參數(shù)?
(6)匯編參數(shù)的傳遞?

帶著這幾個問題,先來大體瀏覽一下代碼

#define switch_to(prev, next, last)     \
do {         \
/*        \
  * Context-switching clobbers(徹底擊敗) all registers, so we clobber \
  * them explicitly, via unused output variables.  \
  * (EAX and EBP is not listed because EBP is saved/restored \
  * explicitly for wchan access and EAX is the return value of \
  * __switch_to())      \
  */        \
unsigned long ebx, ecx, edx, esi, edi;    \
         \
asm volatile("pushfl\n\t"  /* save    flags */ \
       "pushl %%ebp\n\t"  /* save    EBP   */ \
       "movl %%esp,%[prev_sp]\n\t" /* save    ESP   */ \
       "movl %[next_sp],%%esp\n\t" /* restore ESP   */ \
       "movl $1f,%[prev_ip]\n\t" /* save    EIP   */ \
       "pushl %[next_ip]\n\t" /* restore EIP   */ \
       "jmp __switch_to\n" /* regparm call  */ \
       "1:\t"      \
       "popl %%ebp\n\t"  /* restore EBP   */ \
       "popfl\n"   /* restore flags */ \
         \
       /* output parameters */                       \
       : [prev_sp] "=m" (prev->thread.sp),  \
       /*m表示把變量放入內(nèi)存,即把[prev_sp]存儲的變量放入內(nèi)存,最后再寫入prev->thread.sp*/\
         [prev_ip] "=m" (prev->thread.ip),  \
         "=a" (last),                                           \
         /*=表示輸出,a表示把變量last放入ax,eax = last*/         \
         \
         /* clobbered output registers: */  \
         "=b" (ebx), "=c" (ecx), "=d" (edx),  \
         /*b 變量放入ebx,c表示放入ecx,d放入edx,S放入si,D放入edi*/\
         "=S" (esi), "=D" (edi)    \
                \
         /* input parameters: */    \
       : [next_sp]  "m" (next->thread.sp),  \
       /*next->thread.sp 放入內(nèi)存中的[next_sp]*/\
         [next_ip]  "m" (next->thread.ip),  \
                \
         /* regparm parameters for __switch_to(): */ \
         [prev]     "a" (prev),    \
         /*eax = prev  edx = next*/\
         [next]     "d" (next)    \
         \
       : /* reloaded segment registers */   \
   "memory");     \
} while (0)

以上代碼,主要是內(nèi)嵌匯編,這里先簡單介紹一下:
     1 內(nèi)嵌匯編語法
__asm__ __violate__ ("movl %1,%0" : "=r" (result) : "m" (input));

__asm__ __violate__("指令模板" : 輸出部 : 輸入部);

“movl %1,%0”是指令模板;“%0”和“%1”代表指令的操作數(shù),稱為占位符,內(nèi)嵌匯
編靠它們將C 語言表達式與指令操作數(shù)相對應(yīng)。指令模板后面用小括號括起來的是C語言表
達式,本例中只有兩個:“result”和“input”,他們按照出現(xiàn)的順序分別與指令操作數(shù)“%0”,
“%1”對應(yīng);注意對應(yīng)順序:第一個C 表達式對應(yīng)“%0”;第二個表達式對應(yīng)“%1”,依次類
推,操作數(shù)至多有10 個,分別用“%0”,“%1”….“%9”表示
。在每個操作數(shù)前面有一個
用引號括起來的字符串,字符串的內(nèi)容是對該操作數(shù)的限制或者說要求。“result”前面的
限制字符串是“=r”,其中“=”表示“result”是輸出操作數(shù),“r”表示需要將“result”
與某個通用寄存器相關(guān)聯(lián),先將操作數(shù)的值讀入寄存器,然后在指令中使用相應(yīng)寄存器,而
不是“result”本身,當然指令執(zhí)行完后需要將寄存器中的值存入變量“result”,從表面
上看好像是指令直接對“result”進行操作,實際上GCC做了隱式處理,這樣我們可以少寫
一些指令!癷nput”前面的“r”表示該表達式需要先放入某個寄存器,然后在指令中使用
該寄存器參加運算。
(2)memory 破壞描述符(編譯器優(yōu)化)
內(nèi)存訪問速度遠不及CPU處理速度,為提高機器整體性能,在硬件上引入硬件高速緩存
Cache,加速對內(nèi)存的訪問。另外在現(xiàn)代CPU中指令的執(zhí)行并不一定嚴格按照順序執(zhí)行,沒
有相關(guān)性的指令可以亂序執(zhí)行,以充分利用CPU的指令流水線,提高執(zhí)行速度。以上是硬件
級別的優(yōu)化。再看軟件一級的優(yōu)化:一種是在編寫代碼時由程序員優(yōu)化,另一種是由編譯器
進行優(yōu)化。編譯器優(yōu)化常用的方法有:將內(nèi)存變量緩存到寄存器;調(diào)整指令順序充分利用
CPU指令流水線,常見的是重新排序讀寫指令。
對常規(guī)內(nèi)存進行優(yōu)化的時候,這些優(yōu)化是透明的,而且效率很好。由編譯器優(yōu)化或者硬
件重新排序引起的問題的解決辦法是在從硬件(或者其他處理器)的角度看必須以特定順序
執(zhí)行的操作之間設(shè)置內(nèi)存屏障(memory barrier),linux 提供了一個宏解決編譯器的執(zhí)行
順序問題。
void Barrier(void)
這個函數(shù)通知編譯器插入一個內(nèi)存屏障,但對硬件無效,編譯后的代碼會把當前CPU寄存器
中的所有修改過的數(shù)值存入內(nèi)存,需要這些數(shù)據(jù)的時候再重新從內(nèi)存中讀出。
Memory描述符告知GCC:
l  1)不要將該段內(nèi)嵌匯編指令與前面的指令重新排序;也就是在執(zhí)行內(nèi)嵌匯編代碼
之前,它前面的指令都執(zhí)行完畢
l  2)不要將變量緩存到寄存器,因為這段代碼可能會用到內(nèi)存變量,而這些內(nèi)存變
量會以不可預(yù)知的方式發(fā)生改變,因此GCC插入必要的代碼先將緩存到寄存器的變
量值寫回內(nèi)存,如果后面又訪問這些變量,需要重新訪問內(nèi)存。
如果匯編指令修改了內(nèi)存,但是GCC 本身卻察覺不到,因為在輸出部分沒有描述,此時
就需要在修改描述部分增加“memory”,告訴GCC 內(nèi)存已經(jīng)被修改,GCC 得知這個信息后,
就會在這段指令之前,插入必要的指令將前面因為優(yōu)化Cache 到寄存器中的變量值先寫回內(nèi)
存,如果以后又要使用這些變量再重新讀取。
(3)進程切換的標志-----sp指針的切換
    因為進程切換也就是進程描述符的切換,現(xiàn)在讓我們來想一下我們是如何定位某個進程描述符的地址的,看下面的匯編代碼:
   mov $0xffffe000,%ecx
   andl %esp,%ecx
   movl %ecx,p

執(zhí)行上面代碼后,p中即存儲當前運行進程的thread_info結(jié)構(gòu)的地址,但是我們最長用的是進程描述符的地址,因此內(nèi)核設(shè)計了current宏來計算指向進程描述符的指針:
mov $0xfffe000,%ecx
andl %esp,%ecx
movl (%ecx),p
因為task字段在thread_info中的偏移量為0,所以執(zhí)行上述三條指令后,p即是當前運行的進程的描述符指針。
      我們可以看到,只要知道esp,那么,進程就唯一確定了,所以說esp是進程切換的標志
(4)堆棧切換的標志 --- ebp (棧低指針)
    毋庸置疑,棧底指針肯定是堆棧切換的標志
(5)switch_to 三個參數(shù)
進程切換一般都涉及三個進程,如進程a切換成進程b,b開始執(zhí)行,但是當a恢復(fù)執(zhí)行的時候往往是通過一個進程c,而不是進程b。注意switch_to的調(diào)用: switch_to(prev,next,prev), 可以看到last就是prev 調(diào)用方法如下:進程A->進程B switch_to(A,B,A)主要有三個參數(shù):
輸入?yún)?shù)兩個:prev:切換前的進程,next:切換后的進程,輸出參數(shù)一個:last:切換前進程。  
注意這三個變量都是局部變量,在系統(tǒng)棧中,所以切換到另一進程后變量的值不會改變。
進程a切換b之前,eax的值為prev,也就是A;edx的值為next,也就是B,eax的值為last,也就是A
當不考慮第三個參數(shù)時,從C切換成A,內(nèi)核棧切換成A的棧,這時A中的prev和nexxt分別指向A和B,進程C的引用丟失了。這時第三個參數(shù)就派上用場了。C切換進程A后,將C存入eax中,切換到A后,由于輸出部"=a" (last)會將eax的值寫入last中,也就是prev中,所以此時prev和next的值就是C和B了。
(6)匯編參數(shù)的傳遞?
匯編是通過寄存器傳遞參數(shù)的,這里用了eax和edx,這樣jmp就和call差不多了,但是jmp和call的區(qū)別是,call會有硬件自動壓棧一些寄存器的,比如cs:ip ,這里是通過手工壓棧ip,模擬了call,在__switch_to中,用return 返回。我們又會想到為什么不用call呢?原因是進入__switch_to后,我們是為運行別的進程做準備,也就是說當返回的時候應(yīng)該是運行next進程而不是當前進程。如果用call的話,壓棧的是當前進程的ip,那么__switch_to返回后就運行當前進程了,這與我們想運行next的進程的想法是不一致的,因此,我們手工壓棧的是next進程的ip,那么,當__switch_to返回后自然出棧的就是next的ip,也就是運行next進程了。

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
2 [報告]
發(fā)表于 2009-05-05 09:25 |只看該作者
不錯,呵呵

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
3 [報告]
發(fā)表于 2009-05-05 10:57 |只看該作者
LZ這幾天發(fā)表了不少好文章,多謝分享。

論壇徽章:
0
4 [報告]
發(fā)表于 2011-05-06 16:18 |只看該作者
頂樓主

論壇徽章:
0
5 [報告]
發(fā)表于 2011-05-06 16:31 |只看該作者

論壇徽章:
0
6 [報告]
發(fā)表于 2011-05-06 16:37 |只看該作者
學習。。。。。。

論壇徽章:
0
7 [報告]
發(fā)表于 2011-05-07 20:48 |只看該作者
很有啟發(fā)的
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP