1. 程式人生 > 其它 >Linux0.11任務0中fork和pause的內嵌問題

Linux0.11任務0中fork和pause的內嵌問題

網上找的一些理解資料:

該樓層疑似違規已被系統摺疊 隱藏此樓檢視此樓

再談關於任務0中fork和pause的內嵌問題

chaser 2008-7-30 12:52

1。任務0中使用內嵌,僅僅是避免任務1的使用者態堆疊中沒有無用的多餘資訊。
2。即使在任務0中fork和pause都不使用內嵌,對任務1也沒有任何影響。因為任務1的使用者態堆疊與任務0的沒有任何關係。
3。對main.c中開頭的一段註釋“我們需要下面這些內嵌語句--從核心空間建立程序將導致沒有寫時複製。。。”的理解:如果有寫時複製,那麼fork和pause都可以不用內嵌,因為寫時複製可以在任務0或者任務1對堆疊有寫操作時立刻分配新的記憶體頁,從而保證任務0和任務1的堆疊獨立並且乾淨(只包含自己任務的資訊)。
4。同樣是對上面一段註釋,怎樣理解“從核心空間建立程序”?我的疑問是:在執行fork前已經執行了move_to_user_mode();任務0進入使用者態,這時仍然是“從核心空間”麼?對於init()中的fork又怎樣理解呢?核心空間和核心態又怎樣理解呢?
不知前3點的理解是否正確,若不正確請版主指出問題所在,並請教第4個問題。

謝謝。

hinus 發表於 2008-7-30 16:07

又回去讀了一下 copy_page_tables。有了點新的想法。我覺得 main.c 裡的註釋所指的 "kernel space" 所指的應該不是特權級 0 的堆疊或者資料什麼的,而是指低於 1M 部分的記憶體。在第一次呼叫 fork() 時,由於源頁面地址小於 1M,所以父程序,也就是 task0 的寫許可權並沒有改變,這意味著寫時複製機制對 task0 無效,換言之,task0 和 task1 在共享一塊記憶體,只不過 task1 不能在上面寫。這可以稱做“不對稱的寫時複製”這時如果發生了樓上 kenya 所說的那種情況——task0 先於 task1 被排程,那麼 task0 就會向“共享記憶體”裡寫東西,當 task1 寫時複製時,就會把不相干的東西拷過去。保持獨立性,這個原則應該一直被堅持。

而且,task0 的資料和程式碼永遠不會超過 1M,這是不是就是 kenya 兄所說的“不改變程序0的頁面對映結構”的更俗泛的解釋?而 task1 就已經因為第一次 fork() 被移到了 1M 以外,也就是擴充套件記憶體中去了,所以 task1 中再執行 fork() 時,父程序和子程序無論誰先被排程,都會發生寫時複製,這時,可以稱為“對稱的寫時複製”。
S

http://blog.sina.com.cn/s/blog_63fe270801014y8d.html

堆疊這一塊確實是有點不好理解,尤其對於為啥程序1執行前程序0的堆疊要保持“乾淨”這個問題,
也就是產生程序1的fork和pause為什麼要用內聯彙編而不能用函式呼叫的問題。
我的理解是,因為fork函式的特殊性,它會“兩次返回”,一次返回子程序pid,一次返回0。如果fork和pause都採用函式呼叫,
那麼呼叫call pause指令壓入堆疊的返回地址會覆蓋call fork時壓入的地址,
因為他們都在user_stack的同一個地方,因此fork第二次返回時會去執行call pause的下一條指令:一直呼叫pause函式的指令,
而不是call fork的下一條指令:呼叫init的指令,因此核心陷入死迴圈。
如果只是其中一個使用函式呼叫,堆疊只被一個函式使用,就不會出現這種情況。這個分析還是有點抽象,下面看fork和pause都使用函式呼叫的除錯過程。
https://tieba.baidu.com/p/3495213636

main.c中關於pause和fork的內嵌問題

gotop167 發表於 2004-11-23 12:22
main.c中pause採用了內嵌的形式呼叫,從而保證不亂用堆疊,
而其中呼叫了schedule()(該函式我還沒有學習到),是不是該函式是執行在核心狀態下的
如果不是執行在核心態下,不也是會弄亂任務0的使用者堆疊嗎?
謝謝


redgrid 發表於 2004-11-23 16:11

main.c中關於pause和fork的內嵌問題
這個問題已經在論壇中討論過多次。建議瀏覽一下以前的帖子。

  由於建立新程序的過程是通過完全複製父程序程式碼段和資料段的方式實現的,因此在首次使用fork()建立新程序init時,
為了確保新程序使用者態堆疊沒有程序0的多餘資訊,要求程序0在建立首個新程序之前不要使用使用者態堆疊,
也即要求任務0不要呼叫函式。因此在main.c主程式移動到任務0執行後,
任務0中的程式碼fork()不能以函式形式進行呼叫。程式中實現的方法是採用gcc函式內嵌的形式來執行這個系統呼叫。參見下面程式第23行。
  另外,在建立新程序init的過程中,系統對其進行了一些特殊處理。
在為新程序init複製其父程序(程序0)的頁目錄和頁表項時並沒有為它們處於核心區的程式碼和資料執行寫時複製(Copy on Wirte)操作 ,
程序0和程序init實際上同時使用著核心程式碼區內(小於1MB的實體記憶體)相同的程式碼和資料實體記憶體頁面,
只是執行的程式碼不在一處,因此實際上它們也同時使用著相同的使用者堆疊區。
為了不出現衝突問題,就必須要求任務0在整個執行過程中禁止使用到使用者堆疊區域,
而讓程序init能單獨使用堆疊。因此pause()也必須採用內嵌函式形式來實現。
  當系統中一個程序(例如init程序的子程序,程序2)執行過execve()呼叫後,
程序2的程式碼和資料區會位於系統的主記憶體區中,因此係統可以利用寫時複製技術來處理其他新程序的建立和執行。
----------------
  另外,每個程序都有自己的核心堆疊。


bluesea 發表於 2006-12-11 16:53

main.c中關於pause和fork的內嵌問題
以下是我的一些看法,不知道對不對,請各位大俠指點:
程序通過fork()建立子程序,子程序複製了父程序的頁目錄項和頁表,和父程序共享資料、程式碼和使用者堆疊。
子程序建立成功後,父子程序的頁表項中R/W位都變成0,因此任何一個程序對頁面執行寫操作時,都會產生頁防寫異常,
進而呼叫相應的異常處理函式do_wp_page()。do_wp_page()函式實現寫時複製。
然而程序0是一個特殊的程序,它通過fork()產生程序1後,只有程序1的R/W位都變成0,因此只有程序1對頁面執行寫操作時,才會產生頁防寫異常,
進而呼叫相應的異常處理函式do_wp_page()。do_wp_page()函式實現寫時複製。而程序0對頁面執行寫操作時,就會直接寫到程序0和程序1共享的頁面中去。
因此程序0成功建立程序1後,如果程序0先進行了使用者堆疊的寫操作的話,而程序1使用使用者堆疊時(讀或寫),
堆疊中可能會留下程序0留下的資料,這樣程序1可能會向堆疊中寫資料,這會產生防寫,程序1的使用者堆疊會被轉移到主記憶體中,
不會影響程序0的執行。而程序1的使用者堆疊中多了程序1壓棧的資料,可能會影響程序1的正常執行。程序1也有可能要讀使用者堆疊的資料,
這有可能讀到程序0壓棧的資料,會影響系統的正常執行。應此linux0.11採取的策略就是程序的0產生後,
就不使用其使用者堆疊了,程序1只要一寫使用者堆疊,就為其在主記憶體中分配一使用者堆疊。
所以我認為:
一、
而fork不嵌入的話,沒有什麼影響,應為它執行的時候,排程程式還沒有開始執行,因為排程程式是在pause()中呼叫的,
所以此時fork()呼叫過程或呼叫即將返回時根本不會發生程序切換,
而fork()呼叫返回時會將程序0和程序1共享的的使用者堆疊清理乾淨的。程序切換隻會發生在pause()的第一次呼叫之後。
二、
程序0、程序1雖然它們同時使用著相同的使用者堆疊區,然而它們的對使用者堆疊的許可權是不同的。
程序0通過fork()產生程序1時,只將程序1的R/W位都變成0,因此只有程序1對頁面執行寫操作時,才會產生頁防寫異常。
如果pause() 不嵌入的話,在呼叫pause()時,程序0會使用使用者堆疊,而此時,系統有可能切換到程序1執行,由於程序0的pause()還沒有退出,
因此程序0使用的程序0和程序1共享的使用者堆疊還沒有清理乾淨,而此時程序1去使用沒有清理乾淨的共享的使用者堆疊有可能會是程序1出現問題。
但這肯定不會影響程序0的執行,因為程序1對共享的使用者堆疊只有讀的許可權,當它要寫使用者堆疊的時候,會產生頁防寫異常,
為其在主記憶體中分配一使用者堆疊。
所以,我的觀點:pause必須嵌入,而fork可以不嵌入。