一、Ashmem驱动程序
~/Android/kernel/goldfish
----include
----linux
----ashmem.h
----mm
----ashmem.c
驱动程序详解请看《Android系统源代码情景分析》,作者罗升阳。
二、运行时库cutils的匿名共享内存访问接口
~/Android/system/core
----libcutils
----ashmem-dev.c
详解请看《Android系统源代码情景分析》,作者罗升阳。
三、MemoryFile
~/Android/frameworks/base/core/java/android/os
----MemoryFile.java
~/Android/frameworks/base/core/jni
----android_os_MemoryFile.cpp
详解请看《Android系统源代码情景分析》,作者罗升阳。
~/Android/frameworks/base/core/java/android/os
----MemoryFile.java
public ParcelFileDescriptor getParcelFileDescriptor() throws IOException { FileDescriptor fd = getFileDescriptor(); return fd != null ? new ParcelFileDescriptor(fd) : null; } ....... public FileDescriptor getFileDescriptor() throws IOException { return mFD; }
四、应用实例
详解请看《Android系统源代码情景分析》,作者罗升阳。
在本节中,我们将创建一个Android应用程序Ashmem,它由一个Service组件Server和一个Activity组件Client组成。Server组件运行在一个独立的进程中,它内部有一个内存访问服务MemoryService,后者通过MemoryFile类创建一块匿名共享内存。Client组件运行在另一个进程中,它会将内存访问服务MemoryService创建的那块匿名共享内存映射到本进程的地址空间,以便可以访问它的内容,从而可以和Server组件共享一块匿名共享内存。
~/Android/packages/experimental/Ashmem
----AndroidManifest.java
----Android.mk
----src
----shy/luo/ashmem
----IMemoryService.java
----MemoryService.java
----Server.java
----Client.java
----res
----layout
----main.xml
----values
----strings.xml
----drawable
----icon.png
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; }
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(); } } }
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."); } }
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; } }
进程间通信详细步骤,暂时省略,只看表明的步骤,见下图:
接下来的分析,请看Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析http://blog.csdn.net/luoshengyang/article/details/6666491。
注意上文中的这句话, 这里, 我们需要关注的便是虚线框部分了,它在Binder驱动程序中实现了在两个进程中共享同一个打开文件的方法。我们知道,在Linux系统中,文件描述符其实就是一个整数。每一个进程在内核空间都有一个打开文件的数组,这个文件描述符的整数值就是用来索引这个数组的,而且,这个文件描述符只是在本进程内有效,也就是说,在不同的进程中,相同的文件描述符的值,代表的可能是不同的打开文件。因此,在进程间传输文件描述符时,不能简要地把一个文件描述符从一个进程传给另外一个进程,中间必须做一过转换,使得这个文件描述在目标进程中是有效的,并且它和源进程的文件描述符所对应的打开文件是一致的,这样才能保证共享。
也就是根据fd,得到的file结构,在两个进程是一样的,即使两个进程的fd不一样。