0-Android使用Ashmem機制進行跨進程共享內存

Android使用Ashmem機制進行跨進程共享內存



導語:
html

Android系統中,提供了獨特的匿名共享內存子系統Ashmem(Anonymous Shared Memory),它以驅動程序的形式實如今內核空間中。它有兩個特色:java

1.一是可以輔助內存管理系統來有效地管理再也不使用的內存塊linux

2.二是它經過Binder進程間通訊機制來實現進程間的內存共享。android

本文中,咱們將經過實例來簡要介紹Android系統的匿名共享內存的使用方法,使得咱們對Android系統的匿名共享內存機制有一個感性的認識,爲進一步學習它的源代碼實現打下基礎。算法



案例原理:
數據結構

Android系統的匿名共享內存子系統的主體是以驅動程序的形式存在。在系統運行時庫層和應用程序框架層提供了訪問接口,其中,在系統運行時庫層提供了C/C++調用接口,而在應用程序框架層提供了Java調用接口。app

這裏,咱們將直接經過應用程序框架層提供的Java調用接口來講明匿名共享內存子系統Ashmem的使用方法,畢竟咱們在Android開發應用程序時,是基於Java語言的,而實際上,應用程序框架層的Java調用接口是經過JNI方法來調用系統運行時庫層的C/C++調用接口,最後進入到內核空間的Ashmem驅動程序去的。框架

        

咱們在這裏舉的例子是一個名爲Ashmem的應用程序,它包含了一個Server端和一個Client端實現,ide

1. Server端是以Service的形式實現的,在這裏Service裏面,建立一個匿名共享內存文件函數

2. Client是一個Activity,這個Activity經過Binder進程間通訊機制得到前面這個Service建立的匿名共享內存文件的句柄,從而實現共享。

在Android應用程序框架層,提供了一個MemoryFile接口來封裝了匿名共享內存文件的建立和使用,它實如今frameworks/base/core/java/android/os/MemoryFile.java文件中。

下面,咱們就來看看Server端是如何經過MemoryFile類來建立匿名共享內存文件的以及Client是如何得到這個匿名共享內存文件的句柄的。

        

在MemoryFile類中,提供了兩種建立匿名共享內存的方法,咱們經過MemoryFile類的構造函數來看看這兩種使用方法:

    
    
    
    
  1.  viewcopy
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; } ...... }


兩個構造函數的主要區別是第一個參數

1.第一種構造方法是以指定的字符串調用JNI方法native_open來建立一個匿名共享內存文件,獲得一個文件描述符,接着就以這個文件描述符爲參數調用JNI方法natvie_mmap把這個匿名共享內存文件映射在進程空間中,而後就能夠經過這個映射後獲得的地址空間來直接訪問內存數據了;


2.第二種構造方法是以指定的文件描述符來直接調用JNI方法natvie_mmap把這個匿名共享內存文件映射在進程空間中,而後進行訪問,而這個文件描述符就必需要是一個匿名共享內存文件的文件描述符,這是經過一個內部函數isMemoryFile來驗證的,而這個內部函數isMemoryFile也是經過JNI方法調用來進一步驗證的。前面所提到的這些JNI方法調用,最終都是經過系統運行時庫層進入到內核空間的Ashmem驅動程序中去,不過這裏咱們不關心這些JNI方法、系統運行庫層調用以及Ashmem驅動程序的具體實現,在接下來的兩篇文章中,咱們將會着重介紹,這裏咱們只關注MemoryFile這個類的使用方法。

前面咱們說到,咱們在這裏舉的例子包含了一個Server端和一個Client端實現,

1. Server端就是經過第一個構造函數來建立一個匿名共享內存文件


2. Client端過Binder進程間通訊機制來向Server請求獲取這個匿名共享內存的文件描述符,有了這個文件描述符以後,就能夠經過後面一個構造函數來共享這個內存文件了。 而後Client和Server之間就能夠經過這個這個匿名內存共享數據了。



案例實現:

首先在源代碼工程的packages/experimental目錄下建立一個應用程序工程目錄Ashmem。它定義了一個路徑爲shy.luo.ashmem的package,這個例子的源代碼主要就是實如今這裏了。

將會逐一介紹這個package裏面的文件。這裏要用到的Binder進程間通訊接口定義在src/shy/luo/ashmem/IMemoryService.java文件中:


    
    
    
    
  1.  view pcopy
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進程間通訊接口以後,接下來就是要在Server端實現一個本地服務了。這裏,Server端實現的本地服務名爲MemoryService,實如今src/shy/luo/ashmem/MemoryService.java文件中:
        
        
        
        
  1.  view pcopy
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文件中:
        
        
        
        
  1.  view pcopy
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文件中:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.     <LinearLayout  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:orientation="vertical"   
  11.         android:gravity="center">  
  12.         <TextView   
  13.             android:layout_width="wrap_content"  
  14.             android:layout_height="wrap_content"   
  15.             android:text="@string/value">  
  16.         </TextView>  
  17.         <EditText   
  18.             android:layout_width="fill_parent"  
  19.             android:layout_height="wrap_content"   
  20.             android:id="@+id/edit_value"  
  21.             android:hint="@string/hint">  
  22.         </EditText>  
  23.     </LinearLayout>  
  24.      <LinearLayout  
  25.         android:layout_width="fill_parent"  
  26.         android:layout_height="wrap_content"  
  27.         android:orientation="horizontal"   
  28.         android:gravity="center">  
  29.         <Button   
  30.             android:id="@+id/button_read"  
  31.             android:layout_width="wrap_content"  
  32.             android:layout_height="wrap_content"  
  33.             android:text="@string/read">  
  34.         </Button>  
  35.         <Button   
  36.             android:id="@+id/button_write"  
  37.             android:layout_width="wrap_content"  
  38.             android:layout_height="wrap_content"  
  39.             android:text="@string/write">  
  40.         </Button>  
  41.         <Button   
  42.             android:id="@+id/button_clear"  
  43.             android:layout_width="wrap_content"  
  44.             android:layout_height="wrap_content"  
  45.             android:text="@string/clear">  
  46.         </Button>  
  47.     </LinearLayout>  
  48. </LinearLayout>          
相關的字符串定義在res/values/strings.xml文件中:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string name="app_name">Ashmem</string>  
  4.     <string name="value">Value</string>  
  5.     <string name="hint">Please input a value...</string>  
  6.     <string name="read">Read</string>  
  7.     <string name="write">Write</string>  
  8.     <string name="clear">Clear</string>  
  9. </resources>  

這樣,界面的相關配置文件就介紹完了。

咱們還要再來看程序描述文件AndroidManifest.xml的相關配置,它位於Ashmem目錄下:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="shy.luo.ashmem"  
  4.       android:sharedUserId="android.uid.system"  
  5.       android:versionCode="1"  
  6.       android:versionName="1.0">  
  7.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  8.         <activity android:name=".Client"  
  9.                   android:label="@string/app_name">  
  10.             <intent-filter>  
  11.                 <action android:name="android.intent.action.MAIN" />  
  12.                 <category android:name="android.intent.category.LAUNCHER" />  
  13.             </intent-filter>  
  14.         </activity>  
  15.         <service   
  16.             android:enabled="true"   
  17.             android:name=".Server"  
  18.             android:process=".Server" >  
  19.             <intent-filter>  
  20.                 <action android:name="shy.luo.ashmem.server"/>  
  21.                 <category android:name="android.intent.category.DEFAULT"/>  
  22.             </intent-filter>  
  23.         </service>  
  24.     </application>  
  25. </manifest>  
這裏咱們能夠看到,下面的配置項把服務名稱"shy.luo.ashmem.server"和本地服務類Server關聯了起來:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1.    <service   
  2. android:enabled="true"   
  3. android:name=".Server"  
  4. android:process=".Server" >  
  5. <intent-filter>  
  6.             <action android:name="shy.luo.ashmem.server"/>  
  7.             <category android:name="android.intent.category.DEFAULT"/>  
  8.        </intent-filter>  
  9.    </service>  
這樣,咱們就能夠經過startService(new Intent("shy.luo.ashmem.server"))來啓動這個Server了。不過,在Android中,啓動服務是須要權限的,因此,下面這一行配置獲取了啓動服務須要的相應權限:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. android:sharedUserId="android.uid.system"  
最後,咱們來看工程的編譯腳本文件Android.mk,它位於Ashmem目錄下:
[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. LOCAL_MODULE_TAGS :optional  
  5.   
  6. LOCAL_SRC_FILES += $(call all-subdir-java-files)  
  7.   
  8. LOCAL_PACKAGE_NAME :Ashmem  
  9.   
  10. LOCAL_CERTIFICATE :platform  
  11.   
  12. include $(BUILD_PACKAGE)  
  這裏又有一個關鍵的地方:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. LOCAL_CERTIFICATE :platform  

由於咱們須要在程序中啓動Service,因此要配置這一行,而且要把源代碼工程放在Android源代碼平臺中進行編譯。

這樣,整個例子的源代碼實現就介紹完了,接下來就要編譯了。有關如何單獨編譯Android源代碼工程的模塊,以及如何打包system.img,請參考如何單獨編譯Android源代碼中的模塊一文。

執行如下命令進行編譯和打包:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Ashmem  
  2. USER-NAME@MACHINE-NAME:~/Android$ make snod  
這樣,打包好的Android系統鏡像文件system.img就包含咱們前面建立的Ashmem應用程序了。

再接下來,就是運行模擬器來運行咱們的例子了。關於如何在Android源代碼工程中運行模擬器,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文。       

執行如下命令啓動模擬器:

[html]   view plain  copy
  在CODE上查看代碼片 派生到個人代碼片
  1. USER-NAME@MACHINE-NAME:~/Android$ emulator         
模擬器啓動起,就能夠在Home Screen上看到Ashmem應用程序圖標了:

 

 點擊Ashmem圖標,啓動Ashmem應用程序,界面以下:


這樣,咱們就能夠驗證程序的功能了,看看是否實現了在兩個進程中經過使用Android系統的匿名共享內存機制來共享內存數據的功能。

        

經過這個例子的學習,相信讀者對Android系統匿名共享內存子系統Ashmem有了一個大概的認識,可是,這種認識仍是停留在表面上。咱們在文章開始時就提到,Android系統匿名共享內存子系統Ashmem兩個特色,一是可以輔助內存管理系統來有效地管理再也不使用的內存塊,二是它經過Binder進程間通訊機制來實現進程間的內存共享。第二個特色咱們在上面這個例子中看到了,可是彷佛還不夠深刻,咱們知道,在Linux系統中,文件描述符其實就是一個整數,它是用來索引進程保存在內核空間的打開文件數據結構的,並且,這個文件描述符只是在進程內有效,也就是說,在不一樣的進程中,相同的文件描述符的值,表明的多是不一樣的打開文件,既然是這樣,把Server進程中的文件描述符傳給Client進程,彷佛就沒有用了,可是不用擔憂,在傳輸過程當中,Binder驅動程序會幫咱們處理好一切,保證Client進程拿到的文件描述符是在本進程中有效的,而且它指向就是Server進程建立的匿名共享內存文件。至於第一個特色,咱們也準備在後續學習Android系統匿名共享內存子系統Ashmem時,再詳細介紹。

 






相關文章
相關標籤/搜索