Android系統匿名共享記憶體Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
在Android系統中,提供了獨特的匿名共享記憶體子系統Ashmem(Anonymous Shared Memory),它以驅動程式的形式實現在核心空間中。它有兩個特點,一是能夠輔助記憶體管理系統來有效地管理不再使用的記憶體塊,二是它通過Binder程序間通訊機制來實現程序間的記憶體共享。本文中,我們將通過例項來簡要介紹Android系統的匿名共享記憶體的使用方法,使得我們對Android系統的匿名共享記憶體機制有一個感性的認識,為進一步學習它的原始碼實現打下基礎。
《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!
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類的建構函式來看看這兩種使用方法:
public class MemoryFile{ ...... /** * Allocates a new ashmem region. The region is initially not purgable. * * @param name optional name for the file (can be null). * @param length of the memory file in bytes. * @throws IOException if the memory file could not be created. */ public MemoryFile(String name, int length) throws IOException { mLength = length; mFD = native_open(name, length); mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); mOwnsRegion = true; } /** * Creates a reference to an existing memory file. Changes to the original file * will be available through this reference. * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail. * * @param fd File descriptor for an existing memory file, as returned by * {@link #getFileDescriptor()}. This file descriptor will be closed * by {@link #close()}. * @param length Length of the memory file in bytes. * @param mode File mode. Currently only "r" for read-only access is supported. * @throws NullPointerException if <code>fd</code> is null. * @throws IOException If <code>fd</code> does not refer to an existing memory file, * or if the file mode of the existing memory file is more restrictive * than <code>mode</code>. * * @hide */ public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException { if (fd == null) { throw new NullPointerException("File descriptor is null."); } if (!isMemoryFile(fd)) { throw new IllegalArgumentException("Not a memory file."); } mLength = length; mFD = fd; mAddress = native_mmap(mFD, length, modeToProt(mode)); mOwnsRegion = false; } ......}
從註釋中,我們可以看出這兩個建構函式的使用方法,這裡就不再詳述了。兩個建構函式的主要區別是第一個引數,第一種構造方法是以指定的字串呼叫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裡面的檔案。
這裡要用到的Binder程序間通訊介面定義在src/shy/luo/ashmem/IMemoryService.java檔案中:
package shy.luo.ashmem;import android.util.Log;import android.os.IInterface;import android.os.Binder;import android.os.IBinder;import android.os.Parcel;import android.os.ParcelFileDescriptor;import android.os.RemoteException;public interface IMemoryService extends IInterface { public static abstract class Stub extends Binder implements IMemoryService { private static final String DESCRIPTOR = "shy.luo.ashmem.IMemoryService"; public Stub() { attachInterface(this, DESCRIPTOR); } public static IMemoryService asInterface(IBinder obj) { if (obj == null) { return null; } IInterface iin = (IInterface)obj.queryLocalInterface(DESCRIPTOR); if (iin != null && iin instanceof IMemoryService) { return (IMemoryService)iin; } return new IMemoryService.Stub.Proxy(obj); } public IBinder asBinder() { return this; } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getFileDescriptor: { data.enforceInterface(DESCRIPTOR); ParcelFileDescriptor result = this.getFileDescriptor(); reply.writeNoException(); if (result != null) { reply.writeInt(1); result.writeToParcel(reply, 0); } else { reply.writeInt(0); } return true; } case TRANSACTION_setValue: { data.enforceInterface(DESCRIPTOR); int val = data.readInt(); setValue(val); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements IMemoryService { private IBinder mRemote; Proxy(IBinder remote) { mRemote = remote; } public IBinder asBinder() { return mRemote; } public String getInterfaceDescriptor() { return DESCRIPTOR; } public ParcelFileDescriptor getFileDescriptor() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); ParcelFileDescriptor result; try { data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getFileDescriptor, data, reply, 0); reply.readException(); if (0 != reply.readInt()) { result = ParcelFileDescriptor.CREATOR.createFromParcel(reply); } else { result = null; } } finally { reply.recycle(); data.recycle(); } return result; } public void setValue(int val) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(DESCRIPTOR); data.writeInt(val); mRemote.transact(Stub.TRANSACTION_setValue, data, reply, 0); reply.readException(); } finally { reply.recycle(); data.recycle(); } } } static final int TRANSACTION_getFileDescriptor = IBinder.FIRST_CALL_TRANSACTION + 0; static final int TRANSACTION_setValue = IBinder.FIRST_CALL_TRANSACTION + 1; } public ParcelFileDescriptor getFileDescriptor() throws RemoteException; public void setValue(int val) throws RemoteException;}
這裡主要是定義了IMemoryService介面,它裡面有兩個呼叫介面: public ParcelFileDescriptor getFileDescriptor() throws RemoteException; public void setValue(int val) throws RemoteException;
同時,還分別定義了用於Server端實現的IMemoryService.Stub基類和用於Client端使用的代理IMemoryService.Stub.Proxy類。關於Binder程序間通訊機制在應用程式框架層的Java介面定義,請參考前面Android系統程序間通訊Binder機制在應用程式框架層的Java介面原始碼分析一文。有了Binder程序間通訊介面之後,接下來就是要在Server端實現一個本地服務了。這裡,Server端實現的本地服務名為MemoryService,實現在src/shy/luo/ashmem/MemoryService.java檔案中:
package shy.luo.ashmem;import java.io.FileDescriptor;import java.io.IOException;import android.os.Parcel;import android.os.MemoryFile;import android.os.ParcelFileDescriptor;import android.util.Log;public class MemoryService extends IMemoryService.Stub { private final static String LOG_TAG = "shy.luo.ashmem.MemoryService"; private MemoryFile file = null; public MemoryService() { try { file = new MemoryFile("Ashmem", 4); setValue(0); } catch(IOException ex) { Log.i(LOG_TAG, "Failed to create memory file."); ex.printStackTrace(); } } public ParcelFileDescriptor getFileDescriptor() { Log.i(LOG_TAG, "Get File Descriptor."); ParcelFileDescriptor pfd = null; try { pfd = file.getParcelFileDescriptor(); } catch(IOException ex) { Log.i(LOG_TAG, "Failed to get file descriptor."); ex.printStackTrace(); } return pfd; } public void setValue(int val) { if(file == null) { return; } byte[] buffer = new byte[4]; buffer[0] = (byte)((val >>> 24) & 0xFF); buffer[1] = (byte)((val >>> 16) & 0xFF); buffer[2] = (byte)((val >>> 8) & 0xFF); buffer[3] = (byte)(val & 0xFF); try { file.writeBytes(buffer, 0, 0, 4); Log.i(LOG_TAG, "Set value " + val + " to memory file. "); } catch(IOException ex) { Log.i(LOG_TAG, "Failed to write bytes to memory file."); ex.printStackTrace(); } }}
注意,這裡的MemoryService類實現了IMemoryService.Stub類,表示這是一個Binder服務的本地實現。在建構函式中,通過指定檔名和檔案大小來建立了一個匿名共享記憶體檔案,即建立MemoryFile的一個例項,並儲存在類成員變數file中。這個匿名共享記憶體檔名為"Ashmem",大小為4個節字,剛好容納一個整數,我們這裡舉的例子就是要說明如果建立一個匿名共享記憶體來在兩個程序間實現共享一個整數了。當然,在實際應用中,可以根據需要建立合適大小的共享記憶體來共享有意義的資料。這裡還實現了IMemoryService.Stub的兩個介面getFileDescriptor和setVal,一個用來獲取匿名共享記憶體檔案的檔案描述符,一個來往匿名共享記憶體檔案中寫入一個整數,其中,介面getFileDescriptor的返回值是一個ParcelFileDescriptor。在Java中,是用FileDescriptor類來表示一個檔案描述符的,而ParcelFileDescriptor是用來序列化FileDescriptor的,以便在程序間呼叫時傳輸。 定義好本地服務好,就要定義一個Server來啟動這個服務了。這裡定義的Server實現在src/shy/luo/ashmem/Server.java檔案中:
package shy.luo.ashmem;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;import android.os.ServiceManager;public class Server extends Service { private final static String LOG_TAG = "shy.luo.ashmem.Server"; private MemoryService memoryService = null; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { Log.i(LOG_TAG, "Create Memory Service..."); memoryService = new MemoryService(); try { ServiceManager.addService("AnonymousSharedMemory", memoryService); Log.i(LOG_TAG, "Succeed to add memory service."); } catch (RuntimeException ex) { Log.i(LOG_TAG, "Failed to add Memory Service."); ex.printStackTrace(); } } @Override public void onStart(Intent intent, int startId) { Log.i(LOG_TAG, "Start Memory Service."); } @Override public void onDestroy() { Log.i(LOG_TAG, "Destroy Memory Service."); }}
這個Server繼承了Android系統應用程式框架層提供的Service類,當它被啟動時,執行在一個獨立的程序中。當這個Server被啟動時,它的onCreate函式就會被呼叫,然後它就通過ServiceManager的addService介面來新增MemoryService了: memoryService = new MemoryService(); try { ServiceManager.addService("AnonymousSharedMemory", memoryService); Log.i(LOG_TAG, "Succeed to add memory service."); } catch (RuntimeException ex) { Log.i(LOG_TAG, "Failed to add Memory Service."); ex.printStackTrace(); }
這樣,當這個Server成功啟動了,Client就可以通過ServiceManager的getService介面來獲取這個MemoryService了。接著,我們就來看Client端的實現。Client端是一個Activity,實現在src/shy/luo/ashmem/Client.java檔案中:
package shy.luo.ashmem;import java.io.FileDescriptor;import java.io.IOException;import shy.luo.ashmem.R;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.os.MemoryFile;import android.os.ParcelFileDescriptor;import android.os.ServiceManager;import android.os.RemoteException;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;public class Client extends Activity implements OnClickListener { private final static String LOG_TAG = "shy.luo.ashmem.Client"; IMemoryService memoryService = null; MemoryFile memoryFile = null; private EditText valueText = null; private Button readButton = null; private Button writeButton = null; private Button clearButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); IMemoryService ms = getMemoryService(); if(ms == null) { startService(new Intent("shy.luo.ashmem.server")); } else { Log.i(LOG_TAG, "Memory Service has started."); } valueText = (EditText)findViewById(R.id.edit_value); readButton = (Button)findViewById(R.id.button_read); writeButton = (Button)findViewById(R.id.button_write); clearButton = (Button)findViewById(R.id.button_clear); readButton.setOnClickListener(this); writeButton.setOnClickListener(this); clearButton.setOnClickListener(this); Log.i(LOG_TAG, "Client Activity Created."); } @Override public void onResume() { super.onResume(); Log.i(LOG_TAG, "Client Activity Resumed."); } @Override public void onPause() { super.onPause(); Log.i(LOG_TAG, "Client Activity Paused."); } @Override public void onClick(View v) { if(v.equals(readButton)) { int val = 0; MemoryFile mf = getMemoryFile(); if(mf != null) { try { byte[] buffer = new byte[4]; mf.readBytes(buffer, 0, 0, 4); val = (buffer[0] << 24) | ((buffer[1] & 0xFF) << 16) | ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF); } catch(IOException ex) { Log.i(LOG_TAG, "Failed to read bytes from memory file."); ex.printStackTrace(); } } String text = String.valueOf(val); valueText.setText(text); } else if(v.equals(writeButton)) { String text = valueText.getText().toString(); int val = Integer.parseInt(text); IMemoryService ms = getMemoryService(); if(ms != null) { try { ms.setValue(val); } catch(RemoteException ex) { Log.i(LOG_TAG, "Failed to set value to memory service."); ex.printStackTrace(); } } } else if(v.equals(clearButton)) { String text = ""; valueText.setText(text); } } private IMemoryService getMemoryService() { if(memoryService != null) { return memoryService; } memoryService = IMemoryService.Stub.asInterface( ServiceManager.getService("AnonymousSharedMemory")); Log.i(LOG_TAG, memoryService != null ? "Succeed to get memeory service." : "Failed to get memory service."); return memoryService; } private MemoryFile getMemoryFile() { if(memoryFile != null) { return memoryFile; } IMemoryService ms = getMemoryService(); if(ms != null) { try { ParcelFileDescriptor pfd = ms.getFileDescriptor(); if(pfd == null) { Log.i(LOG_TAG, "Failed to get memory file descriptor."); return null; } try { FileDescriptor fd = pfd.getFileDescriptor(); if(fd == null) { Log.i(LOG_TAG, "Failed to get memeory file descriptor."); return null; } memoryFile = new MemoryFile(fd, 4, "r"); } catch(IOException ex) { Log.i(LOG_TAG, "Failed to create memory file."); ex.printStackTrace(); } } catch(RemoteException ex) { Log.i(LOG_TAG, "Failed to get file descriptor from memory service."); ex.printStackTrace(); } } return memoryFile; }}
Client端的介面主要包含了三個按鈕Read、Write和Clear,以及一個用於顯示內容的文字框。這個Activity在onCreate時,會通過startService介面來啟動我們前面定義的Server程序。呼叫startService時,需要指定要啟動的服務的名稱,這裡就是"shy.luo.ashmem.server"了,後面我們會在程式的描述檔案AndroidManifest.xml看到前面的Server類是如何和名稱"shy.luo.ashmem.server"關聯起來的。關於呼叫startService函式來啟動自定義服務的過程,可以參考Android系統在新程序中啟動自定義服務過程(startService)的原理分析一文。
內部函式getMemoryService用來獲取IMemoryService。如果是第一次呼叫該函式,則會通過ServiceManager的getService介面來獲得這個IMemoryService介面,然後儲存在類成員變數memoryService中,以後再呼叫這個函式時,就可以直接返回memoryService了。
內部函式getMemoryFile用來從MemoryService中獲得匿名共享記憶體檔案的描述符。同樣,如果是第一次呼叫該函式,則會通過IMemoryService的getFileDescriptor介面來獲得MemoryService中的匿名共享記憶體檔案的描述符,然後用這個檔案描述符來建立一個MemoryFile例項,並儲存在類成員變數memoryFile中,以後再呼叫這個函式時,就可以直接返回memoryFile了。
有了memoryService和memoryFile後,我們就可以在Client端訪問Server端建立的匿名共享記憶體了。點選Read按鈕時,就通過memoryFile的readBytes介面把共享記憶體中的整數讀出來,並顯示在文字框中;點選Write按鈕時,就通過memoryService這個代理類的setVal介面來呼叫MemoryService的本地實現類的setVal服務,從而把文字框中的數值寫到Server端建立的匿名共享記憶體中去;點選Clear按鈕時,就會清空文字框的內容。這樣,我們就可以通過Read和Write按鈕來驗證我們是否在Client和Server兩個程序中實現記憶體共享了。
現在,我們再來看看Client介面的配置檔案,它定義在res/layout/main.xml檔案中:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/value"> </TextView> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/edit_value" android:hint="@string/hint"> </EditText> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center"> <Button android:id="@+id/button_read" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/read"> </Button> <Button android:id="@+id/button_write" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/write"> </Button> <Button android:id="@+id/button_clear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/clear"> </Button> </LinearLayout></LinearLayout>
相關的字串定義在res/values/strings.xml檔案中:<?xml version="1.0" encoding="utf-8"?><resources> <string name="app_name">Ashmem</string> <string name="value">Value</string> <string name="hint">Please input a value...</string> <string name="read">Read</string> <string name="write">Write</string> <string name="clear">Clear</string></resources>
這樣,介面的相關配置檔案就介紹完了。我們還要再來看程式描述檔案AndroidManifest.xml的相關配置,它位於Ashmem目錄下:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="shy.luo.ashmem" android:sharedUserId="android.uid.system" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Client" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:enabled="true" android:name=".Server" android:process=".Server" > <intent-filter> <action android:name="shy.luo.ashmem.server"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service> </application></manifest>
這裡我們可以看到,下面的配置項把服務名稱"shy.luo.ashmem.server"和本地服務類Server關聯了起來: <service android:enabled="true" android:name=".Server" android:process=".Server" > <intent-filter> <action android:name="shy.luo.ashmem.server"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service>
這樣,我們就可以通過startService(new Intent("shy.luo.ashmem.server"))來啟動這個Server了。不過,在Android中,啟動服務是需要許可權的,所以,下面這一行配置獲取了啟動服務需要的相應許可權: android:sharedUserId="android.uid.system"
最後,我們來看工程的編譯指令碼檔案Android.mk,它位於Ashmem目錄下:LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES += $(call all-subdir-java-files)LOCAL_PACKAGE_NAME := AshmemLOCAL_CERTIFICATE := platforminclude $(BUILD_PACKAGE)
這裡又有一個關鍵的地方:LOCAL_CERTIFICATE := platform
因為我們需要在程式中啟動Service,所以要配置這一行,並且要把原始碼工程放在Android原始碼平臺中進行編譯。這樣,整個例子的原始碼實現就介紹完了,接下來就要編譯了。有關如何單獨編譯Android原始碼工程的模組,以及如何打包system.img,請參考如何單獨編譯Android原始碼中的模組一文。
執行以下命令進行編譯和打包:
[email protected]:~/Android$ mmm packages/experimental/[email protected]:~/Android$ make snod
這樣,打包好的Android系統映象檔案system.img就包含我們前面建立的Ashmem應用程式了。再接下來,就是執行模擬器來執行我們的例子了。關於如何在Android原始碼工程中執行模擬器,請參考在Ubuntu上下載、編譯和安裝Android最新原始碼一文。
執行以下命令啟動模擬器:
[email protected]:~/Android$ emulator
模擬器啟動起,就可以在Home Screen上看到Ashmem應用程式圖示了:點選Ashmem圖示,啟動Ashmem應用程式,介面如下:
這樣,我們就可以驗證程式的功能了,看看是否實現了在兩個程序中通過使用Android系統的匿名共享記憶體機制來共享記憶體資料的功能。
通過這個例子的學習,相信讀者對Android系統匿名共享記憶體子系統Ashmem有了一個大概的認識,但是,這種認識還是停留在表面上。我們在文章開始時就提到,Android系統匿名共享記憶體子系統Ashmem兩個特點,一是能夠輔助記憶體管理系統來有效地管理不再使用的記憶體塊,二是它通過Binder程序間通訊機制來實現程序間的記憶體共享。第二個特點我們在上面這個例子中看到了,但是似乎還不夠深入,我們知道,在Linux系統中,檔案描述符其實就是一個整數,它是用來索引程序儲存在核心空間的開啟檔案資料結構的,而且,這個檔案描述符只是在程序內有效,也就是說,在不同的程序中,相同的檔案描述符的值,代表的可能是不同的開啟檔案,既然是這樣,把Server程序中的檔案描述符傳給Client程序,似乎就沒有用了,但是不用擔心,在傳輸過程中,Binder驅動程式會幫我們處理好一切,保證Client程序拿到的檔案描述符是在本程序中有效的,並且它指向就是Server程序建立的匿名共享記憶體檔案。至於第一個特點,我們也準備在後續學習Android系統匿名共享記憶體子系統Ashmem時,再詳細介紹。
因此,為了深入瞭解Android系統匿名共享記憶體子系統Ashmem,在接下來的兩篇文章中,圍繞上面提到的兩個特點,分別學習:
學習完這兩篇文章後,相信大家對 Android系統匿名共享記憶體子系統Ashmem就會有一個更深刻的認識了,敬請關注。