![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CZmNWMiRjMlJGZzUGNmdjZ2UGNkJTOhZWO5Q2MzIjY48CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
Linux 的同步機制從 2.0 到 2.6 以來不斷發展完善。從最初的原子操作,到後來的信号量,從
大核心鎖到今天的自旋鎖。這些同步機制的發展伴随 Linux 從單處理器到對稱多處理器的
過渡;
伴随着從非搶占核心到搶占核心的過度。Linux 的鎖機制越來越有效,也越來越複雜。
Linux 的核心鎖主要是自旋鎖和信号量。
自旋鎖最多隻能被一個可執行線程持有,如果一個執行線程試圖請求一個已被争用(已經被
持有)的自旋鎖,那麼這個線程就會一直進行忙循環——旋轉——等待鎖重新可用。要是鎖
未被争用,請求它的執行線程便能立刻得到它并且繼續進行。自旋鎖可以在任何時刻防止多
于一個的執行線程同時進入臨界區。
Linux 中的信号量是一種睡眠鎖。如果有一個任務試圖獲得一個已被持有的信号量時,信号
量會将其推入等待隊列,然後讓其睡眠。這時處理器獲得自由去執行其它代碼。當持有信号
量的程序将信号量釋放後,在等待隊列中的一個任務将被喚醒,進而便可以獲得這個信号
量。
信号量的睡眠特性,使得信号量适用于鎖會被長時間持有的情況;隻能在程序上下文中使用,
因為中斷上下文中是不能被排程的;另外當代碼持有信号量時,不可以再持有自旋鎖。
Linux 核心中的同步機制:原子操作、信号量、讀寫信号量和自旋鎖的 API,另外一些同步機
制,包括大核心鎖、讀寫鎖、大讀者鎖、RCU (Read-Copy Update,顧名思義就是讀-拷貝修
改),和順序鎖。
2) Linux 中的使用者模式和核心模式是什麼含意?MS-DOS 等作業系統在單一的 CPU 模式下運作,但是一些類 Unix 的作業系統則使用了雙
模式,可以有效地實作時間共享。在 Linux 機器上,CPU 要麼處于受信任的核心模式,要麼處
于受限制的使用者模式。除了核心本身處于核心模式以外,所有的使用者程序都運作在使用者模式
之中。
核心模式的代碼可以無限制地通路所有處理器指令集以及全部記憶體和 I/O 空間。如果使用者
模式的程序要享有此特權,它必須通過系統調用向裝置驅動程式或其他核心模式的代碼發出
請求。另外,使用者模式的代碼允許發生缺頁,而核心模式的代碼則不允許。
在 2.4 和更早的核心中,僅僅使用者模式的程序可以被上下文切換出局,由其他程序搶占。除非
發生以下兩種情況,否則核心模式代碼可以一直獨占 CPU:
(1) 它自願放棄 CPU;
(2) 發生中斷或異常。
2.6 核心引入了核心搶占,大多數核心模式的代碼也可以被搶占。
3) 怎樣申請大塊核心記憶體?在 Linux 核心環境下,申請大塊記憶體的成功率随着系統運作時間的增加而減少,雖然可以通過
vmalloc 系列調用申請實體不連續但虛拟位址連續的記憶體,但畢竟其使用效率不高且在 32 位
系統上 vmalloc 的記憶體位址空間有限。是以,一般的建議是在系統啟動階段申請大塊記憶體,但
是其成功的機率也隻是比較高而已,而不是 100%。如果程式真的比較在意這個申請的成功
與否,隻能退用“啟動記憶體”(Boot Memory)。下面就是申請并導出啟動記憶體的一段示例代碼:
void* x_bootmem = NULL;
EXPORT_SYMBOL(x_bootmem);
unsigned long x_bootmem_size = 0;
EXPORT_SYMBOL(x_bootmem_size);
static int __init x_bootmem_setup(char *str)
{
x_bootmem_size = memparse(str, &str);
x_bootmem = alloc_bootmem(x_bootmem_size);
printk(“Reserved %lu bytes from %p for xn”, x_bootmem_size, x_bootmem);
return 1;
}
__setup(“x-bootmem=”, x_bootmem_setup);
可見其應用還是比較簡單的,不過利弊總是共生的,它不可避免也有其自身的限制:
記憶體申請代碼隻能連接配接進核心,不能在子產品中使用。
被申請的記憶體不會被頁配置設定器和 slab 配置設定器所使用和統計,也就是說它處于系統的可見内
存之外,即使在将來的某個地方你釋放了它。
一般使用者隻會申請一大塊記憶體,如果需要在其上實作複雜的記憶體管理則需要自己實作。
在不允許記憶體配置設定失敗的場合,通過啟動記憶體預留記憶體空間将是我們唯一的選擇。
4) 使用者程序間通信主要哪幾種方式?(1)管道(Pipe):管道可用于具有親緣關系程序間的通信,允許一個程序和另一個與它有共同祖
先的程序之間進行通信。
(2)命名管道(named pipe):命名管道克服了管道沒有名字的限制,是以,除具有管道所具有的
功能外,它還允許無親緣關系程序間的通信。命名管道在檔案系統中有對應的檔案名。命名
管道通過指令 mkfifo 或系統調用 mkfifo 來建立。
(3)信号(Signal):信号是比較複雜的通信方式,用于通知接受程序有某種事件發生,除了用于進
程間通信外,程序還可以發送信号給程序本身;linux 除了支援 Unix 早期信号語義函數 sigal
外,還支援語義符合 Posix.1 标準的信号函數 sigaction(實際上,該函數是基于 BSD 的,BSD
為了實作可靠信号機制,又能夠統一對外接口,用 sigaction 函數重新實作了 signal 函數)。
(4)消息(Message)隊列:消息隊列是消息的連結表,包括 Posix 消息隊列 system V 消息隊
列。有足夠權限的程序可以向隊列中添加消息,被賦予讀權限的程序則可以讀走隊列中的消
息。消息隊列克服了信号承載資訊量少,管道隻能承載無格式位元組流以及緩沖區大小受限等
缺
(5)共享記憶體:使得多個程序可以通路同一塊記憶體空間,是最快的可用 IPC 形式。是針對其他
通信機制運作效率較低而設計的。往往與其它通信機制,如信号量結合使用,來達到程序間
的同步及互斥。
(6)信号量(semaphore):主要作為程序間以及同一程序不同線程之間的同步手段。
(7)套接字(Socket):更為一般的程序間通信機制,可用于不同機器之間的程序間通信。起初是
由 Unix 系統的 BSD 分支開發出來的,但現在一般可以移植到其它類 Unix 系統上:Linux 和
System V 的變種都支援套接字。
5) 通過夥伴系統申請核心記憶體的函數有哪些?在實體頁面管理上實作了基于區的夥伴系統(zone based buddy system)。對不同區的記憶體
使用單獨的夥伴系統(buddy system)管理,而且獨立地監控空閑頁。相應接口
alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。
6) Linux 虛拟檔案系統的關鍵資料結構有哪些?(至少寫出四個)struct super_block,struct inode,struct file,struct dentry;
7) 對檔案或裝置的操作函數儲存在那個資料結構中?struct file_operations
8) Linux 中的檔案包括哪些?執行檔案,普通檔案,目錄檔案,連結檔案和裝置檔案,管道檔案。
9) 建立程序的系統調用有那些?clone(),fork(),vfork();系統調用服務例程:sys_clone,sys_fork,sys_vfork;
10) 調用 schedule()進行程序切換的方式有幾種?1.系統調用 do_fork();
2.定時中斷 do_timer();
3.喚醒程序 wake_up_process
4.改變程序的排程政策 setscheduler();
5.系統調用禮讓 sys_sched_yield();