一、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不一样。