第6章 Posix共享內存區
6.1 共享內存區
共享內存區是可用IPC形式中最快的,只有映射和解除映射需要進入內核的系統調用,映射後對共享內存區的訪問和修改不再需要系統調用(內核只要負責好頁表映射和處理頁面故障即可),但通常需要同步手段。
一個客戶-服務器間傳遞文件數據的例子中,FIFO或消息隊列等IPC方式需要4次內核-進程間的數據複製,每次都需要切換地址空間,開銷很大;共享內存區只需要2次跨內核的數據複製。
6.2 mmap
mmap的三個目的:
1. 使用普通文件以提供內存映射I/O。
2. 使用特殊文件以提供匿名內存映射。
3. 使用shm_open以提供Posix共享內存區。
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
addr:調用者希望的起始地址,通常爲空,表示由內核選擇。
len:映射區大小。
prot:映射區的保護模式,PROT_READ | PROT_WRITE | PROT_EXEC | PROT_NONE。
flags:共享模式,MAP_SHARED和MAP_PRIVATE指定對映射區的數據修改會不會影響到底層支撐對象上;MAP_FIXED表示指定addr爲起始地址。
fd和offset:用於支撐的底層文件範圍。
mmap成功返回後可以關閉fd,不會影響映射。映射後對共享內存區的訪問就被納入到操作系統的虛擬內存的管理下,因此不再需要系統調用。
munmap用於從某個進程的地址空間刪除一個映射關係。
msync用於同步一個指定了MAP_SHARED標誌的映射區與支撐文件數據。
終端和套接字不能進行映射。
如果指定了MAP_SHARED,映射文件的實際內容就是共享內存區的初始內容。
6.3 匿名內存映射
如果只想在父子進程間使用共享內存區,可以用匿名內存映射來簡化操作:
1. 4.4BSD匿名內存映射:將mmap的flags指定成MAP_SHARED | MAP_ANON,fd爲-1。
2. SVR4:打開特殊設備/dev/zero,將得到的fd用於mmap。
6.4 映射區長度與文件大小
mmap返回的地址一般是頁對齊的,映射區最後不滿一頁的部分可訪問,但對它的修改不會寫入文件,對超出映射區所在的頁的訪問會產生段錯誤。
若映射區大小超出文件大小,則訪問在映射區內但超出文件範圍的頁面時會引發總線錯誤。
映射過程中如果底層文件大小改變,也會同樣改變映射區的可訪問範圍。
6.5 Posix共享內存區
兩種無親緣關係進程間共享內存的方法:
1. 內存映射文件:open一個普通文件,用它的fd進行mmap。
2. 共享內存區對象:shm_open一個IPC名字,用它的fd進行mmap。
6.6 功能函數
int shm_open(const char *name, int oflag, mode_t mode);
創建或打開一個Posix共享內存區對象。Posix沒有指定一個新建的共享內存區對象的初始內容。
int shm_unlink(const char *name);
引用計數刪除。
int ftruncate(int fd, off_t length);
改變文件或共享內存區對象的大小。
int fstat(int fd, struct stat *buf);
獲取文件信息,對共享內存區對象只有4個成員有信息:
st_mode:讀寫權限;
st_uid、st_gid:所有者身份;
st_size:共享內存區大小。