linux用戶空間與內核空間
1. 深入User space(用戶空間) 與 Kernel space(內核空間)
深入探索linux世界中的神秘雙面:User space與Kernel space
在Linux的廣闊舞台中,兩種獨特的運行空間——User space(用戶程序的領地)與Kernel space(內核的核心地帶)如同兩股平行的力量,各自承擔著至關重要的角色。Kernel space,作為系統的守護者,以隔離和保護的姿態,擁有執行所有命令和操控系統資源的特權。相比之下,User space受限於許可權,它通過system call(系統調用)與內核進行微妙的溝通,確保數據安全與流暢的進程交互。
讓我們通過實例來窺探這兩大領域的互動。想像你正在運行的top命令,它如一面鏡子,反映出CPU時間的分配:user部分代表User space的運行時間,sys部分則象徵著Kernel space的職責。niceness、idle和wait等指標,就像財務報告中的細節,揭示著系統的運行狀態。
time命令,如同時間偵探,記錄下程序執行的點點滴滴。內核空間和用戶空間的關系,可以比喻為銀行和儲戶,內核扮演著守門人,管理許可權,用戶空間則像儲戶,按需獲取服務。在普通IO操作中,進程將控制權暫時交給內核,內核負責處理,甚至預讀取,再將數據安全地傳遞給用戶空間的緩沖區,就像銀行轉賬一樣,需經過嚴格的驗證和許可權控制。
硬碟數據的處理同樣遵循這個邏輯。內核負責處理非對齊的數據塊,虛擬內存機制則提升了I/O效率,通過MMU(內存管理單元)實現多地址指向同一物理內存,擴展了存儲空間。當內存訪問出現問題時,內核作為幕後決策者,介入處理內存調入和可能的頁面調出。
Linux的系統結構就像一座三重塔:硬體-內核空間-用戶空間,進程在執行系統調用時,會從用戶態轉變為內核態,擁有0級特權,每個進程擁有獨立的內核棧。而在用戶態,進程執行用戶代碼,擁有3級特權,中斷處理程序則依賴於進程的內核棧,體現了許可權和控制的微妙平衡。
邏輯地址、線性地址和物理地址,構成了內存管理的三維世界。虛擬內存擴展了可用內存,邏輯地址,也就是虛擬地址,與物理地址之間存在著固定映射,就像銀行賬戶和實際存款的關系。
在32位傳統Linux系統中,物理地址與邏輯地址之間的差異體現在0xC0000000的偏移。內核模塊巧妙地利用高端邏輯地址,映射到低端物理內存,解決大內存訪問的問題。
內存映射與高端內存的交互,是Linux內核技術的精華。通過ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM的劃分,內核解決了超過1GB內存的訪問難題。高端內存被用於臨時大容量存儲,使用完畢後會自動釋放。mmap函數則像一座橋梁,連接文件和進程的地址空間,使得文件操作和進程間共享變得輕而易舉。
vm_area_struct,這個內存管理的關鍵結構,就像內存地圖,用鏈表或樹的形式管理不同的區域。它包含了地址信息和系統調用函數指針,mmap函數就是通過它創建和管理虛擬映射區域。
當程序試圖訪問映射空間時,可能會觸發缺頁異常,此時內核會介入,通過頁表載入缺失頁面,必要時從磁碟獲取數據。寫操作可能延遲回寫到文件,但可以通過msync()函數確保數據的一致性。
總結來說,User space與Kernel space的協作是Linux內存管理的核心,它們各自負責不同的職責,共同構建起系統的穩健運行。理解這兩個空間的工作原理,就像理解銀行和儲戶之間的交易規則,是深入掌握Linux系統的關鍵。
2. linux進程地址空間劃分
Linux 64位系統在理論上擁有16位十六進製表示的巨大內存地址空間,即從0x0000000000000000到0xFFFFFFFFFFFFFFFF。然而,Linux僅實際使用了其中的256T空間,其餘部分未被使用。
在Linux 64位操作中,實際使用的是低47位地址,高17位用於擴展,只能取全0或全1值。這樣,可用的地址空間被分為兩部分:用戶空間(0x0000000000000000至0x00007FFFFFFFFFFF)和內核空間(0xFFFF800000000000至0xFFFFFFFFFFFFFFFF),剩餘部分未被利用。
用戶空間主要包含以下部分:代碼段、數據段、BSS段、堆和棧。
代碼段用於存放程序執行代碼,即CPU執行的機器指令。
數據段存放已初始化且初值不為0的全局變數和靜態局部變數,屬於靜態內存分配,可讀可寫。
BSS段包括未初始化全局變數和靜態局部變數的空間。
堆(heap)是動態分配內存的區域,當進程讀取文件時,若文件未在內存中,會通過缺頁中斷獲取物理內存,通過磁碟調頁將文件數據讀入內存,實現文件的讀取。
文件在兩個進程間共享時,即使它們映射到同一文件,虛擬地址空間也可能不同。若進程A先讀取文件,則會獲取物理內存,通過磁碟調頁將文件數據讀入內存。進程B在訪問文件時,若文件數據不在內存中,則會查找緩存區,如果緩存中有文件數據,則建立映射關系,實現進程間通信。
棧(stack)用於存儲函數調用時的局部變數。
以數組s和指針p3為例,數組s的內容是在運行時賦值,而指針p3指向的常量區字元串內容在編譯時已賦值。
使用malloc函數分配內存時,虛擬內存的分配情況如下:
當malloc分配的內存小於128k時,使用brk分配內存,將_edata向高地址移動,只分配虛擬空間,不對應物理內存。第一次讀/寫數據時,會觸發內核缺頁中斷,內核分配物理內存,建立虛擬地址空間映射關系。若分配的內存不被訪問,對應的物理內存不會被分配。
brk分配的內存需要等待高地址內存釋放後才能釋放,可能導致內存碎片。
當malloc分配的內存大於128k時,使用mmap分配內存,在堆和棧之間尋找空閑內存分配,對應獨立內存且初始化為0。mmap分配的內存可以直接通過free釋放。
當最高地址空間的空閑內存超過128k時,Linux執行內存緊縮操作,釋放部分內存。
當進程訪問未建立映射關系的虛擬內存時,邏輯地址轉換為物理地址,發現當前頁不在內存中,處理器自動觸發缺頁異常。
3. Linux的內核空間和用戶空間是如何劃分的(以32位系統為例)
通常32位Linux內核地址空間劃分0~3G為用戶空間,3~4G為內核空間。地址分配如下圖所示
直接映射區:線性空間中從3G開始最大896M的區間,為直接內存映射區,該區域的線性地址和物理地址存在線性轉換關系:線性地址=3G+物理地址。
動態內存映射區:該區域由內核函數vmalloc來分配,特點是:線性空間連續,但是對應的物理空間不一定連續。vmalloc分配的線性地址所對應的物理頁可能處於低端內存,也可能處於高端內存。
永久內存映射區:該區域可訪問高端內存。訪問方法是使用alloc_page(_GFP_HIGHMEM)分配高端內存頁或者使用kmap函數將分配到的高端內存映射到該區域。
固定映射區:該區域和4G的頂端只有4k的隔離帶,其每個地址項都服務於特定的用途,如ACPI_BASE等。