萬盛學電腦網

 萬盛學電腦網 >> Linux教程 >> Linux--Linux內核模塊編程--系統調用在線閱讀

Linux--Linux內核模塊編程--系統調用在線閱讀

 系統調用
  迄今為止,我們做的唯一的事就是用好已定義的內核機制去登記 /proc 文件和設備驅動處理程序。如果你想做內核程序員認為你想做的,例如寫設備驅動程序,這就對了。但是如果你想做一些不平常的事,在某些地方改變系統的行為呢?那麼這大多取決你自己。
  這是內核編程中變得危險的地方。當寫下下面的例子,我殺死了 open 系統調用。這意味著我不能打開任何文件,不能運行任何程序,甚至不能 shutdown 計算機。我不得不按電源開關。幸運的,沒有文件消失。為了確保不失去任何文件,在你做insmod 和 rmmod之前請運行 sync 。
  忘記 /proc 文件,忘記設備文件。它們只是次要的細節。 所有進程都要使用的和內核通信的真正的方法是系統調用。當一個進程請求內核的服務時(例如打開文件,分支一個新進程,請求更多內存),這是被使用的機制。如果你想改變內核的行為方式,這是你要做的地方。順便說一下,如果你想看看一個程序使用了什麼系統調用,運行 strace <命令> <參數列表>。
  通常,一個進程不能訪問內核。它不能訪問內核的內存和調用內核的函數。CPU硬件強迫這個(那就是為什麼叫‘保護模式’的原因)。系統調用是對這個通常的規則的例外。所發生的是進程用適當的值填充寄存器然後調用跳到內核中先前已定義的區域的特定的指令(當然,該區域是用戶進程可讀但不可寫的)。在 Intel CPU下,使用中斷 0x80 做這個。硬件知道一旦你跳到這個區域,你就不再是運行在嚴格的用戶模式而是操作系統內核--因此你就可以做任何你想做的。
  內核中那個進程可以跳到的區域被稱為system_call。 在該區域的程序檢查系統調用數,該數告訴內核進程請求什麼服務。然後,它在系統調用表(sys_call_table)中查找調用的內核函數的地址。然後它調用該函數並在該函數返回後做一些系統檢查,再返回那個進程(或者如果該進程的時間運行完了就返回到一個不同的進程)。如果你想讀這個代碼,它在源文件arch//kernel/entry.S中的 ENTRY(system_call)行後。
  因此,如果你想改變某個系統調用的工作方法,我們所需要做的是寫一個自己的函數以實現它(通常是加一些我們的代碼然後再調用原來的函數)並且改變 sys_call_table 中的指針指向我們的函數。因為我們可能隨後要移除它而我們不想留下一個不穩定的系統,所以在 cleanup_module 中將那個表恢復成原來的狀態是很重要的。
  這兒的源代碼是這樣一個內核模塊的例子。我們想“偵察”某個用戶,並在該用戶打開一個文件的時候 printk 一個信息。朝著這個目標,我們用我們自己的被稱為our_sys_open的函數代替原來的系統調用去打開文件。這個函數檢查當前進程的UID(用戶的ID)而如果它等於我們要偵察的UID,它就調用 printk顯示要打開的文件名。然後,它用相同的參數調用原來的 open 函數做實際的打開文件的工作。
  init_module 代替sys_call_table 中合適的區域並且將原來的指針保存在一個變量中。 cleanup_module 函數使用該變量將沒件事恢復成通常的狀態。這個方法是危險的,因為兩個內核模塊同時改變同一個系統調用是可能的。想象我們有兩個內核模塊 A 和 B。A 的打開系統調用將是 A_open 而B 的將是 B_open 。現在,當 A 被插入內核,系統調用被 A_open 代替,當它完成時將調用原來的 sys_open 。接著,B 被插入內核,它將用 B_open 代替系統調用,當它完成時將調用它認為的原來的系統調用 A_open。
  現在,如果 B 被先移除,所有的事情將是好的--它將簡單的恢復系統調用為將恢復原始的系統調用的 A_open。然而,如果A 被移除然後 B 才被移除,系統將崩潰。A 的移除將恢復系統調用為原始的 sys_open,將 B 排除出那個環。然後,當 B 被移除,它將恢復系統調用為 它 認為是原始的系統調用的不再存在於內存的 A_open。咋看起來我們好象可以通過檢查系統調用是否等於我們的函數及是否根本不去改變它(因此 當 B 被移除時不會改變系統調用)來解決這個問題,但那會制造更嚴重的問題。當 A 被移除,看似系統調用變為 B_open ,因此它不再指向 A_open,因而在它被從內存移除前它不會將它恢復成 sys_open。不幸的, B_open 將仍然試圖調用不再存在的 A_open ,因此即使不移除 B 系統也會崩潰。 (譯者認為無論是否進行檢查,系統都會在A被先移除的情況下在B還未移除時使系統崩潰,因為從作者假設的情況看,B會調用“它”認為的原始的系統調用來完成其功能,在沒有檢查的情況下,B一樣在其存儲原系統調用的變量中存儲A的函數A_OPEN並進行調用而使系統崩潰。即使B不調用“它”認為的原始的系統調用來完成其功能,系統也會崩潰,因為它無法恢復系統調用。)


copyright © 萬盛學電腦網 all rights reserved