Linux內核閱讀筆記 0.11 一

什麼是寫時複製?COW copy on write
寫入時複製是一種計算機程序設計領域的優化策略。其核心思想是,如果有多個調用者同時請求相同資源(如內存或磁盤上的數據存儲),他們會共同獲取相同的指針指向相同的資源,直到某個調用者試圖修改資源的內容時,系統纔會真正複製一份專用副本(private copy)給該調用者,而其他調用者所見到的最初的資源仍然保持不變。這個過程對其他的調用者是透明的(transparently)。此作法的主要優點是如果調用者沒有修改該資源,就不會有副本(private copy)被建立,因此多個調用者只是讀取操作是可以共享同一份資源。

什麼是內聯函數?static inline 和 extern inline 的含義
它的引入使得編程者只關心函數的功能和使用方法,而不必關心函數功能的具體實現;函數的引入可以減少程序的目標代碼,實現程序代碼和數據的共享。但是,函數調用也會帶來降低效率的問題,因爲調用函數實際上將程序執行順序轉移到函數所存放在內存中某個地址,將函數的程序內容執行完後,再返回到轉去執行該函數前的地方。這種轉移操作要求在轉去前要保護現場並記憶執行的地址,轉回後先要恢復現場,並按原來保存地址繼續執行。因此,函數調用要有一定的時間和空間方面的開銷,於是將影響其效率。特別是對於一些函數體代碼不是很大,但又頻繁地被調用的函數來講,解決其效率問題更爲重要。引入內聯函數實際上就是爲了解決這一問題。
在程序編譯時,編譯器將程序中出現的內聯函數的調用表達式用內聯函數的函數體來進行替換。顯然,這種做法不會產生轉去轉回的問題,但是由於在編譯時將函數休中的代碼被替代到程序中,
因此會增加目標
程序代碼量,進而增加空間開銷,而在時間代銷上不象函數調用時那麼大,可見它是以目標代碼的增加爲代價來換取時間的節省。
1.內聯函數可減少cpu的系統開銷,並且程序的整體速度將加快,但當內聯函數很大時,會有相反的作用,因此一般比較小的函數才使用內聯函數.
2.有兩種內聯函數的聲明方法,一種是在函數前使用inline關見字,另一種是在類的內部定義函數的代碼,這樣的函數將自動轉換爲內聯函數,而且沒必要將inline放在函數前面.
3.內聯是一種對編譯器的請求,下面這些情況會阻止編譯器服從這項請求.
  如果函數中包含有循環,switch或goto語句,遞歸函數,含有static的函數.
extern inline表示該函數是已聲明過的了.由於函數本身可以聲明多次,所以extern對函數的影響僅僅把函數的隱藏屬性顯式化了.
extern 對於非函數的對象是有用的,因爲對象聲明時會帶來內存的分配,而用 extern就表示該對象已經聲明過了,不用再分配內存.

_syscall0
添加新的系統調用_syscall0(int, mysyscall)
EAX寄存器以稱爲累加器,AX寄存器是算術運算的主要寄存器,所有的輸入、輸出只使用AL或AX人作爲數據寄存器。在80386及其以上的微處理器中,EAX寄存器可以用來存儲單元的偏移地址。

#include <linux/unistd.h>
_syscall0(int,mysyscall) /* 注意這裏沒有分號*/
int main()
{
mysyscall();
}

好,由於有了_syscall0 這個宏,mysyscall 將得到定義。但是現在系統會去找系統調用號,以放入eax。所以,接下來我們定義系統調用號。

添加系統調用號
系統調用號在文件unistd.h裏面定義。這個文件可能在你的系統上會有兩個版本:一個
是C庫文件版本,出現的地方是在/usr/include/unistd.h和/usr/include/asm/unistd.h;另外還有
一個版本是內核自己的unistd.h,出現的地方是在你解壓出來的2.4.18 內核代碼的對應位置
(比如/usr/src/linux/include/linux/unistd.h和/usr/include/asm-i386/unistd.h)。當然,也有可能
這個C 庫文件只是一個到對應內核文件的連接。
現在,你要做的就是在文件unistd.h中添加我
們的系統調用號:__NR_mysyscall,如下所示:
include/asm-i386/unistd.h
/usr/include/asm/unistd.h

243 #define __NR_lremovexattr 236
244 #define __NR_fremovexattr 237
245 #define __NR_mysyscall 238 /* 添加的系統調用 */

添加系統調用號之後,系統才能根據這個號,作爲索引,去找syscall_table中的相應表項。
所以說,我們接下來的一步就是:
在系統調用表中添加相應表項
我們前面講過,系統調用處理程序(system_call)會根據eax 中的索引到系統調用表
(sys_call_table)中去尋找相應的表項。所以,我們必須在那裏添加我們自己的一個值。
arch/i386/kernel/entry.S

398 ENTRY(sys_call_table)
399 .long SYMBOL_NAME(sys_ni_syscall)
400 .long SYMBOL_NAME(sys_exit)
401 .long SYMBOL_NAME(sys_fork)
402 .long SYMBOL_NAME(sys_read)
403 .long SYMBOL_NAME(sys_write)
……
……
634 .long SYMBOL_NAME(sys_ni_syscall)
635 .long SYMBOL_NAME(sys_ni_syscall)
636 .long SYMBOL_NAME(sys_ni_syscall)
637 .long SYMBOL_NAME(sys_mysyscall)
638
639 .rept NR_syscalls-(.-sys_call_table)/4
640 .long SYMBOL_NAME(sys_ni_syscall)
641 .endr

到現在爲止,系統已經能夠正確地找到並且調用sys_mysyscall。剩下的就只有一件事情,那
就是sys_mysyscall的實現。
sys_mysyscall 的實現
我們把這一小段程序添加在kernel/sys.c 裏面。在這裏,我們沒有在kernel 目錄下另外
添加自己的一個文件,這樣做的目的是爲了簡單,而且不用修改Makefile,省去不必要的麻
煩。

asmlinkage int sys_mysyscall(void)
{
current->uid = current->euid = current->suid = current->fsuid = 0;
return 0;
}

這個系統調用中,把標誌進程身份的幾個變量uid、euid、suid和fsuid都設爲0。
到這裏爲止,我們所要做的添加一個新的系統調用的所有工作就完成了,是不是非常簡
單?的確如此。因爲Linux 內核結構的層次性還是非常清楚的,這就使得每一個開發者可以
把精力放在怎麼樣實現具體的功能上,而不用在一些接口函數上傷腦筋。
測試

#include <linux/unistd.h>
_syscall0(int,mysyscall) /* 注意這裏沒有分號*/
int main()
{
mysyscall(); /* 這個系統調用的作用是使得自己的uid爲0 */
printf(“em…, this is my uid: %d. \n”, getuid());
}

全局描述表(GDT Global Descriptor Table):在保護模式下一個重要的數據結構。
局部描述符表(LDT— Local Descriptor Table):是保護模式下存儲器尋址的一種數據表,它包含了與某個任務相關聯的段描述符,在設計操作系統時,通常每個任務有一個獨立的LDT。LDT提供了將一任務的代碼段、數據段與操作系統的其餘部分相隔離的機制。
LDT的定位與GDT不同,LDT的段基址與段限由LDT描述符表示,該描述符同一般的描述符一樣存放在全局描述符表中,因此首先要從GDT中找到LDT描述符,80386微處理器中有一個局部描述符表寄存器 LDTR,這是一個16位寄存器,LDTR中存放一個被稱爲“段選擇符”(Segment Selector)的16位數,段選擇符用來在GDT中尋找LDT描述符。
段描述符是GDT(全局描述表)和LDT表(局部描述符表)中的一個數據結構項,用於向處理器提供有關一個段的位置和大小信息以及訪問控制的狀態信息
保護模式:設計用來增強多工和系統穩定度,像是 內存保護,分頁 系統,以及硬件支援的 虛擬內存。
另外一種286和其之後CPU的運行模式是實模式,一種向前兼容且關閉了保護模式這些特性的CPU運行模式。用來讓新的芯片可以運行舊的軟件。依照設計的規格,所有的x86 CPU都是在實模式下開機,來確保傳統操作系統的向前兼容性。在任何保護模式的特性可用前,他們必須要由某些程序手動地切換到保護模式。在現今的計算機,這種切換通常是由操作系統在開機時候必須完成的第一件任務的一個。它也可能當CPU在保護模式下運行時,使用虛擬86模式來運行設計運行在實模式下的代碼。

程序不可見部分:就是cache
任務狀態段:TSS是指在操作系統進程管理的過程中,任務(進程)切換時的任務現場信息。 在任務切換過程中,首先,處理器中各寄存器的當前值被自動保存到TR(任務寄存器)所指定的TSS中;然後,下一任務的TSS的選擇子被裝入TR;最後,從TR所指定的TSS中取出各寄存器的值送到處理器的各寄存器中。由此可見,通過在TSS中保存任務現場各寄存器狀態的完整映象,實現任務的切換。

從實模式到保護模式的過程針對80386處理器
1.初始化IDT
2.初始化GDT
3.PE位置1
4.JMP 跳轉
5.選擇子

虛擬8086模式
特殊的模式。時間片的技術,運行多個任務。

Linux系統的體系結構
用戶應用程序
操作系統服務
操作系統內核(和硬件交互)
硬件系統

主程序》系統服務》支持函數

單內核模式,速度快,但是層次結構不強。

Linux內核幾個重要的模塊:
1.內存管理模塊
2.進程調度模塊
3.文件系統模塊
4.進程間通信模塊
5.網絡接口模塊

發佈了28 篇原創文章 · 獲贊 20 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章