轉自:
http://www.linuxgraphics.cn/gui/ipc_shrmem.html
共享內存簡介
共享內存可以說是最有用的進程間通信方式,也是最快的IPC形式。兩個不同進程A、B共享內存的意思是,同一塊物理內存被映射到進程A、B各自的進程地址空間。進程A可以即時看到進程B對共享內存中數據的更新,反之亦然。由於多個進程共享同一塊內存區域,必然需要某種同步機制,互斥鎖和信號量都可以。1
採用共享內存通信的一個顯而易見的好處是效率高,因爲進程可以直接讀寫內存,而不需要任何數據的拷貝。對於像管道和消息隊列等通信方式,則需要在內核和用戶空間進行四次的數據拷貝,而共享內存則只拷貝兩次數據1:一次從輸入文件到共享內存區,另一次從共享內存區到輸出文件。實際上,進程之間在共享內存時,並不總是讀寫少量數據後就解除映射,有新的通信時,再重新建立共享內存區域。而是保持共享區域,直到通信完畢爲止,這樣,數據內容一直保存在共享內存中,並沒有寫回文件。共享內存中的內容往往是在解除映射時才寫回文件的。因此,採用共享內存的通信方式效率是非常高的。1
本文介紹了共享內存的兩種方法,第一種是通過 mmap 實現共享內存,第二種是 System V 進程間通信機制。
mmap 方式
mmap()系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間後,進程可以向訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。
實際上,mmap()系統調用並不是完全爲了用於共享內存而設計的。它本身提供了不同於一般對普通文件的訪問方式,進程可以像讀寫內存一樣對普通文件的操作。而Posix或系統V的共享內存IPC則純粹用於共享目的,當然 mmap() 實現共享內存也是其主要應用之一。
下圖是 mmap 將文件映射到進程地址空間的示意圖3:
mmap 示意圖 |
創建文件並將其 mmap 到地址空間的主要流程如下所示:
int fd; void* memptr; /* create a tmp file.*/ fd = open ("/var/tmp/mmapfile", O_WRONLY | O_CREAT | O_TRUNC, 0644); /* Write data to file */ write (fd, &data, sizeof (data)); /* mmap file to address space of calling process*/ memptr = mmap (0, sizeof (data), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ...... /* Delete the mapping .*/ unlink ("/var/tmp/mmapfile"); munmap (memptr, sizeof (data));
映射文件到自己的地址空間的主要流程如下所示:
int fd; void* memptr; fd = open ("/var/tmp/mmapfile", O_RDONLY); size = lseek (fd, 0, SEEK_END); memptr = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0); ...... unlink ("/var/tmp/mmapfile"); munmap (memptr, size);
System V 共享內存
System V 共享內存是通過映射特殊文件系統 shm 中的文件實現進程間的共享內存通信,即把所有共享數據放在共享內存區域 (IPC shared memory region),任何想要訪問該數據的進程都必須在本進程的地址空間新增一塊內存區域,用來映射存放共享數據的物理內存頁面。2
創建共享內存的主要流程如下面的僞代碼所示:
key_t shnm_key; int shmid; void* memptr; /* get a shm key */ shm_key = ftok (filename, 0); /* allocate a shared memory segment */ shmid = shmget (shm_key, sizem SHM_PARAM | IPC_CREAT | IPC_EXCL); /* Attach the shared memory segment identified by shmid to the address space of calling process */ memptr = shmat (shmid, 0, 0); /* write data to shared memory */ memcpy (memptr, &data, sizeof (data)); /* Mark the segment to be destroyed. The segment will only actually be destroyed after the last process detached it. */ shmctl (shmid, IPC_RMID, NULL); ....... /* Detached the shared memory segment from the address space of calling process. */ shmdt (memptr);
attach 共享內存到 calling process 的流程如下所示:
void* memptr; get shmid from shared memory segment creator /* Attach shared memory segment to the address space of calling process.*/ memptr = shmat(shmid, 0, SHM_RDONLY); ... ... /* Detached the shared memory segment.*/ shmdt (memptr);
See Also
在 IBM Developworks 上有兩篇文章對 mmap 和 System V 這兩種共享內存方式進行了詳細描述和比較:
- Linux 環境進程間通信(五):共享內存 (上)
- Linux 環境進程間通信(五):共享內存 (下)
- mmap 的示意圖來自 http://www.bitctrl.com/qnx/qnx6_sysarch/qnx6_sysarch_2b.htm
- mmap、munmap、shmget, shmat, shmctl, shmdt 手冊
- sharedmem.tar.gz 是我整理的一個共享內存的示例代碼。