Linux核心空間對映到使用者空間
一:簡介
共享記憶體可以說是最有用的程序間通訊方式,也是最快的IPC形式。兩個不同程序A、B共享記憶體的意思是,同一塊實體記憶體被對映到程序A、B各自的程序地址空間。程序A可以即時看到程序B對共享記憶體中資料的更新,反之亦然。由於多個程序共享同一塊記憶體區域,必然需要某種同步機制,互斥鎖和訊號量都可以。
採用共享記憶體通訊的一個顯而易見的好處是效率高,因為程序可以直接讀寫記憶體,而不需要任何資料的拷貝。對於像管道和訊息佇列等通訊方式,則需要在核心和使用者空間進行四次的資料拷貝,而共享記憶體則只拷貝兩次資料。一次從輸入檔案到共享記憶體區,另一次從共享記憶體區到輸出檔案。實際上,程序之間在共享記憶體時,並不總是讀寫少量資料後就解除對映,有新的通訊時,再重新建立共享記憶體區域。而是保持共享區域,直到通訊完畢為止,這樣,資料內容一直儲存在共享記憶體中,並沒有寫回檔案。共享記憶體中的內容往往是在解除對映時才寫回檔案的。因此,採用共享記憶體的通訊方式效率是非常高的。
二:例項應用
linux的2.2.x核心支援多種共享記憶體方式,如mmap()系統呼叫,Posix共享記憶體,以及系統V共享記憶體。當核心空間和使用者空間存在大量資料互動時, 共享記憶體對映就成了這種情況下的不二選擇; 它能夠最大限度的降低核心空間和使用者空間之間的資料拷貝, 從而大大提高系統的效能.
以下是建立從核心空間到使用者空間的共享記憶體對映的模板程式碼(在核心2.6.18和2.6.32上測試通過):
1.核心空間分配記憶體
[cpp] view plain copy print?-
#include <linux/types.h>
- #include <linux/mm.h>
- #include <linux/vmalloc.h>
- int mmap_alloc(int require_buf_size)
- {
- struct page *page;
- mmap_size = PAGE_ALIGN(require_buf_size);
- #if USE_KMALLOC //for kmalloc
- mmap_buf = kzalloc(mmap_size, GFP_KERNEL);
- if (!mmap_buf) {
- return -1;
- }
-
for (page = virt_to_page(mmap_buf ); page < virt_to_page(mmap_buf + mmap_size); page++) {
- SetPageReserved(page);
- }
- #else //for vmalloc
- mmap_buf = vmalloc(mmap_size);
- if (!mmap_buf ) {
- return -1;
- }
- for (i = 0; i < mmap_size; i += PAGE_SIZE) {
- SetPageReserved(vmalloc_to_page((void *)(((unsigned long)mmap_buf) + i)));
- }
- #endif
- return 0;
- }
2.使用者空間對映記憶體
- int test_mmap()
- {
- mmap_fd = open("/dev/mmap_dev", O_RDWR);
- mmap_ptr = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, mmap_fd, 0);
- if (mmap_ptr == MAP_FAILED) {
- return -1;
- }
- return 0;
- }
3.核心空間對映記憶體: 實現file_operations的mmap函式
- staticint mmap_mmap(struct file *filp, struct vm_area_struct *vma)
- {
- int ret;
- unsigned long pfn;
- unsigned long start = vma->vm_start;
- unsigned long size = PAGE_ALIGN(vma->vm_end - vma->vm_start);
- if (size > mmap_size || !mmap_buf) {
- return -EINVAL;
- }
- #if USE_KMALLOC
- return remap_pfn_range(vma, start, (virt_to_phys(mmap_buf) >> PAGE_SHIFT), size, PAGE_SHARED);
- #else
- /* loop over all pages, map it page individually */
- while (size > 0) {
- pfn = vmalloc_to_pfn(mmap_buf);
- if ((ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) < 0) {
- return ret;
- }
- start += PAGE_SIZE;
- mmap_buf += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- #endif
- return 0;
- }
- staticconststruct file_operations mmap_fops = {
- .owner = THIS_MODULE,
- .ioctl = mmap_ioctl,
- .open = mmap_open,
- .mmap = mmap_mmap,
- .release = mmap_release,
- };
4.使用者空間撤銷記憶體對映
- void test_munmap()
- {
- munmap(mmap_ptr, mmap_size);
- close(mmap_fd);
- }
5.核心空間釋放記憶體; 必須在使用者空間執行munmap系統呼叫後才能釋放
- void mmap_free()
- {
- #if USE_KMALLOC
- struct page *page;
- for (page = virt_to_page(mmap_buf); page < virt_to_page(mmap_buf + mmap_size); page++) {
- ClearPageReserved(page);
- }
- kfree(mmap_buf);
- #else
- int i;
- for (i = 0; i < mmap_size; i += PAGE_SIZE) {
- ClearPageReserved(vmalloc_to_page((void *)(((unsigned long)mmap_buf) + i)));
- }
- vfree(mmap_buf);
- #endif
- mmap_buf = NULL;
- }