大廠面試路上的阻礙-Linux內核面試題(含答案)
1. Linux 中主要有哪幾種內核鎖?
Linux 的同步機制從 2.0 到 2.6 以來不斷發(fā)展完善。從最初的原子操作,到后來 的信號量,從大內核鎖到今天的自旋鎖。這些同步機制的發(fā)展伴隨 Linux 從單處 理器到對稱多處理器的過渡;伴隨著從非搶占內核到搶占內核的過度。Linux 的 鎖機制越來越有效,也越來越復雜。 自旋鎖最多只能被一個可執(zhí)行線程持有,如果一個執(zhí)行線程試圖請求一個已被爭 用已經被持有)的自旋鎖,那么這個線程就會一直進行忙循環(huán)——旋轉——等待 鎖重新可用。要是鎖未被爭用,請求它的執(zhí)行線程便能立刻得到它并且繼續(xù)進行。 自旋鎖可以在任何時刻防止多于一個的執(zhí)行線程同時進入臨界區(qū)。 信號量的睡眠特性,使得信號量適用于鎖會被長時間持有的情況;只能在進程上 下文中使用,因為中斷上下文中是不能被調度的;另外當代碼持有信號量時,不 可以再持有自旋鎖。 Linux 內核中的同步機制:原子操作、信號量、讀寫信號量和自旋鎖的 API,另 外一些同步機制,包括大內核鎖、讀寫鎖、大讀者鎖、RCU (Read-Copy Update, 顧名思義就是讀-拷貝修改),和順序鎖。
2. Linux 中的用戶模式和內核模式是什么含意?
MS-DOS 等操作系統(tǒng)在單一的 CPU 模式下運行,但是一些類 Unix 的操作系統(tǒng)則使 用了雙模式,可以有效地實現(xiàn)時間共享。在 Linux 機器上,CPU 要么處于受信任 的內核模式,要么處于受限制的用戶模式。除了內核本身處于內核模式以外,所 有的用戶進程都運行在用戶模式之中。 內核模式的代碼可以無限制地訪問所有處理器指令集以及全部內存和 I/O 空間。 如果用戶模式的進程要享有此特權,它必須通過系統(tǒng)調用向設備驅動程序或其他 內核模式的代碼發(fā)出請求。另外,用戶模式的代碼允許發(fā)生缺頁,而內核模式的 代碼則不允許。 在 2.4 和更早的內核中,僅僅用戶模式的進程可以被上下文切換出局,由其他進 程搶占。除非發(fā)生以下兩種情況,否則內核模式代碼可以一直獨占 CPU: (1) 它自愿放棄 CPU; (2) 發(fā)生中斷或異常。 2.6 內核引入了內核搶占,大多數(shù)內核模式的代碼也可以被搶占。
3. 怎樣申請大塊內核內存?
在 Linux 內核環(huán)境下,申請大塊內存的成功率隨著系統(tǒng)運行時間的增加而減少, 雖然可以通過 vmalloc 系列調用申請物理不連續(xù)但虛擬地址連續(xù)的內存,但畢竟 其使用效率不高且在 32 位系統(tǒng)上 vmalloc 的內存地址空間有限。所以,一般的 建議是在系統(tǒng)啟動階段申請大塊內存,但是其成功的概率也只是比較高而已,而不是100%。如果程序真的比較在意這個申請的成功與否,只能退用“啟動內 存”Boot Memory)。下面就是申請并導出啟動內存的一段示例代碼:
可見其應用還是比較簡單的,不過利弊總是共生的,它不可避免也有其自身的限制:內存申請代碼只能連接進內核,不能在模塊中使用。被申請的內存不會被頁分配 器和 slab 分配器所使用和統(tǒng)計,也就是說它處于系統(tǒng)的可見內存之外,即使在 將來的某個地方你釋放了它。一般用戶只會申請一大塊內存,如果需要在其上實 現(xiàn)復雜的內存管理則需要自己實現(xiàn)。
在不允許內存分配失敗的場合,通過啟動內 存預留內存空間將是我們唯一的選擇。
【文章福利】小編推薦自己的Linux內核技術交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。?!前100名進群領取,額外贈送一份價值699的內核資料包(含視頻教程、電子書、實戰(zhàn)項目及代碼)? ??


4. 用戶進程間通信主要哪幾種方式?
管道 Pipe):管道可用于具有親緣關系進程間的通信,允許一個進程和另一個 與它有共同祖先的進程之間進行通信
命名管道 named pipe):命名管道克服了管道沒有名字的限制,因此,除具有 管道所具有的功能外,它還允許無親緣關系進程間的通信。命名管道在文件系統(tǒng) 中有對應的文件名。命名管道通過命令 mkfifo 或系統(tǒng)調用 mkfifo 來創(chuàng)建。
信號 Signal):信號是比較復雜的通信方式,用于通知接受進程有某種事件發(fā) 生,除了用于進程間通信外,進程還可以發(fā)送信號給進程本身;linux 除了支持 Unix 早期信號語義函數(shù) sigal 外,還支持語義符合 Posix.1 標準的信號函數(shù) sigaction 實際上,該函數(shù)是基于 BSD 的,BSD 為了實現(xiàn)可靠信號機制,又能夠 統(tǒng)一對外接口,用 sigaction 函數(shù)重新實現(xiàn)了 Signal 函數(shù))。
消息 Message)隊列:消息隊列是消息的鏈接表,包括 Posix 消息隊列 system ?V 消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則 可以讀走隊列中的消息。
消息隊列克服了信號承載信息量少,管道只能承載無格 式字節(jié)流以及緩沖區(qū)大小受限等缺
信號量 semaphore):主要作為進程間以及同一進程不同線程之間的同步手段。
套接字 Socket):更為一般的進程間通信機制,可用于不同機器之間的進程間 通信。起初是由 Unix 系統(tǒng)的 BSD 分支開發(fā)出來的,但現(xiàn)在一般可以移植到其它 類 Unix 系統(tǒng)上:Linux 和 System V 的變種都支持套接字。
5. 通過伙伴系統(tǒng)申請內核內存的函數(shù)有哪些?
在物理頁面管理上實現(xiàn)了基于區(qū)的伙伴系統(tǒng) zone based buddy system)。對不 同區(qū)的內存使用單獨的伙伴系統(tǒng)(buddy system)管理,而且獨立地監(jiān)控空閑頁。 相應接口 alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。
補充知識:
1. 原理說明 * 頁全局目錄(Page Global Directory) * 頁中間目錄(Page Middle Directory) 頁全局目錄包含若干頁上級目錄的地址,頁上級目錄又依次包含若干頁中間目錄 的地址,而頁中間目錄又包含若干頁表的地址,每一個頁表項指 向一個頁框。
Linux 中采用 4KB 大小的 頁框作為標準的內存分配單元。 1.1.伙伴系統(tǒng)算法 為了避免出現(xiàn)這種情況,Linux 內核中引入了伙伴系統(tǒng)算法(buddy system)。把 所有的空閑頁框分組為 11 個 塊鏈表,每個塊鏈表分別包含大小為 1,2,4,8, 16,32,64,128,256,512 和 1024 個連續(xù)頁框的頁框塊。最大可以申請 1024 個連 續(xù)頁框,對應 4MB 大小的連續(xù)內存。
每個頁框塊的第一個頁框的物理地址 是該塊大小的整數(shù)倍。 頁框塊在釋放時,會主動將兩個連續(xù)的頁框塊合并為一個較大的頁框塊。 slab 分配器源于 Solaris 2.4 的 分配算法,工作于物理內存頁框分配器之上, 管理特定大小對象的緩存,進行快速而高效的內存分配。
2. 常用內存分配函數(shù) unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order) __get_free_pages 函數(shù)是最原始的內存分配方式,直接從伙伴系統(tǒng)中獲取原始 頁框,返回值為第一個頁框的起始地址。__get_free_pages 在實現(xiàn)上只是封裝 了 alloc_pages 函 數(shù),從代碼分析,alloc_pages 函數(shù)會分配長度為 1< struct kmem_cache *kmem_cache_create(const char *name, size_t size void (*ctor)(void*, struct kmem_cache *, unsigned long), void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags) kmem_cache_create/ kmem_cache_alloc 是基于 slab 分配器的一種內存分配方 式,適用于反復分配釋放同一大小內存塊的場合。
首先用 kmem_cache_create 創(chuàng) 建一個高速緩存區(qū)域,然后用 kmem_cache_alloc 從 該高速緩存區(qū)域中獲取新的 內存塊。kmem_cache_alloc 一次能分配的最大內存由 mm/slab.c 文件中的 MAX_OBJ_ORDER 宏定義,在默認的 2.6.18 內核版本中,該宏定義為 5,于是一次最多能申請 1<<5 * 4KB 也就是 128KB 的連續(xù)物理內存。分析內核源碼發(fā)現(xiàn), kmem_cache_create 函數(shù)的 size 參數(shù)大于 128KB 時會調用 BUG()。測試結果驗證 了分析結果,用 kmem_cache_create 分配超過 128KB 的內存時使內核崩潰。
void *kmalloc(size_t size, gfp_t flags) 2.4.vmalloc 前面幾種內存分配方式都是物理連續(xù)的,能保證較低的平均訪問時間。但是在某 些場合中,對內存區(qū)的請求不是很頻繁,較高的內存訪問時間也 可以接受,這 是就可以分配一段線性連續(xù),物理不連續(xù)的地址,帶來的好處是一次可以分配較 大塊的內存。
圖 3-1 表 示的是 vmalloc 分配的內存使用的地址范圍。vmalloc 對 一次能分配的內存大小沒有明確限制。出于性能考慮,應謹慎使用 vmalloc 函數(shù)。 在測試過程中, 最大能一次分配 1GB 的空間。
2.5.dma_alloc_coherent ma_addr_t *dma_handle, gfp_t gfp) 2.6.ioremap ioremap 是一種更直接的內存“分配”方式,使用時直接指定物理起始地址和需 要分配內存的大小,然后將該段 物理地址映射到內核地址空間。ioremap 用到 的物理地址空間都是事先確定的,和上面的幾種內存 分配方式并不太一樣,并 不是分配一段新的物理內存。ioremap 多用于設備驅動,可以讓 CPU 直接訪問外 部設備的 IO 空間。ioremap 能映射的內存由原有的物理內存空間決定,所以沒 有進行測試。 如果要分配大量的連續(xù)物理內存,上述的分配函數(shù)都不能滿足,就只能用比較特 殊的方式,在 Linux 內 核引導階段來預留部分內存。 void* alloc_bootmem(unsigned long size) 2.7.2.通過內核引導參數(shù)預留頂部內存 3.幾種分配函數(shù)的比較 __get_free_pages 直接對頁框進行操作 4MB 適用于分配較大量的連續(xù)物理內存 kmalloc 基于 kmem_cache_alloc 實現(xiàn) 128KB 最常見的分配方式,需要小于頁框 大小的內存時可以使用 dma_alloc_coherent 基于__alloc_pages 實現(xiàn) 4MB 適用于 DMA 操 作 alloc_bootmem 在啟動 kernel 時,預留一段內存,內核看不見小于物理內存大 小,內存管理要求較高。
6. 通過 slab 分配器申請內核內存的函數(shù)有?
7.Linux 的內核空間和用戶空間是如何劃分的(以 32 位系統(tǒng) 為例)?
8. vmalloc()申請的內存有什么特點?
9. 用戶程序使用 malloc()申請到的內存空間在什么范圍?
10.在支持并使能 MMU 的系統(tǒng)中,Linux 內核和用戶程序分 別運行在物理地址模式還是虛擬地址模式?
11. ARM 處理器是通過幾級也表進行存儲空間映射的?
12. Linux 是通過什么組件來實現(xiàn)支持多種文件系通的?
虛擬文件系統(tǒng)。
13. Linux 虛擬文件系統(tǒng)的關鍵數(shù)據(jù)結構有哪些?(至少寫出 四個)
14.對文件或設備的操作函數(shù)保存在那個數(shù)據(jù)結構中?
15.Linux 中的文件包括哪些?
16.創(chuàng)建進程的系統(tǒng)調用有那些?
17.調用 schedule()進行進程切換的方式有幾種?
18.Linux 調度程序是根據(jù)進程的動態(tài)優(yōu)先級還是靜態(tài)優(yōu)先級 來調度進程的?
Liunx 調度程序是根據(jù)根據(jù)進程的動態(tài)優(yōu)先級來調度進程的,但是動態(tài)優(yōu)先級又 是根據(jù)靜態(tài)優(yōu)先級根據(jù)算法計算出來的,兩者是兩個相關聯(lián)的值。
因為高優(yōu)先級 的進程總是比低優(yōu)先級的進程先被調度,為防止多個高優(yōu)先級的進程占用 CPU 資 源,導致其他進程不能占有 CPU,所以引用動態(tài)優(yōu)先級概念。
19.進程調度的核心數(shù)據(jù)結構是哪個?
20.如何加載、卸載一個模塊?
21.模塊和應用程序分別運行在什么空間?
22.Linux 中的浮點運算由應用程序實現(xiàn)還是內核實現(xiàn)?
應用程序實現(xiàn),Linux 中的浮點運算是利用數(shù)學庫函數(shù)實現(xiàn)的,庫函數(shù)能夠被應 用程序鏈接后調用,不能被內核鏈接調用。這些運算是在應用程序中運行的,然 后再把結果反饋給系統(tǒng)。
Linux 內核如果一定要進行浮點運算,需要在建立內核 時選上 math-emu,使用軟件模擬計算浮點運算,據(jù)說這樣做的代價有兩個:用戶 在安裝驅動時需要重建內核,可能會影響到其他的應用程序,使得這些應用程序 在做浮點運算的時候也使用 math-emu,大大的降低了效率。
23.模塊程序能否使用可鏈接的庫函數(shù)?
24.TLB 中緩存的是什么內容?
TLB,頁表緩存,當線性地址被第一次轉換成物理地址的時候,將線性地址和物 理地址的對應放到 TLB 中,用于下次訪問這個線性地址時,加快轉換速度。
25.Linux 中有哪幾種設備?
字符設備和塊設備。網卡是例外,他不直接與設備文件對應,mknod 系統(tǒng)調用用 來創(chuàng)建設備文件。
26.字符設備驅動程序的關鍵數(shù)據(jù)結構是哪個?
字符設備描述符 struct cdev,cdev_alloc()用于動態(tài)的分配 cdev 描述符, cdev_add()用于注冊一個 cdev 描述符,cdev 包含一個 struct kobject 類型的 數(shù)據(jù)結構它是核心的數(shù)據(jù)結構。
27.設備驅動程序包括哪些功能函數(shù)?
28.如何唯一標識一個設備?
Linux 使用一個設備編號來唯一的標示一個設備,設備編號分為:主設備號和次 設備號,一般主設備號標示設備對應的驅動程序,次設備號對應設備文件指向的 設備,在內核中使用 dev_t 來表示設備編號,一般它是 32 位長度,其中 12 位用 于表示主設備號,20 位用于表示次設備號,利用 MKDEV(int major,int minor); 用于生成一個 dev_t 類型的對象。
29.Linux 通過什么方式實現(xiàn)系統(tǒng)調用?
靠軟件中斷實現(xiàn)的,首先,用戶程序為系統(tǒng)調用設置參數(shù),其中一個編號是系統(tǒng) 調用編號,參數(shù)設置完成后,程序執(zhí)行系統(tǒng)調用指令,x86 上的軟中斷是有 int 產生的,這個指令會導致一個異常,產生一個事件,這個事件會導致處理器跳轉 到內核態(tài)并跳轉到一個新的地址。并開始處理那里的異常處理程序,此時的異常 處理就是系統(tǒng)調用程序。 ?
30.Linux 軟中斷和工作隊列的作用是什么?
Linux 中的軟中斷和工作隊列是中斷處理。
軟中斷一般是“可延遲函數(shù)”的總稱,它不能睡眠,不能阻塞,它處于中斷上 下文,不能進城切換,軟中斷不能被自己打斷,只能被硬件中斷打斷(上半部), 可以并發(fā)的運行在多個 CPU 上。所以軟中斷必須設計成可重入的函數(shù),因此也需 要自旋鎖來保護其數(shù)據(jù)結構。
工作隊列中的函數(shù)處在進程上下文中,它可以睡眠,也能被阻塞,能夠在不同 的進程間切換。已完成不同的工作。 可延遲函數(shù)和工作隊列都不能訪問用戶的進程空間,可延時函數(shù)在執(zhí)行時不可能 有任何正在運行的進程,工作隊列的函數(shù)有內核進程執(zhí)行,他不能訪問用戶空間 地址。
