1. 程式人生 > >核心與使用者態程式共享記憶體的方法

核心與使用者態程式共享記憶體的方法

一、首先獲取一塊物理上連續的實體記憶體

有多種方法。

(a)通過kernel命令列引數預留一些記憶體

這種方法,適合於需要大塊的物理連續的記憶體。

假設實體記憶體總量為256M。命令列引數中,指定 mem=224M。即只讓核心使用前224M記憶體,忽略其餘的記憶體。

這樣,我們就有了32M的記憶體可用,記憶體起始實體地址為224*1024*1024。

在核心態,通過ioremap,就可以將此實體地址處的記憶體對映到核心空間。

不過,這種方法好像在X86_64架構下會有問題。在arm上,則沒有發現問題。

有高手知道原因,還望指導一下^_^

(b)直接從核心申請一塊記憶體

通過__get_free_pages(GFP_KERNEL,n); 從核心申請n個記憶體頁。n好像必須是2的整數次方。

二、將記憶體對映到使用者空間

通過上面的工作,核心中已經得到了此記憶體的虛擬地址,當然這是核心空間的虛擬地址。我們假設此地址儲存在了void  *rsv_mem變數中。

接下來把他對映到使用者空間。這個工作分兩部分。一部分是核心態的工作,一部分是使用者態的工作。

1. 核心態的工作

在核心態呼叫misc_register註冊一個裝置,此裝置的其他方面,我們就不說了。

只說說file_operations中的mmap成員的實現。這個實現很簡單,程式碼如下:

static int soft_wdt_mmap(struct file *filp, struct vm_area_struct *vma)
 {
     unsigned long pfn = (virt_to_phys(rsv_mem) >> PAGE_SHIFT);

     if (remap_pfn_range(vma, vma->vm_start, pfn,
                vma->vm_end - vma->vm_start,
                vma->vm_page_prot))
         return -EAGAIN;

     return 0;
 }

2. 使用者態的工作

假設記憶體大小是8192位元組。


    int fd=open("/dev/my_dev", O_RDWR);
    void *ptr_dev_mem==mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

好了,這就完成了使用者態的映射了。

實際上,要想方便的應用記憶體共享,實現高效能的業務處理,還有一些工作要做。
以生產者、消費者為例,可以這樣實現具體的應用。
核心態驅動將這塊對映好的大記憶體分成很多塊小的buffer,放到一個buffer pool中管理起來。
同時,核心態驅動建立一個佇列,裡面存放待消費的內容。
然後,核心態的驅動需要實現poll函式,以便使用者態程式能夠使用poll或select查詢有沒有待消費的內容。
另外,核心驅動還需要實現一組ioctl函式,以便使用者態獲取佇列中的buffer資訊。
最後,在核心態中建立一個生產者執行緒,他不斷的從buffer pool中申請一個buffer,填入資料後,將他送入佇列。


使用者態程式實現消費者。
消費者開啟裝置檔案,並完成記憶體對映後,通過select或poll操作,就知道當前有沒有待消費的內容。
若有,則可以通過ioctl系統呼叫(可以定義一個無阻塞的命令),進入核心態,核心態則將佇列首部的buffer做出隊操作,並返回此buffer的資訊(例如,核心態根據buffer的核心態地址返回此buffer的索引號)。使用者態就可以根據返回的資訊,得到此buffer的使用者態地址,進行消費。消費之後,再通過ioctl系統呼叫,告訴核心將此buffer釋放回buffer pool。核心與使用者態之間,還可以定義一個阻塞模式的ioctl命令,使用者態用此命令獲取buffer,若當前佇列無buffer,則將消費者掛起,直到有buffer入列了,或者消費者收到了訊號。