說起共享內存,一般來說會讓人想起下面一些方法:
1、多線程。線程之間的內存都是共享的。更確切的說,屬於同一進程的線程使用的是同一個地址空間,而不是在不同地址空間之間進行內存共享;
2、父子進程間的內存共享。父進程以MAP_SHARED|MAP_ANONYMOUS選項mmap一塊匿名內存,fork之后,其子孫進程之間就能共享這塊內存。這種共享內存由於受到進程父子關系的限制,一般較少使用;
3、mmap文件。多個進程mmap到同一個文件,實際上就是大家在共享文件page cache中的內存。不過文件牽涉到磁盤的讀寫,用來做共享內存顯然十分笨重,所以就有了不跟磁盤扯上關系的內存文件,也就是我們這里要討論的tmpfs和shmem;
tmpfs是一套虛擬的文件系統,在其中創建的文件都是基於內存的,機器重啟即消失。
shmem是一套ipc,通過相應的ipc系統調用shmget能夠以指定key創建一塊的共享內存。需要使用這塊內存的進程可以通過shmat系統調用來獲得它。
雖然是兩套不同的接口,但是在內核里面的實現卻是同一套。shmem內部掛載了一個tmpfs分區(用戶不可見),shmget就是在該分區下獲取名為"SYSV${key}"的文件。然后shmat就相當於mmap這個文件。
所以我們接下來就把tmpfs和shmem當作同一個東西來討論了。
tmpfs/shmem是一個介於文件和匿名內存之間的東西。
一方面,它具有文件的屬性,能夠像操作文件一樣去操作它。它有自己inode、有自己的page cache;
另一方面,它也有匿名內存的屬性。由於沒有像磁盤這樣的外部存儲介質,內核在內存緊缺時不能簡單的將page從它們的page cache中丟棄,而需要swap-out;(參閱《linux頁面回收淺析》)
對tmpfs/shmem內存的讀寫,就是對page cache中相應位置的page所代表的內存進行讀寫,這一點跟普通的文件映射沒有什么不同。
如果進程地址空間的相應位置尚未映射,則會建立到page cache中相應page的映射;
如果page cache中的相應位置還沒有分配page,則會分配一個。當然,由於不存在磁盤上的源數據,新分配的page總是空的(特別的,通過read系統調用去讀一個尚未分配page的位置時,並不會分配新的page,而是共享ZERO_PAGE);
如果page cache中相應位置的page被回收了,則會先將其恢復;
對於第三個“如果”,tmpfs/shmem和普通文件的page回收及其恢復方式是不同的:
page回收時,跟普通文件的情況一樣,內核會通過prio_tree反向映射找到映射這個page的每一個page table,然后將其中對應的pte清空。
不同之處是普通文件的page在確保與磁盤同步(如果page為臟的話需要刷回磁盤)之后就可以丟棄了,而對於tmpfs/shmem的page則需要進行swap-out。
注意,匿名page在被swap-out時,並不是將映射它的pte清空,而是得在pte上填寫相應的swap_entry,以便知道page被換出到哪里去,否則再需要這個page的時候就沒法swap-in了。
而tmpfs/shmem的page呢?page table中對應的pte被清空,swap_entry會被存放在page cache的radix_tree的對應slot上。
等下一次訪問觸發page fault時,page需要恢復。
普通文件的page恢復跟page未分配時的情形一樣,需要新分配page、然后根據映射的位置重新從磁盤讀出相應的數據;
而tmpfs/shmem則是通過映射的位置找到radix_tree上對應的slot,從中得到swap_entry,從而進行swap-in,並將新的page放回page cache;
這里就有個問題了,在page cache的radix_tree的某個slot上,怎么知道里面存放着的是正常的page?還是swap-out后留下的swap_entry?
如果是swap_entry,那么slot上的值將被加上RADIX_TREE_EXCEPTIONAL_ENTRY標記(值為2)。swap_entry的值被左移兩位后OR上RADIX_TREE_EXCEPTIONAL_ENTRY,填入slot。
也就是說,如果${slot} & RADIX_TREE_EXCEPTIONAL_ENTRY != 0,則它代表swap_entry,且swap_entry的值是${slot} >> 2;否則它代表page,${slot}就是指向page的指針,當然其值可能是NULL,說明page尚未分配。
那么顯然,page的地址值其末兩位肯定是0,否則就可能跟RADIX_TREE_EXCEPTIONAL_ENTRY標記沖突了;而swap_entry的值最大只能是30bit或62bit(對應32位或64位機器),否則左移兩位就溢出了。
最后以一張圖說明一下匿名page、文件映射page、tmpfs/shmem page的回收及恢復過程: