Android系統匿名共享記憶體Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃

broadviewbj發表於2012-10-31
在Android系統中,提供了獨特的匿名共享記憶體子系統Ashmem(Anonymous Shared Memory),它以驅動程式的形式實現在核心空間中。它有兩個特點,一是能夠輔助記憶體管理系統來有效地管理不再使用的記憶體塊,二是它透過Binder程式間通訊機制來實現程式間的記憶體共享。本文中,我們將透過例項來簡要介紹Android系統的匿名共享記憶體的使用方法,使得我們對Android系統的匿名共享記憶體機制有一個感性的認識,為進一步學習它的原始碼實現打下基礎。

        Android系統的匿名共享記憶體子系統的主體是以驅動程式的形式實現在核心空間的,同時,在系統執行時庫層和應用程式框架層提供了訪問介面,其中,在系統執行時庫層提供了C/C++呼叫介面,而在應用程式框架層提供了Java呼叫介面。這裡,我們將直接透過應用程式框架層提供的Java呼叫介面來說明匿名共享記憶體子系統Ashmem的使用方法,畢竟我們在Android開發應用程式時,是基於Java語言的,而實際上,應用程式框架層的Java呼叫介面是透過JNI方法來呼叫系統執行時庫層的C/C++呼叫介面,最後進入到核心空間的Ashmem驅動程式去的。

        我們在這裡舉的例子是一個名為Ashmem的應用程式,它包含了一個Server端和一個Client端實現,其中,Server端是以Service的形式實現的,在這裡Service裡面,建立一個匿名共享記憶體檔案,而Client是一個Activity,這個Activity透過Binder程式間通訊機制獲得前面這個Service建立的匿名共享記憶體檔案的控制程式碼,從而實現共享。在Android應用程式框架層,提供了一個MemoryFile介面來封裝了匿名共享記憶體檔案的建立和使用,它實現在frameworks/base/core/java/android/os/MemoryFile.java檔案中。下面,我們就來看看Server端是如何透過MemoryFile類來建立匿名共享記憶體檔案的以及Client是如何獲得這個匿名共享記憶體檔案的控制程式碼的。

        在MemoryFile類中,提供了兩種建立匿名共享記憶體的方法,我們透過MemoryFile類的建構函式來看看這兩種使用方法:

  1. public class MemoryFile  
  2. {  
  3.     ......  
  4.   
  5.     /** 
  6.     * Allocates a new ashmem region. The region is initially not purgable. 
  7.     * 
  8.     * @param name optional name for the file (can be null). 
  9.     * @param length of the memory file in bytes. 
  10.     * @throws IOException if the memory file could not be created. 
  11.     */  
  12.     public MemoryFile(String name, int length) throws IOException {  
  13.         mLength = length;  
  14.         mFD = native_open(name, length);  
  15.         mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);  
  16.         mOwnsRegion = true;  
  17.     }  
  18.   
  19.     /** 
  20.     * Creates a reference to an existing memory file. Changes to the original file 
  21.     * will be available through this reference. 
  22.     * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail. 
  23.     * 
  24.     * @param fd File descriptor for an existing memory file, as returned by 
  25.     *        {@link #getFileDescriptor()}. This file descriptor will be closed 
  26.     *        by {@link #close()}. 
  27.     * @param length Length of the memory file in bytes. 
  28.     * @param mode File mode. Currently only "r" for read-only access is supported. 
  29.     * @throws NullPointerException if fd is null. 
  30.     * @throws IOException If fd does not refer to an existing memory file, 
  31.     *         or if the file mode of the existing memory file is more restrictive 
  32.     *         than mode. 
  33.     * 
  34.     * @hide 
  35.     */  
  36.     public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {  
  37.         if (fd == null) {  
  38.             throw new NullPointerException("File descriptor is null.");  
  39.         }  
  40.         if (!isMemoryFile(fd)) {  
  41.             throw new IllegalArgumentException("Not a memory file.");  
  42.         }  
  43.         mLength = length;  
  44.         mFD = fd;  
  45.         mAddress = native_mmap(mFD, length, modeToProt(mode));  
  46.         mOwnsRegion = false;  
  47.     }  
  48.   
  49.     ......  
  50. }  
  1. public class MemoryFile  
  2. {  
  3.     ......  
  4.   
  5.     /** 
  6.     * Allocates a new ashmem region. The region is initially not purgable. 
  7.     * 
  8.     * @param name optional name for the file (can be null). 
  9.     * @param length of the memory file in bytes. 
  10.     * @throws IOException if the memory file could not be created. 
  11.     */  
  12.     public MemoryFile(String name, int length) throws IOException {  
  13.         mLength = length;  
  14.         mFD = native_open(name, length);  
  15.         mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);  
  16.         mOwnsRegion = true;  
  17.     }  
  18.   
  19.     /** 
  20.     * Creates a reference to an existing memory file. Changes to the original file 
  21.     * will be available through this reference. 
  22.     * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail. 
  23.     * 
  24.     * @param fd File descriptor for an existing memory file, as returned by 
  25.     *        {@link #getFileDescriptor()}. This file descriptor will be closed 
  26.     *        by {@link #close()}. 
  27.     * @param length Length of the memory file in bytes. 
  28.     * @param mode File mode. Currently only "r" for read-only access is supported. 
  29.     * @throws NullPointerException if fd is null. 
  30.     * @throws IOException If fd does not refer to an existing memory file, 
  31.     *         or if the file mode of the existing memory file is more restrictive 
  32.     *         than mode. 
  33.     * 
  34.     * @hide 
  35.     */  
  36.     public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {  
  37.         if (fd == null) {  
  38.             throw new NullPointerException("File descriptor is null.");  
  39.         }  
  40.         if (!isMemoryFile(fd)) {  
  41.             throw new IllegalArgumentException("Not a memory file.");  
  42.         }  
  43.         mLength = length;  
  44.         mFD = fd;  
  45.         mAddress = native_mmap(mFD, length, modeToProt(mode));  
  46.         mOwnsRegion = false;  
  47.     }  
  48.   
  49.     ......  
  50. }  
        從註釋中,我們可以看出這兩個建構函式的使用方法,這裡就不再詳述了。兩個建構函式的主要區別是第一個引數,第一種構造方法是以指定的字串呼叫JNI方法native_open來建立一個匿名共享記憶體檔案,從而得到一個檔案描述符,接著就以這個檔案描述符為引數呼叫JNI方法natvie_mmap把這個匿名共享記憶體檔案對映在程式空間中,然後就可以透過這個對映後得到的地址空間來直接訪問記憶體資料了;第二種構造方法是以指定的檔案描述符來直接呼叫JNI方法natvie_mmap把這個匿名共享記憶體檔案對映在程式空間中,然後進行訪問,而這個檔案描述符就必須要是一個匿名共享記憶體檔案的檔案描述符,這是透過一個內部函式isMemoryFile來驗證的,而這個內部函式isMemoryFile也是透過JNI方法呼叫來進一步驗證的。前面所提到的這些JNI方法呼叫,最終都是透過系統執行時庫層進入到核心空間的Ashmem驅動程式中去,不過這裡我們不關心這些JNI方法、系統執行庫層呼叫以及Ashmem驅動程式的具體實現,在接下來的兩篇文章中,我們將會著重介紹,這裡我們只關注MemoryFile這個類的使用方法。

        前面我們說到,我們在這裡舉的例子包含了一個Server端和一個Client端實現,其中, Server端就是透過前面一個建構函式來建立一個匿名共享記憶體檔案,接著,Client端過Binder程式間通訊機制來向Server請求這個匿名共享記憶體的檔案描述符,有了這個檔案描述符之後,就可以透過後面一個建構函式來共享這個記憶體檔案了。

        因為涉及到Binder程式間通訊,我們首先定義好Binder程式間通訊介面。Binder程式間通訊機制的相關介紹,請參考前面一篇文章Android程式間通訊(IPC)機制Binder簡要介紹和學習計劃,這裡就不詳細介紹了,直接進入主題。
        首先在原始碼工程的packages/experimental目錄下建立一個應用程式工程目錄Ashmem。關於如何獲得Android原始碼工程,請參考在Ubuntu上下載、編譯和安裝Android最新原始碼一文;關於如何在Android原始碼工程中建立應用程式工程,請參考在Ubuntu上為Android系統內建Java應用程式測試Application Frameworks層的硬體服務一文。這裡,工程名稱就是Ashmem了,它定義了一個路徑為shy.luo.ashmem的package,這個例子的原始碼主要就是實現在這裡了。下面,將會逐一介紹這個package裡面的檔案。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/13164110/viewspace-747971/,如需轉載,請註明出處,否則將追究法律責任。

相關文章