Binder是一种进程间通信机制
Binder是一个虚拟物理设备驱动
Binder是一个能发起通信的java类
Linux是一切皆文件
那么都在什么时候需要多进程操作?
突破进程内存限制,如图库占用内存过多
功能稳定性,独立的通信进程保持长连接稳定性
规避系统内存泄漏,独立的webView进程阻隔内存泄漏导致的问题
隔离风险,对于不稳定的功能放在独立进程避免主内存崩溃
surferView、webView本身设计就有问题,可以考虑多进程保证内存泄漏风险
那么为什么选择binder?
性能 只需要一次拷贝
特点 C/S架构
安全性为每个进程分配UID 支持实名和匿名
什么是实名?什么是匿名?
有在ServiceManager 注册称为实名, 没有在ServiceManager注册,称为匿名,比如自己的Service就是匿名,可以变成实名么?也可以变成实名
共享内存
性能 无需拷贝性能更好
特点 控制复杂,易用性差
依赖上层协议
那么什么叫依赖上层协议?简单说就是如果恶意软件发起的,用假的协议也是一样能访问
不安全 而 Binder 分配UID 更好追踪 所以binder更安全 而且 共享内存直接修改数据没有同步控制,很容易访问絮乱,安全性太差,没有经过拷贝数据
Socket
性能 需要拷贝两次
特点 C/S架构 效率低,开销大
依赖上层协议
这里有个问题 为什么socket既可以访问网络也可以跨进程通信
Socket 是一种通用的网络通信协议,它可以在不同的网络节点之间进行通信。在网络通信中,Socket 提供了一种标准的接口,使得不同主机之间可以建立连接、发送和接收数据。
而在跨进程通信中,Android 系统采用了 Binder 机制。Binder 是一种进程间通信(IPC)机制,通过将对象和方法的调用传递给其他进程来实现通信。Binder 通过底层的 Linux 进程间通信(IPC)机制来传输数据,实现了进程间的数据共享和通信。
那么为什么 Socket 可以用于网络通信和跨进程通信呢?这是因为在底层实现上,网络通信和跨进程通信都依赖于操作系统提供的 IPC 机制。Socket 是一种通用的接口,它可以在不同的层级上实现不同的传输协议(如 TCP、UDP),而底层的实现细节则由操作系统负责处理。
在网络通信中,Socket 通过 TCP/IP 协议栈与网络进行交互,发送和接收网络数据包。而在跨进程通信中,Socket 可以通过底层的 Binder 机制与其他进程进行通信,传递数据和调用方法。
因此,虽然 Socket 在应用层面上被用于网络通信和跨进程通信,但它们的底层实现机制是不同的。网络通信依赖于 TCP/IP 协议栈,而跨进程通信依赖于 Binder 机制。
socket比binder更消耗性能,所以为什么Android系统中的通信大部分使用socket?
Android系统中使用socket作为通信方式的主要原因其更加通用和灵活。虽然在性能方面,Socket确实会消耗一些额外的资源,但它在设备、跨平台和跨网络环境的通信中具有广泛的应用。
首先,Socket是一种标准的网络编程接口,几乎所有的操作系统都支持它。这意味着使用Socket可以实现跨平台的通信,而不仅限于Android设备之间的通信。这对于开发跨平台的应用程序和与其他设备进行通信非常有用。
其次,Socket提供了一种基于网络的通信模型,可以通过TCP或UDP协议进行数据传输。这种灵活性使得开发者可以根据具体需求选择合适的协议和通信方式。例如,对于需要可靠传输和有序数据的场景,可以使用TCP协议;而对于实时性要求较高的场景,可以选择UDP协议。
另外,Socket还能够支持各种网络通信模式,如客户端-服务器模式、对等模式等。这使得开发者可以根据应用程序的需求选择合适的通信模式,从而更好地满足应用程序的要求。
虽然Binder在Android系统中主要用于进程间通信(IPC),但它的使用场景主要集中在同一设备上的进程通信,而Socket则更适用于网络通信和跨设备通信。因此,在Android系统中,Socket被广泛应用于网络通信和与其他设备进行通信的场景,以实现更大的灵活性和通用性。
还有一个问题就是安全性,binder会开启一个自己的线程池,但是binder也存在死锁的风险
如果说线程有死锁,有数据不安全性,那么多进程存在这种问题嘛?答案是肯定的 后面分析
Binder是一次拷贝,那么什么叫做一次拷贝?
那么首先看下传统的IPC机制
64、32的区别是什么?
寻址就是所谓的门牌编号
- 寻址能力:64位系统的主要优势之一是能够支持更大的内存寻址空间。32位系统的寻址空间限制在4GB以内,而64位系统可以支持更大的内存容量(最多可达16EB)。
- 整数运算:64位系统可以对更大范围的整数进行计算,具有更高的精度和更大的整数范围。这对于一些需要处理大数据量或需要高精度计算的应用程序很重要。
- 浮点运算:64位系统在浮点运算方面也具有优势,可以进行更精确的浮点计算,提供更高的计算精度。
- 指令集扩展:64位架构引入了一些新的指令集扩展,如SSE2、AVX等,可以提供更高效的数据处理能力和并行计算能力。
那么一般来讲用户空间寻址占3g,而内核占一g
那么问题来了?一个内存才8G,那么进程就占4G如何处理?那么这里涉及一个概念,虚拟内存和物理内存
软件工程师说的内存都是虚拟内存
物理内存就是内存条
比如一个数组,而内存不一定会有连续的地址,为了解决这种问题,就分为了虚拟内存和物理内存,这是一个比较简单的比喻
那么内存管理单元就会处理虚拟地址和物理地址的映射关系
内核空间映射都是同一块内存,用户空间映射的是不同的
如果一个进程调用另一个进程首先要copy_form_user 系统首先要看是否有权限
我们所说的拷贝就是这个copy_form_user调用了几次 在用户空间运行的称用户态 内核成为内核态
转化非常消耗时间,但是为什么?
因为需要保存当前进程的状态,耗时且消耗资源
内核空间和内核空间是相通的,但是和用户空间是不通的,所以需要系统调用将内存调用到用户空间如下所示 传统IPC两次拷贝如下所示
那么binder为啥是一次拷贝??还有为啥既然mmap能映射还需要拷贝?
是为了安全性考虑,所以才进行拷贝一次的目的
通过这种mmap形式去映射内存,到达跨进程通信的目的
那为什么两边不能都直接映射?那就变成了内存共享,实现难度加大性能会差一点但是使用更加方便
那么问题来了 Android用户态切换内核态非常耗时 所以Binder是开启了新的线程执行的吗?
是的,Binder 在 Android 中会开启新的线程执行跨进程通信的操作。这是因为在进行 Binder 调用时,涉及到用户态和内核态之间的切换,而这种切换是相对耗时的。为了避免阻塞应用程序主线程,Android 系统会将 Binder 相关的操作放在独立的线程中执行。
具体来说,Android 的 Binder 机制在底层使用了线程池来管理 Binder 线程。当应用程序发起跨进程通信时,系统会在线程池中选择一个空闲的 Binder 线程来执行相应的操作,例如发送请求、等待响应等。通过将 Binder 相关的操作放在独立的线程中执行,可以避免阻塞应用程序主线程,提高应用程序的响应性能。
需要注意的是,由于 Binder 线程是由系统管理的,因此开发者无法直接控制和操作它。开发者只需要使用正确的方式进行跨进程通信,并遵循异步操作的原则,让系统自行管理 Binder 线程的创建和销毁。这样可以确保应用程序在进行跨进程通信时获得较好的性能和响应性。
Binder 并发环境下是如何保证安全的?
在 Binder 并发环境下,安全性是通过以下方式得到保证的:
-
进程隔离:Binder 使用进程间通信(IPC)机制来实现组件之间的通信,每个组件运行在单独的进程中。这种进程隔离确保了组件之间的相互独立性,一个组件的错误不会影响到其他组件。
-
线程安全:在每个进程中,Binder 会为每个线程提供一个独立的 Binder 线程池。这样可以避免多个线程同时访问 Binder 对象,从而保证了线程安全性。
-
引用计数:Binder 使用引用计数来管理对象的生命周期。当一个组件需要使用另一个组件提供的服务时,它会增加对该组件的引用计数;当不再需要该服务时,它会减少对该组件的引用计数。只有当引用计数为零时,该组件才会被销毁,这样可以避免资源泄漏和访问无效对象的情况发生。
-
权限控制:Binder 支持权限控制来限制对特定服务的访问。每个 Binder 组件都可以定义自己的权限级别,并在注册时进行声明。只有具有相应权限的组件才能访问该服务,这样可以确保只有经过授权的组件才能执行敏感操作。
综上所述,Binder 在并发环境下通过进程隔离、线程安全、引用计数和权限控制等机制来保证安全性。
为什么binder线程池就可以维护安全,或者说为什么新开一个线程池就能保证线程安全?
新开一个 Binder 线程池可以有助于维护线程安全的原因如下:
-
独立的线程:每个进程都有自己的 Binder 线程池,不同进程之间的线程是相互独立的。这意味着每个组件在自己所在的进程中拥有独立的线程池,可以避免不同组件之间的线程冲突和竞争条件。
-
串行执行:Binder 线程池中的线程一般是串行执行的,即每次只处理一个请求。这种串行执行的机制确保了在同一时刻只有一个线程在访问 Binder 对象,避免了多个线程同时对对象进行操作导致的线程安全问题,如竞态条件和数据不一致性。
-
同步机制:Binder 线程池使用了同步机制来保证线程安全。例如,可以使用锁来控制对共享资源的访问,或者使用信号量来限制并发访问的数量。这样可以确保在并发环境下对关键资源的访问是互斥的,避免了数据竞争和访问冲突。
总的来说,新开一个 Binder 线程池可以提供独立的线程环境,实现线程的串行执行和使用同步机制来保证线程安全。这样可以有效地避免并发访问导致的线程安全问题,确保 Binder 在并发环境下的安全性。
ServerManager 我们在网络请求的时候会先通过URL去访问DNS服务器去获取到域名ip,拿到ip后再去访问服务器,那么ServerManager就是类似的流程
网络请求流程
ServerManager就跟DNS一样会有路由表(显式通信)
ActivityTaskManagerService 的Lifecycle 代码 publishBinderService 传入的是 service因为ActivityTaskManagerService 实现了stub接口 onstart是在反射创建服务的时候调用方调用的
public static final class Lifecycle extends SystemService {
private final ActivityTaskManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new ActivityTaskManagerService(context);
}
@Override
public void onStart() {
publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
mService.start();
}
binder一般是在系统启动时候进行启动的,进程启动最终会调用到open_driver
static int open_driver(const char *driver)
382{
//打开binder驱动
383 int fd = open(driver, O_RDWR | O_CLOEXEC);
384 if (fd >= 0) {
385 int vers = 0;
386 status_t result = ioctl(fd, BINDER_VERSION, &vers);
387 if (result == -1) {
388 ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
389 close(fd);
390 fd = -1;
391 }
392 if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
393 ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
394 vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
395 close(fd);
396 fd = -1;
397 }
398 size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
399 result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
400 if (result == -1) {
401 ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
402 }
403 } else {
404 ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
405 }
406 return fd;
407}
那么这里有个问题 为什么binder在系统初始化时候第一时间初始化?
就比如AMS,如果启动一个子app,如果第一时间没有进行初始化,那么想开启一个Activity如何开启?只能通过binder通信去开启Activity
那么zygote为什么没有binder?使用socket?安全性,有可能会死锁
Android sdk专门为了aidl在SDK内部使用了 aidl工具可以生成c++文件
aidl的流程图大概
接下来分析下绑定流程
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
..省略一车代码
//ActivityManager.getService() 先拿到对应的binder对象
int res = ActivityManager.getService().bindIsolatedService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static IActivityTaskManager getTaskService() {
return ActivityTaskManager.getService();
}
@UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
//首先先拿到 ServiceManager 里的 binder对象
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
然后经过一系列跨进程调用就到了
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
if (r.app == null || r.app.thread == null) {
// If service is not currently running, can't yet bind.
return false;
}
if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
+ " rebind=" + rebind);
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
//这里的thread就是我们之前跨进程说到的在main函数里会创建appThread对象
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.getReportedProcState());
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (TransactionTooLargeException e) {
然后进行进程切换
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
if (DEBUG_SERVICE)
Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
sendMessage(H.BIND_SERVICE, s);
}
Handler才是真正的切换了进程 就实现了跨进程执行
那么绑定流程搞定后看下Binder的底层逻辑
AIDL调用了接口就会走到这 这里是调用逻辑
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
//这里是一个IBinder接口,实现类是BinderProxy
boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
在BinderProxy里
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
//我们只看主线流程走到了 transactNative
try {
return transactNative(code, data, reply, flags);
} finally {
}
}
在JNI调用之后就走到了 android_os_BinderProxy_transact 方法
1235static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
1236 jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
1237{
1238 if (dataObj == NULL) {
1239 jniThrowNullPointerException(env, NULL);
1240 return JNI_FALSE;
1241 }
1242
1243 Parcel* data = parcelForJavaObject(env, dataObj);
1244 if (data == NULL) {
1245 return JNI_FALSE;
1246 }
1247 Parcel* reply = parcelForJavaObject(env, replyObj);
1248 if (reply == NULL && replyObj != NULL) {
1249 return JNI_FALSE;
1250 }
1251
在这里调用getBPNativeData函数实际上获取的是BinderProxyNativeData
实际上是BpBinder
1252 IBinder* target = getBPNativeData(env, obj)->mObject.get();
1253 if (target == NULL) {
1254 jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
1255 return JNI_FALSE;
1256 }
1257
1258 ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",
1259 target, obj, code);
1260
1261
1262 bool time_binder_calls;
1263 int64_t start_millis;
1264 if (kEnableBinderSample) {
1265 // Only log the binder call duration for things on the Java-level main thread.
1266 // But if we don't
1267 time_binder_calls = should_time_binder_calls();
1268
1269 if (time_binder_calls) {
1270 start_millis = uptimeMillis();
1271 }
1272 }
1273
1274 //printf("Transact from Java code to %p sending: ", target); data->print();
1275
接下来在这边传入参数进入下一步
status_t err = target->transact(code, *data, reply, flags);
1276 //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
1277
1278 if (kEnableBinderSample) {
1279 if (time_binder_calls) {
1280 conditionally_log_binder_call(start_millis, target, code);
1281 }
1282 }
1283
1284 if (err == NO_ERROR) {
1285 return JNI_TRUE;
1286 } else if (err == UNKNOWN_TRANSACTION) {
1287 return JNI_FALSE;
1288 }
1289
1290 signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
1291 return JNI_FALSE;
1292}
这里的getBPNativeData 实际上是反射实现
(BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
这段代码是通过 JNI(Java Native Interface)实现的反射代码。它使用了JNI函数GetLongField
来获取obj
对象的gBinderProxyOffsets.mNativeData
字段的值,并将其转换为BinderProxyNativeData*
类型。通过这种方式,可以在Java代码中访问和操作底层的原生数据。
接下来到了BPBinder里的 transact 方法
209status_t BpBinder::transact(
210 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
211{
212 // Once a binder has died, it will never come back to life.
213 if (mAlive) {
创建了一个线程的操作
214 status_t status = IPCThreadState::self()->transact(
215 mHandle, code, data, reply, flags);
216 if (status == DEAD_OBJECT) mAlive = 0;
217 return status;
218 }
219
220 return DEAD_OBJECT;
221}
594status_t IPCThreadState::transact(int32_t handle,
595 uint32_t code, const Parcel& data,
596 Parcel* reply, uint32_t flags)
597{
598 //省略一些代码
为了整合一些数据,将数据写入到结构体
err = writeTransactionData(BC_TRANSACTION_SG, flags, handle, code, data, nullptr);
627
628 if (err != NO_ERROR) {
629 if (reply) reply->setError(err);
630 return (mLastError = err);
631 }
617
618 if ((flags & TF_ONE_WAY) == 0) {
619 #if 0
620 if (code == 4) { // relayout
621 ALOGI(">>>>>> CALLING transaction 4");
622 } else {
623 ALOGI(">>>>>> CALLING transaction %d", code);
624 }
625 #endif
626 if (reply) {
接下来在这里
627 err = waitForResponse(reply);
628 } else {
629 Parcel fakeReply;
630 err = waitForResponse(&fakeReply);
631 }
632 #if 0
633 if (code == 4) { // relayout
634 ALOGI("<<<<<< RETURNING transaction 4");
635 } else {
636 ALOGI("<<<<<< RETURNING transaction %d", code);
637 }
638
650
651 return err;
652}
一步一图人才能看得懂,那么 这个时序就走到了IPCThreadState这个类,这个类干嘛的,都说晕了,BPBinder是客户端联系Binder驱动设备的,那IPCThreadState干嘛的?
ProcessState 专门管理每个应用进程的 Binder 操作,同一个进程中只有一个 ProcessState 实例存在,且只在 ProcessState 对象创建时才打开 Binder 设备以及内存映射。相关代码如下:
sp<ProcessState> ProcessState::self(){
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) { //如果创建过 ProcessState 就直接返回
return gProcess;
}
gProcess = new ProcessState;
return gProcess;
}
外部统一通过 ProcessState::self() 方法获取 ProcessState,以此保证 ProcessState 的进程单例,ProcessState 的构造函数如下:
rocessState::ProcessState()
: mDriverFD(open_driver()) //打开 binder 设备
, mVMStart(MAP_FAILED) //初始化为 MAP_FAILED,映射成功后会变更
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS) //binder 线程最大数量
, mStarvationStartTimeMs(0)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1){
if (mDriverFD >= 0) { //已经成功打开 binder 驱动设备
// 将应用进程一块虚拟内存空间与 binder 驱动映射,在此内存块上进行数据通信
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) { //映射失败处理
ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
}
}
}
OK,我不懂 open_driver打开了什么binder设备?mmap是这个类初始化就做的,那么mmap怎么连上的?ok这些问题留到后面,binder_open mmap这三大重要流程我们慢慢分析,我们先关心主线,搞清楚结构再搞清楚代码细节
989 status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
990 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
991 {
992 ...
通知对应的驱动来写入这些数据
995 tr_sg.transaction_data.target.handle = handle;
996
1001
1002 const status_t err = data.errorCheck();
1003 if (err == NO_ERROR) {
1004 tr_sg.transaction_data.data_size = data.ipcDataSize();
取出数据 另一个进程也可以进行解析数据
1005 tr_sg.transaction_data.data.ptr.buffer = data.ipcData();
1006 tr_sg.transaction_data.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
1007 tr_sg.transaction_data.data.ptr.offsets = data.ipcObjects();
1008 tr_sg.buffers_size = data.ipcBufferSize();
1009 }
....
1020
1021 mOut.writeInt32(cmd);
1022 mOut.write(&tr_sg, sizeof(tr_sg));
1023
1024 return NO_ERROR;
1025 }
循环读取驱动
761status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
762{
763 uint32_t cmd;
764 int32_t err;
765
766 while (1) {
在这里读取驱动
767 if ((err=talkWithDriver()) < NO_ERROR) break;
768
840 }
//省略一些与主线无关的代码
在这里进行ioctl 进行与内存打交道
852status_t IPCThreadState::talkWithDriver(bool doReceive)
853{
854 //省略无关流程
900 do {
901 IF_LOG_COMMANDS() {
902 alog << "About to read/write, write size = " << mOut.dataSize() << endl;
903 }
904#if defined(__ANDROID__)
905 if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
906 err = NO_ERROR;
907 else
908 //......
951
952 return err;
953}
ioctl方法资料:【通俗易懂】详解 ioctl 函数是如何操控底层的_ioctl函数详解-优快云博客
一步一张图,时序图大概如下
那么接下来,我们要多问几个问题,我们看源码看到现在,binder的一次拷贝在哪?它的线程池在哪?为什么需要一个线程池?怎么映射的?
那么先回答第一个问题 binder的一次拷贝在哪?
binder调用 ioctl 后会进行拷贝
ioctl 操作内核层的地址
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
void __user *ubuf = (void __user *)arg;
/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/
binder_selftest_alloc(&proc->alloc);
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: {
int max_threads;
if (copy_from_user(&max_threads, ubuf,
sizeof(max_threads))) {
ret = -EINVAL;
goto err;
}
binder_inner_proc_lock(proc);
proc->max_threads = max_threads;
binder_inner_proc_unlock(proc);
break;
}
copy_from_user进行一次复制
ServiceManager 里的binder 获取方式和 Service的绑定不同 这里是读取逻辑
ServiceManager里的binder需要通过 getContextObject来获取
private static IServiceManager getIServiceManager() {
102 if (sServiceManager != null) {
103 return sServiceManager;
104 }
105
106 // Find the service manager
107 sServiceManager = ServiceManagerNative
108 .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
109 return sServiceManager;
110 }
976
977static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
978{
979 sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
建造java对象 下面有做分析
980 return javaObjectForIBinder(env, b);
981}
ProcessState就是进程状态,启动进程会第一时间启动ProcessState 会进行初始化binder,为Binder通信提供支持初始化Binder线程池等
那么为什么ProcessState 来提供这些底层能力?
因为 ProcessState self 函数
sp<ProcessState> ProcessState::self()
69{
70 Mutex::Autolock _l(gProcessMutex);
71 if (gProcess != NULL) {
72 return gProcess;
73 }
74 gProcess = new ProcessState("/dev/binder");
75 return gProcess;
76}
因为在这里初始化了binder驱动等一系列事情
409ProcessState::ProcessState(const char *driver)
410 : mDriverName(String8(driver))
411 , mDriverFD(open_driver(driver))
412 , mVMStart(MAP_FAILED)
413 , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
414 , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
415 , mExecutingThreadsCount(0)
416 , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
417 , mStarvationStartTimeMs(0)
418 , mManagesContexts(false)
419 , mBinderContextCheckFunc(NULL)
420 , mBinderContextUserData(NULL)
421 , mThreadPoolStarted(false)
422 , mThreadPoolSeq(1)
423{
424 if (mDriverFD >= 0) {
调用mmap接口向binder驱动中申请内核空间
425 // mmap the binder, providing a chunk of virtual address space to receive transactions.
426 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
427 if (mVMStart == MAP_FAILED) {
428 // *sigh*
429 ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
430 close(mDriverFD);
431 mDriverFD = -1;
432 mDriverName.clear();
433 }
434 }
435
436 LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");
437}
438
继续分析getContextObject,我们知道ServiceManager的路由表最后走到这里我们看下这个方法是如何实现的
110sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
111{
112 return getStrongProxyForHandle(0);
113}
为什么是第0个?第0个就代表了ServiceManager所在的进程 固定的地址
243
244sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
245{
251
252 if (e != NULL) {
253 // We need to create a new BpBinder if there isn't currently one, OR we
254 // are unable to acquire a weak reference on this current one. See comment
255 // in getWeakProxyForHandle() for more info about this.
256 IBinder* b = e->binder;
257 省略无关代码
284 如果第0个没有binder 就返回一个新的binder对象
285 b = BpBinder::create(handle);
286 e->binder = b;
287 if (b) e->refs = b->getWeakRefs();
288 result = b;
289 } else {
290 // This little bit of nastyness is to allow us to add a primary
291 // reference to the remote proxy when this team doesn't have one
292 // but another team is sending the handle to us.
293 result.force_set(b);
294 e->refs->decWeak(this);
295 }
296 }
297
298 return result;
299}
300
那么BPbinder代表什么?它就代表了驱动的封装 这个BPbinder是native的对象,不能直接给上层使用,所以需要JNI进行封装,如果想把native的对象变成java层的对象,那就需要通过反射将native对象转成java对象 javaObjectForIBinder
644jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
645{
646 .....省略无关代码
658
659 BinderProxyNativeData* nativeData = gNativeDataCache;
660 if (nativeData == nullptr) {
661 nativeData = new BinderProxyNativeData();
662 }
663 // gNativeDataCache is now logically empty.
通过反射制造java对象
664 jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
665 gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
省略无关代码
666 ......
686
687 return object;
688}
由此可见,ServiceManager 的 getService 本质上也是跨进程去 底层获取保存的BPBinder
client 端的调用流程结束,看下binder的启动流程
init.rc文件 serviceManager 是自己的进程
116 int main(int argc, char** argv) {
117 if (argc > 2) {
118 LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
119 }
120 要使用的binder驱动为 /dev/binder
121 const char* driver = argc == 2 ? argv[1] : "/dev/binder";
122
初始化binder驱动
123 sp<ProcessState> ps = ProcessState::initWithDriver(driver);
124 ps->setThreadPoolMaxThreadCount(0);
125 ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
126
实例化ServiceManager 现在是在ServiceManager自己的进程
127 sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
添加自身服务
128 if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
129 LOG(ERROR) << "Could not self register servicemanager";
130 }
131 设置服务端为bbinder
132 IPCThreadState::self()->setTheContextObject(manager);
133 ps->becomeContextManager(nullptr, nullptr);
134 通过loop来处理binder事务
135 sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
136
137 BinderCallback::setupTo(looper);
138 ClientCallbackCallback::setupTo(looper, manager);
139
140 while(true) {
141 looper->pollAll(-1);
142 }
143
144 // should not be reached
145 return EXIT_FAILURE;
146 }
初始化binder驱动
349 static int open_driver(const char *driver)
350 {
打开binder驱动
351 int fd = open(driver, O_RDWR | O_CLOEXEC);
352 if (fd >= 0) {
353 int vers = 0;
验证Binder版本
354 status_t result = ioctl(fd, BINDER_VERSION, &vers);
355 if (result == -1) {
356 ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
357 close(fd);
358 fd = -1;
359 }
360 if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
361 ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
362 vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
363 close(fd);
364 fd = -1;
365 }
设置binder最大线程数
366 size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
367 result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
368 if (result == -1) {
369 ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
370 }
371 } else {
372 ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
373 }
374 return fd;
375 }
如果ServiceManager想提供服务就需要类似aidl的sub类,实现后才会提供服务
ServiceManager实现BnServiceManager才能够提供服务
68 // LooperCallback for IClientCallback
69 class ClientCallbackCallback : public LooperCallback {
70 public:
71 static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
72 sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
73
74 int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
75 LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
76
77 itimerspec timespec {
78 .it_interval = {
79 .tv_sec = 5,
80 .tv_nsec = 0,
81 },
82 .it_value = {
83 .tv_sec = 5,
84 .tv_nsec = 0,
85 },
86 };
87
88 int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, ×pec, nullptr);
89 LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);
90 监听binder描述符
91 int addRes = looper->addFd(fdTimer,
92 Looper::POLL_CALLBACK,
93 Looper::EVENT_INPUT,
94 cb,
95 nullptr);
96 LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");
97
98 return cb;
99 }
100 当binder驱动发来消息后就可以通过handleEvent函数接收处理
101 int handleEvent(int fd, int /*events*/, void* /*data*/) override {
102 uint64_t expirations;
103 int ret = read(fd, &expirations, sizeof(expirations));
104 if (ret != sizeof(expirations)) {
105 ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
106 }
107
108 mManager->handleClientCallbacks();
109 return 1; // Continue receiving callbacks.
110 }
然后通过前面setTheContextObject 传进来的ServiceManager的onTransact传递 给BnServiceManager
接下来看几个Binder的方法
static int binder_open(struct inode *nodp, struct file *filp)
{
//结构体1 下边有分析内部结构
struct binder_proc *proc;
struct binder_device *binder_dev;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
current->group_leader->pid, current->pid);
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
spin_lock_init(&proc->inner_lock);
spin_lock_init(&proc->outer_lock);
get_task_struct(current->group_leader);
proc->tsk = current->group_leader;
mutex_init(&proc->files_lock);
INIT_LIST_HEAD(&proc->todo);
if (binder_supported_policy(current->policy)) {
proc->default_priority.sched_policy = current->policy;
proc->default_priority.prio = current->normal_prio;
} else {
proc->default_priority.sched_policy = SCHED_NORMAL;
proc->default_priority.prio = NICE_TO_PRIO(0);
}
binder_dev = container_of(filp->private_data, struct binder_device,
miscdev);
proc->context = &binder_dev->context;
//与binder申请内存地址息息相关
//初始化对应binder_alloc
binder_alloc_init(&proc->alloc);
binder_stats_created(BINDER_STAT_PROC);
//应用程序的pid,调用方的pid
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->waiting_threads);
//private_data 被赋值为 proc 接下来mmap会用到
filp->private_data = proc;
mutex_lock(&binder_procs_lock);
//将数据放入列表头(不重要)
hlist_add_head(&proc->proc_node, &binder_procs);
mutex_unlock(&binder_procs_lock);
//类似debug类型的不重要
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
/*
* proc debug entries are shared between contexts, so
* this will fail if the process tries to open the driver
* again with a different context. The priting code will
* anyway print all contexts that a given PID has, so this
* is not a problem.
*/
proc->debugfs_entry = debugfs_create_file(strbuf, 0444,
binder_debugfs_dir_entry_proc,
(void *)(unsigned long)proc->pid,
&binder_proc_fops);
}
return 0;
}
struct binder_proc {
struct hlist_node proc_node;
//binder的线程数
struct rb_root threads;
//所谓bpbinder rb_root 是一个红黑树,提高效率
struct rb_root nodes;
//引索
struct rb_root refs_by_desc;
//节点
struct rb_root refs_by_node;
int pid;
struct vm_area_struct *vma;
struct task_struct *tsk;
struct files_struct *files;
struct hlist_node deferred_work_node;
int deferred_work;
void *buffer;
ptrdiff_t user_buffer_offset;
struct list_head buffers;
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space;
struct page **pages;
size_t buffer_size;
uint32_t buffer_free;
struct list_head todo;
wait_queue_head_t wait;
struct binder_stats stats;
struct list_head delivered_death;
int max_threads;
int requested_threads;
int requested_threads_started;
int ready_threads;
long default_priority;
};
MMap
binder-map内存 与 server 进程会进行mmap进行映射
//vm_area_struct linux 0-3G 连续的虚拟的结构体
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
//先拿到 binder_open里赋值的 private_data 也就是申请内存的
struct binder_proc *proc = filp->private_data;
const char *failure_string;
if (proc->tsk != current->group_leader)
return -EINVAL;
//如果传入的参数超过4m就只能给4m
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
//log
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
__func__, proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
//检测是否失败
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
vma->vm_flags &= ~VM_MAYWRITE;
//这里是回调方法 但是里面没有做什么事情 只是用于打印
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
关键方法,初始化的alloc 和内存区域 都传入到了 binder_alloc_mmap_handler方法里
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
if (ret)
return ret;
mutex_lock(&proc->files_lock);
proc->files = get_files_struct(current);
mutex_unlock(&proc->files_lock);
return 0;
err_bad_arg:
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
int ret;
struct vm_struct *area;
const char *failure_string;
struct binder_buffer *buffer;
mutex_lock(&binder_alloc_mmap_lock);
//先判断一下alloc 是否被map过,第一次进入是不会被map过的
if (alloc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
//构造一个内核虚拟地址的方法 申请空间
area = get_vm_area(vma->vm_end - vma->vm_start, VM_ALLOC);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
//初始化为内核的一个虚拟的首地址,这里已经初始化完成
alloc->buffer = area->addr;
//用户空间对内核空间的偏移量
//申请到的空间减去 buffer 也就是内核空间 用户空间首地址 - 内核空间首地址
alloc->user_buffer_offset =
vma->vm_start - (uintptr_t)alloc->buffer;
mutex_unlock(&binder_alloc_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR(
(vma->vm_start ^ (uint32_t)alloc->buffer))) {
pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n",
alloc->pid, vma->vm_start, vma->vm_end,
alloc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
//需要多少页?初始化为0 一般是4096
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
GFP_KERNEL);
if (alloc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
//大小
alloc->buffer_size = vma->vm_end - vma->vm_start;
//申请好了一个壳
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
ret = -ENOMEM;
failure_string = "alloc buffer struct";
goto err_alloc_buf_struct_failed;
}
buffer->data = alloc->buffer;
//添加到list里
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
binder_insert_free_buffer(alloc, buffer);
//异步空间只有同步空间的一半
alloc->free_async_space = alloc->buffer_size / 2;
barrier();
alloc->vma = vma;
alloc->vma_vm_mm = vma->vm_mm;
/* Same as mmgrab() in later kernel versions */
atomic_inc(&alloc->vma_vm_mm->mm_count);
return 0;
err_alloc_buf_struct_failed:
kfree(alloc->pages);
alloc->pages = NULL;
err_alloc_pages_failed:
mutex_lock(&binder_alloc_mmap_lock);
vfree(alloc->buffer);
alloc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
mutex_unlock(&binder_alloc_mmap_lock);
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
那mmap到底干了啥?首先进行内核偏移的计算,在内核把用户空间的内存区域和内核空间的内存区域都进行了申请,将内核空间和用户空间的内存偏移计算出来干什么?后面如果通过内核空间寻找用户空间只需要知道偏移量就可以进行映射
但是好像没有申请内存?
新的版本是当你发起申请的时候动态给你内存,而不是一开始给你的1m-8k的内存
当你需要的时候,会按照物理页(page)给你内存 那么是如何做到的?
static int binder_update_page_range(struct binder_proc *proc, int allocate,
void *start, void *end, struct vm_area_struct *vma)
{
void *page_addr;
unsigned long user_page_addr;
struct vm_struct tmp_area;
struct page **page;
struct mm_struct *mm;
if (binder_debug_mask & BINDER_DEBUG_BUFFER_ALLOC)
printk(KERN_INFO "binder: %d: %s pages %p-%p\n",
proc->pid, allocate ? "allocate" : "free", start, end);
if (end <= start)
return 0;
if (vma)
mm = NULL;
else
mm = get_task_mm(proc->tsk);
if (mm) {
down_write(&mm->mmap_sem);
vma = proc->vma;
}
if (allocate == 0)
goto free_range;
if (vma == NULL) {
printk(KERN_ERR "binder: %d: binder_alloc_buf failed to "
"map pages in userspace, no vma\n", proc->pid);
goto err_no_vma;
}
//根据start和end 的大小 计算申请对应的内存页
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
struct page **page_array_ptr;
算出需要多少物理页
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
BUG_ON(*page);
*page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (*page == NULL) {
printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
"for page at %p\n", proc->pid, page_addr);
goto err_alloc_page_failed;
}
tmp_area.addr = page_addr;
tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
page_array_ptr = page;
算出内存空间的内存地址
ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
if (ret) {
printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
"to map page at %p in kernel\n",
proc->pid, page_addr);
goto err_map_kernel_failed;
}
将用户空间的偏移地址计算出
user_page_addr =
(uintptr_t)page_addr + proc->user_buffer_offset;
vma是用户空间的内存地址 进行赋值
ret = vm_insert_page(vma, user_page_addr, page[0]);
if (ret) {
printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
"to map page at %lx in userspace\n",
proc->pid, user_page_addr);
goto err_vm_insert_page_failed;
}
/* vm_insert_page does not seem to increment the refcount */
}
if (mm) {
up_write(&mm->mmap_sem);
mmput(mm);
}
return 0;
free_range:
那为什么这里又有一个类似的行为?
for (page_addr = end - PAGE_SIZE; page_addr >= start;
page_addr -= PAGE_SIZE) {
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
if (vma)
zap_page_range(vma, (uintptr_t)page_addr +
proc->user_buffer_offset, PAGE_SIZE, NULL);
err_vm_insert_page_failed:
最后释放
unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
err_map_kernel_failed:
__free_page(*page);
*page = NULL;
err_alloc_page_failed:
;
}
err_no_vma:
if (mm) {
up_write(&mm->mmap_sem);
mmput(mm);
}
return -ENOMEM;
}