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时,再详细介绍。

 






相关文章
相关标签/搜索