目录
  1. 1. 简介
    1. 1.1. 内存划分
    2. 1.2. 传统 IPC 传输数据
    3. 1.3. Binder 传输数据
  2. 2. Binder
    1. 2.1. 三种 IPC 方式的数据的拷贝次数
    2. 2.2. 面向对象的 Binder IPC
  3. 3. Binder 模型
    1. 3.1. Linux进程间通信机制
  4. 4. Binder的驱动注册
  5. 5. Binder的JNI注册
  6. 6. Binder工作机制 - AIDL
    1. 6.1. 从案例入手
  7. 7. 剑指大厂
    1. 7.1. Binder有什么优势? — 《ByteDance》
    2. 7.2. Binder是如何做到一次拷贝? — 《Tencent》
    3. 7.3. mmap的原理讲解 — 《Tencent》
重拾Android-【吃透源码系列】之Binder

我们知道Android系统启动之后,要想启动一个应用程序则需要保证该应用程序的进程先被启动,如果该进程不存在就会请求 Zygote 进程启动需要的应用程序进程。这样在 Zygote 的 Java 框架层(Framework源码层)中会创建一个 Server 端的 Socket,这个 Socket 用来等待 AMS 请求 Zygote 来创建新的应用程序进程(Zygote 进程通过 fork 自身创建应用程序进程),这样应用程序进程就会获得 Zygote 进程在启动时创建的虚拟机实例。与此同时,也创建出了 Binder 线程池消息循环,这样运行在应用进程中的应用程序就可以很方便的使用 Binder 进行进程间通信以及处理消息了。

简介

既然决定吃透源码,那就必须从源头捋起,所以这篇文章会综合《深入理解Android内核设计思想》、《Android开发艺术探索》以及《Android进阶解密》三书,加上在享学课堂学习有关 Binder 进程课程,会在本文中对Binder设计细节做一个全面的阐述。首先通过介绍 Binder 通信模型和 Binder 通信协议了解 Binder 的设计需求;然后分别阐述 Binder 在系统不同架构分层间的呈现方式及其作用,最后分析 Binder 在数据接收端的设计考虑,包括:线程池管理、内存映射、等待队列等。为读者尽最大可能展现 Android Binder 原本的样貌。这样学就够了!

内存划分

内存划分.png

传统 IPC 传输数据

传统 IPC 传输数据.png

Binder 传输数据

Binder 传输数据.png

Binder

Binder 是 Android 系统进程间通信(IPC)方式之一,这里为了突出 Android Binder IPC 通信机制的优越性,先分析对比一波 共享内存Socket 和本文介绍的 Binder 的数据拷贝。

三种 IPC 方式的数据的拷贝次数

图片1.png

共享内存

共享内存可以类比 Java 的内存, 堆就是线程共享区域,而此处的堆就是 Linux 里的内存共享区域,虽然无需拷贝,但控制复杂,难以复用,基于此方案的通信,试想一下,如果有一个恶意的程序或者某个app运行崩溃,就会导致关联的应用甚至系统跟着奔溃,这是致命的。

Socket/管道/消息队列

基于 C/S 架构的通信,目前 linux 支持 的 IPC 包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及 socket 中只有 socket 支持 Client-Server 的通信方式。

socket 作为一款通用接口,其传输效率低效,开销巨大,主要用在跨网络的进程间通信和本机上进程间的低速通信。如果采用socket在底层架设一套协议,这无疑加重系统设计的复杂性,而且也难以保证通信效率。

消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,这里至少有两次拷贝过程。

Binder(基于传输性、安全性考虑均是最佳)

基于 C/S 通信模式,传输过程只需一次拷贝,为发送方添加 UID/PID 身份,既支持实名 Binder,也支持匿名 Binder,安全性高。

面向对象的 Binder IPC

Binder 使用 Client-Server 通信方式:一个进程作为 Server 提供诸如视频/音频解码,视频捕获,地址本查询,网络连接等服务;多个进程作为 Client 向 Server 发起服务请求,获得所需要的服务。

要想实现 Client-Server 通信据必须实现以下两点:

  • Server 必须有确定的访问接入点或者说地址来接受Client的请求, 并且 Client 可以通过某种途径获知 Server 的地址;

  • 制定 Command-Reply 协议来传输数据

这就好比,网络通信中,Server 的访问接入点就是 Server 主机的IP地址+端口号,传输协议为 TCP 协议;

对 Binder 而言,Binder 可以看成 Server 提供的实现某个特定服务的访问接入点,Client 通过这个 “地址” 向 Server 发送请求来使用该服务;

对 Client 而言,Binder 可以看成是通向 Server 的管道入口,要想和某个 Server 通信首先必须建立这个管道并获得管道入口。

与其他 IPC 方式不同点在于,Binder 使用了面向对象思想来描述作为访问接入点的 Binder 和在 Client 中的入口:

Binder 是一个实体位于 Server 中的对象,该对象提供了一套方法用以实现对服务的请求,就象类的成员函数。

遍布于 Client 中的入口可以看成指向这个 Binder 对象的“指针”,一旦获得了这个“指针”就可以调用该对象的方法访问 Server。

Binder 模型

Binder 框架定义了四个角色:ServerClientServiceManagerBinder驱动,这其中前三个位于 用户空间,驱动位于 内核空间。这四个角色的关系和互联网类似:Server 是服务器,Client 是客户终 端,SMgr 是域名服务器(DNS),驱动是路由器。

Linux进程间通信机制

管道、信号量、Socket、共享内存

共享内存:数据发送方、内核、数据接收方 三者共享一块物理内存空间

Binder:数据发送方通过 copy_fromm_user() 拷贝一次,将数据拷贝到内核,而内核和数据接收方有一块内存共享区域,所以这两者无需拷贝,即可使用。

Binder的驱动注册

Linux 一切皆文件

binder_init

  • 分配内存

  • 初始化设备

  • 放入设备链表 binder_devices

binder_open

  • 初始化 proc 创建 binder_proc 进程对象

  • 创建同步锁 binder_lock

  • 串联进程链表 binder_procs -> binder_proc -> binder_proc-> // hlist_head -> hlist_node -> hlist_node ->

  • 内核函数filp -> proc 赋值 private_data(后续binder访问全是它) // flip()函数反置bitset中所有的位,即将1设为0,0设为1。

  • 解除同步锁 binder_unlock

binder_mmap

  • struct vm_struct*area ——— 内核的虚拟内存

  • struct vm_area_struct*vma ——— 进程的虚拟内存(4M 驱动设定;1M - 8k 应用层设定 ===》 binder传输超过1M就会崩溃)

  • area = get_vm_area(vma->vma_end - vma->vma_start, VMREMAP) 分配内存重新建立映射关联

  • proc buffer指针 指向 area 的内核虚拟指针

  • 计算用户空间偏移值 user_buffer_offset:用户空间虚拟内存地址(proc->buffer) = 虚拟内存地址(vma->vm_start) + 偏移值(proc->user_buffer_offset)

Binder的JNI注册

Binder工作机制 - AIDL

从案例入手

案例直接引用《Android开发艺术探索》-Binder章节

新建一个Book实体对象类,并实现序列化

public class Book implements Parcelable {

public int bookId;
public String bookName;

public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}

protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}

public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}

@Override
public Book[] newArray(int size) {
return new Book[size];
}
};

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}

新建项目->new AIDL File->Book.aidl

package com.tufusi.aidl;

parcelable Book;

新建项目->new AIDL File->IBookManager.aidl

package com.tufusi.aidl;

import com.tufusi.aidl.Book;

interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}

Build->make project 在build文件夹中,依次点开generated\aidl_source_output_dir\debug\out\com\tufusi\aidl,找到IBookManager.java,接下来分析这个类,从而了解Binder通信机制。

先罗列系统相关接口类

  • IInterface.java
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
  • IBinder.java

大部分接口对于客户端来说都是hide注解的,只列出对我们Binder通信分析最有用的执行方法。

public interface IBinder {

public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;

... 省略
}
  • Binder.java

去掉大部分@hide方法及native方法,只展示重要分析部分。

public class Binder implements IBinder {

// 省略

/**
* Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
* 这里涉及到Binder线程池,后面分析
*/
public static final void joinThreadPool() {
BinderInternal.joinThreadPool();
}

/**
* 判断传入的IInterface是否是代理对象
* @hide
*/
public static final boolean isProxy(IInterface iface) {
return iface.asBinder() != iface;
}

// 省略

/**
* 默认构造器,仅仅是初始化该对象
*
* 如果正在创建一个Binder通信令牌 (一个Binder对象没有附加任何接口),就应该考虑使用这个替换
*/
public Binder() {
this(null);
}

/**
* 用于创建原始Binder对象(令牌)和描述符的构造函数。
*/
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);

if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mDescriptor = descriptor;
}

/**
* 将特定接口与Binder做关联的简便方法
* 在调用后, queryLocalInterface() 会帮我们实现: 当相应的描述符被请求时返回给定的 IInterface owner
* attach进来的owner就是Stub
*/
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}

/**
* 如果描述符相匹配,则返回指定的 mOwner
*/
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}

/**
* 代理执行监听器,这个接口用来通知每个代理端Binder的回调
*/
@SystemApi
public interface ProxyTransactListener {

@Nullable
default Object onTransactStarted(@NonNull IBinder binder, int transactionCode, int flags) {
return onTransactStarted(binder, transactionCode);
}

@Nullable
Object onTransactStarted(@NonNull IBinder binder, int transactionCode);

void onTransactEnded(@Nullable Object session);
}

/**
* 为代理端对象设置代理执行监听器,这个监听器是全局的。但是是隐藏的系统服务接口,客户端一般用不到
*/
@SystemApi
public static void setProxyTransactListener(@Nullable ProxyTransactListener listener) {
BinderProxy.setTransactListener(listener);
}

/**
* Stub存根类的一个默认实现(该类继承Binder,并实现AIDL接口),默认返回false,只有命中不同的分支语句才会返回true。
* 我们要在这里进行覆写,从而对事务进行适当的数据编排。
*
* @param code 要执行的动作。这应该
* 是一个介于{@link #FIRST_CALL_TRANSACTION}和 {@link # LAST_CALL_TRANSACTION}之间的数字 。
*
* @param data 从调用方接收的编组数据。
* @param reply 如果调用者期待返回结果,需要在这里对其进行编组。
* @param flags 额外的操作标志。
*
* @return 成功调用时返回true;通常返回false表示你不明白这里的事务代码(下面就会知道为什么说返回false表示不懂这里的处理逻辑,因为只有同一进程才会返回false,而同一进程为何要使用Binder进行通信呢)
*/
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
// 这里主要是针对code进行一些fd的写入写出,目的是为了通信,涉及到序列化
}


/**
* 默认的反序列化的事务代码的实现. 在远程服务端,对Binder进行事务的调用以执行IPC
*/
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);

if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}

/**
* 给Binder设置死亡代理
*/
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
}

/**
* 解除死亡代理关联
*/
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
return true;
}

// 省略
}
  • BinderProxy.java

服务端代理类。这个类代码量比较庞大,继续删减,只留分析有用的代码。

public final class BinderProxy implements IBinder {

// 省略

@GuardedBy("sProxyMap")
private static final ProxyMap sProxyMap = new ProxyMap();

/**
* 统计当前进程中存活的代理接口数量
*/
public static final class InterfaceCount {
private final String mInterfaceName;
private final int mCount;

InterfaceCount(String interfaceName, int count) {
mInterfaceName = interfaceName;
mCount = count;
}

@Override
public String toString() {
return mInterfaceName + " x" + Integer.toString(mCount);
}
}

/**
* 检索一个本地接口——在代理的情况下始终为空
*/
public IInterface queryLocalInterface(String descriptor) {
return null;
}

/**
* Perform a binder transaction on a proxy.
*
* @param code The action to perform. This should
* be a number between {@link #FIRST_CALL_TRANSACTION} and
* {@link #LAST_CALL_TRANSACTION}.
* @param data Marshalled data to send to the target. Must not be null.
* If you are not sending any data, you must create an empty Parcel
* that is given here.
* @param reply Marshalled data to be received from the target. May be
* null if you are not interested in the return value.
* @param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
*
* @return
* @throws RemoteException
*/
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");

if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
&& Binder.sWarnOnBlockingOnCurrentThread.get()) {

// For now, avoid spamming the log by disabling after we've logged
// about this interface at least once
mWarnOnBlocking = false;

if (Build.IS_USERDEBUG) {
// Log this as a WTF on userdebug builds.
Log.wtf(Binder.TAG,
"Outgoing transactions from this process must be FLAG_ONEWAY",
new Throwable());
} else {
Log.w(Binder.TAG,
"Outgoing transactions from this process must be FLAG_ONEWAY",
new Throwable());
}
}

final boolean tracingEnabled = Binder.isTracingEnabled();
if (tracingEnabled) {
final Throwable tr = new Throwable();
Binder.getTransactionTracker().addTrace(tr);
StackTraceElement stackTraceElement = tr.getStackTrace()[1];
Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
}

// Make sure the listener won't change while processing a transaction.
final Binder.ProxyTransactListener transactListener = sTransactListener;
Object session = null;

if (transactListener != null) {
final int origWorkSourceUid = Binder.getCallingWorkSourceUid();
session = transactListener.onTransactStarted(this, code, flags);

// Allow the listener to update the work source uid. We need to update the request
// header if the uid is updated.
final int updatedWorkSourceUid = Binder.getCallingWorkSourceUid();
if (origWorkSourceUid != updatedWorkSourceUid) {
data.replaceCallingWorkSourceUid(updatedWorkSourceUid);
}
}

final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
AppOpsManager.pauseNotedAppOpsCollection();

if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
flags |= FLAG_COLLECT_NOTED_APP_OPS;
}

try {
return transactNative(code, data, reply, flags);
} finally {
AppOpsManager.resumeNotedAppOpsCollection(prevCollection);

if (transactListener != null) {
transactListener.onTransactEnded(session);
}

if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}
}
}

// 省略
}

接下来分析系统为我们自动生成的基于AIDL规范的用于进程间通信的接口类。

这里,系统为我们生成一个IBookManager.java类,这个类主要涉及三个类:Default、抽象类Stub、还有一个静态内部类Proxy

思考: 为什么谷歌会将三个类全放在一个接口类里,按照三个类拆开不是更便于阅读么?这大概是因为防止各个应用进行AIDL命名类名时起冲突,通过这个粒度进行类划分,可以确保不冲突。

  • IBookManager.java
// 继承IInterface接口,Default实现了这个自定义接口,必须实现 asBinder 方法
public interface IBookManager extends android.os.IInterface {
/**
* 默认空实现
*/
public static class Default implements com.tufusi.aidl.IBookManager {
@Override
public java.util.List<com.tufusi.aidl.Book> getBookList() throws android.os.RemoteException {
return null;
}

@Override
public void addBook(com.tufusi.aidl.Book book) throws android.os.RemoteException {
}

@Override
public android.os.IBinder asBinder() {
// 返回当前的Binder对象
return null;
}
}

/**
* 本地端的IPC实现类
*/
public static abstract class Stub extends android.os.Binder implements com.tufusi.aidl.IBookManager {

// Binder的唯一标识,一般是当前 Binder的全类名表示
private static final java.lang.String DESCRIPTOR = "com.tufusi.aidl.IBookManager";

/**
* 构造Stub类并依附 IBookManager 接口,同时传入描述符
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* 将 IBinder对象转换成到 com.tufusi.aidl.IBookManager接口类对象,
* 如果需要,生成代理类.
*/
public static com.tufusi.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
// 根据描述符,查询本地接口组,是否有匹配的AIDL接口类型的对象,如果有,则直接走本地通信,不会走跨进程通信
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.tufusi.aidl.IBookManager))) {
// 返回的是服务端的Stub对象
return ((com.tufusi.aidl.IBookManager) iin);
}
// 包装传入的IBinder对象,返回服务端代理对象 Stub.Proxy对象
return new com.tufusi.aidl.IBookManager.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
// 将当前类对象转为IBinder对象,Stub返回的是本地端对象 & Proxy那边返回的是服务端代理对象(其实Stub也是本地端的一个代理对象)
return this;
}

// 事务执行,运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
// 分发请求的目标方法的code
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.tufusi.aidl.Book> _result = this.getBookList();
// 当目标方法执行完毕,向reply中写入返回值
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.tufusi.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.tufusi.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
// 如果客户端的请求返回false,就表明没有权限进行远程调用
default: {
return super.onTransact(code, data, reply, flags);
}
}
}

// 服务端代理类,当客户端和服务端位于同一进程,则不会进行跨进程的transact过程。
private static class Proxy implements com.tufusi.aidl.IBookManager {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

// 这个方法运行在客户端,当客户端进行远程调用此方法时,内部是这样实现的:
// 1)创建该方法所需的输入型 Parcel 对象 _data、输出型 Parcel 对象 _reply 和返回值对象 _result
// 2)把该方法的参数信息写入 _data 中(如果有参数)
// 3)调用transact方法发起 RPC(远程过程调用)请求,同时当前线程挂起
// 4)服务端 onTransact方法会被调用,直到 RPC 过程返回后,当前线程继续执行,并从 _reply 中取出 RPC 过程的返回结果
// 5)返回 _reply 中的数据
@Override
public java.util.List<com.tufusi.aidl.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.tufusi.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
// ☆☆☆☆☆☆ 通过返回状态,判断是否执行本地调用 ☆☆☆☆☆☆☆
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.tufusi.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

@Override
public void addBook(com.tufusi.aidl.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}

public static com.tufusi.aidl.IBookManager sDefaultImpl;
}

// 这两个整型ID跟aidl中定义的方法有关,都会在IBinder的两个常量值上实现递增 0x00000001 ~ 0x00ffffff(给vpn用的)
// 这两个值能确保手机系统中所有用到的aidl方法的id唯一。
// 这两个id用于标识在transact过程中客户端所请求的到底是哪个方法,进行不同命中
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

public static boolean setDefaultImpl(com.tufusi.aidl.IBookManager impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}

public static com.tufusi.aidl.IBookManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}

public java.util.List<com.tufusi.aidl.Book> getBookList() throws android.os.RemoteException;

public void addBook(com.tufusi.aidl.Book book) throws android.os.RemoteException;
}

剑指大厂

Binder有什么优势? — 《ByteDance》

优点:
内存开辟
风险隔离

Binder是如何做到一次拷贝? — 《Tencent》

内存划分:内存被操作系统分成两块:用户空间和内核空间,用户空间时用户程序代码运行的地方,内核空间是系统内核运行的地方,可以共享。为了安全,这两者是隔离的,这样即使用户的应用程序崩溃也不会影响到内核。

32位系统 Vs 64位系统

32位系统,即2^32,即总共可访问地址为4G。内核1G,用户空间3G。

64位系统,低位:0-47位才是有效的可变地址(寻址空间256T),高位:48-63位全补0或1。
一般高位全补0对应的地址空间是用户空间。高位全补1对应的是内核空间。

mmap的原理讲解 — 《Tencent》

MMAP:Linux通过将一个虚拟 内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)

对文件进行mmap,会在进程的虚拟内存分配地址空间,建立映射关系。实现这样的映射关系后,就可以对这一段内存采用指针的方式进行读写操作,而系统会自动回写到对应的文件磁盘上。

打赏
  • 微信
  • 支付宝

评论