当前位置: 首页 > news >正文

Android8 binder源码学习分析笔记(三)

前文回顾:

https://blog.csdn.net/g_i_a_o_giao/article/details/151221566?spm=1001.2014.3001.5501https://blog.csdn.net/g_i_a_o_giao/article/details/151221566?spm=1001.2014.3001.5501https://blog.csdn.net/g_i_a_o_giao/article/details/151158611?spm=1001.2014.3001.5501https://blog.csdn.net/g_i_a_o_giao/article/details/151158611?spm=1001.2014.3001.5501上文我们分析了startActivity中framework层面的源码,知道了在Activity启动过程中binder的作用。接下来我们继续分析一下binder驱动是如何启动的。

在Zygote的启动过程中,fork完SystemServer的进程以后,会调用handleSystemServerProcess方法,在这个方法中会调用ZygoteInit.zygoteInit方法。那么我们重新看一下这个方法。之前我们只分析了RuntimeInit.applicationInit方法,今天我们来看看nativeZygoteInit方法。可以看到调用的是native层的方法。

 public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {if (RuntimeInit.DEBUG) {Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");}Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");RuntimeInit.redirectLogStreams();RuntimeInit.commonInit();ZygoteInit.nativeZygoteInit();return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);}private static final native void nativeZygoteInit();

在/frameworks/base/core/jni/AndroidRuntime.cpp下我们找到了cpp层面实现的nativeZygoteInit方法。在这个方法中调用了子类的回调方法onZygoteInit。

static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{gCurRuntime->onZygoteInit();
}

之前我们已经分析过,app_process.cpp是继承自AndroidRuntime的。那么执行的就是app_process中的onZygoteInit方法。在这个方法中获取了ProcessState类的静态方法self,在这个方法中,主要是创建了ProcessState对象。

virtual void onZygoteInit(){sp<ProcessState> proc = ProcessState::self();ALOGV("App process: starting thread pool.\n");proc->startThreadPool();}ProcessState.cpp:
sp<ProcessState> ProcessState::self()
{Mutex::Autolock _l(gProcessMutex);if (gProcess != NULL) {return gProcess;}gProcess = new ProcessState("/dev/binder");return gProcess;
}

继续分析ProcessState对象,在这个对象的构造方法中,核心是调用了open_driver方法。

// ProcessState 构造函数,参数 driver 指定 Binder 驱动设备路径(如 "/dev/binder")
ProcessState::ProcessState(const char *driver): mDriverName(String8(driver))          // 存储驱动设备名称, mDriverFD(open_driver(driver))        // 打开驱动设备,返回文件描述符, mVMStart(MAP_FAILED)                  // 初始映射地址设为失败状态, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)      // 初始化线程计数互斥锁, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)  // 初始化线程数减少条件变量, mExecutingThreadsCount(0)             // 当前执行中的线程数初始化为0, mMaxThreads(DEFAULT_MAX_BINDER_THREADS) // 最大Binder线程数(默认值), mStarvationStartTimeMs(0)             // 线程饥饿开始时间戳, mManagesContexts(false)               // 是否管理上下文标记, mBinderContextCheckFunc(NULL)         // Binder上下文检查函数指针, mBinderContextUserData(NULL)          // 上下文检查的用户数据, mThreadPoolStarted(false)             // 线程池是否已启动标记, mThreadPoolSeq(1)                     // 线程池序列号(用于线程命名)
{// 如果成功打开驱动设备(文件描述符有效)if (mDriverFD >= 0) {// 使用 mmap 映射内核空间的内存到用户空间,用于接收事务数据// 参数说明:// - 0: 由系统选择映射地址// - BINDER_VM_SIZE: 映射区域大小(通常为1MB-8MB)// - PROT_READ: 仅读权限(Binder驱动负责写入)// - MAP_PRIVATE: 私有映射(不共享)| MAP_NORESERVE: 不保留交换空间// - mDriverFD: Binder驱动文件描述符// - 0: 偏移量为0mVMStart = 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;            // 标记为无效值mDriverName.clear();       // 清空驱动名称}}// 如果驱动初始化失败(文件描述符无效),终止进程LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

那我们继续去分析一下open_driver方法。在这个方法中主要调用了open方法打开设备文件(这里是/dev/binder)。
然后调用了ioctl方法,在这里简单介绍一下ioctl方法:ioctl 的全称是 Input/Output Control,即输入输出控制。它是一个用于对设备进行特定操作的系统调用。简单讲就是让linux中的用户空间和驱动进行交互。
在open_driver方法中。ioctl有两处调用。

status_t result = ioctl(fd, BINDER_VERSION, &vers);用户获取Binder驱动的版本号。
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);用于设置Binder线程池可以创建的最大线程数
#define DEFAULT_MAX_BINDER_THREADS 15// 打开 Binder 驱动设备文件并初始化
// 参数 driver: Binder 设备文件的路径(如 "/dev/binder")
// 返回值: 成功返回文件描述符,失败返回 -1
static int open_driver(const char *driver)
{// 以读写方式打开设备文件,并设置 O_CLOEXEC 标志(进程执行 exec 时自动关闭该文件描述符)int fd = open(driver, O_RDWR | O_CLOEXEC);if (fd >= 0) {  // 打开成功int vers = 0;// 通过 ioctl 获取 Binder 驱动版本status_t result = ioctl(fd, BINDER_VERSION, &vers);if (result == -1) {  // ioctl 操作失败ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));close(fd);fd = -1;}// 检查驱动版本与用户空间版本是否匹配if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",vers, BINDER_CURRENT_PROTOCOL_VERSION, result);close(fd);fd = -1;}// 设置 Binder 线程池的最大线程数size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);if (result == -1) {ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));// 注意:这里没有关闭 fd,因为版本检查已通过,设置线程数失败不影响继续使用}} else {// 打开设备文件失败ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));}return fd;
}

至此Binder驱动就已经连接并且初始化成功了。我们再回到app_main.cpp的onZygoteInit方法,在调用了ProcessState的self方法以后,又调用了ProcessState的startThreadPool方法,在这个方法中又调用了spawnPooledThread方法,那么在这个方法中创建了PoolThread对象并且调用了该对象的run方法。


void ProcessState::startThreadPool()
{AutoMutex _l(mLock);if (!mThreadPoolStarted) {mThreadPoolStarted = true;spawnPooledThread(true);}
}void ProcessState::spawnPooledThread(bool isMain)
{if (mThreadPoolStarted) {// 创建binder线程的名字String8 name = makeBinderThreadName();ALOGV("Spawning new pooled thread, name=%s\n", name.string());// 创建PoolThread对象sp<Thread> t = new PoolThread(isMain);t->run(name.string());}
}

我们来看看这个PoolThread内部类。这个类继承自Thread类,在调用run方法以后,会执行该对象的threadLoop方法。在这个方法中,调用了IPCThreadState的self方法,然后调用了joinThreadPool方法。


class PoolThread : public Thread
{
public:explicit PoolThread(bool isMain): mIsMain(isMain){}protected:virtual bool threadLoop(){IPCThreadState::self()->joinThreadPool(mIsMain);return false;}const bool mIsMain;
};

然后我们去frameworks/native/libs/binder/IPCThreadState.cpp中看一下self方法。在这个方法中,核心就是调用pthread_key_create方法来创建key,然后再回到restart代码段,尝试用生成的key获取IPCThreadState对象,获取不到就创建一个IPCThreadState对象。

static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
static bool gHaveTLS = false;
static pthread_key_t gTLS = 0;
static bool gShutdown = false;
static bool gDisableBackgroundScheduling = false;// 获取当前线程的 IPCThreadState 单例对象
// 返回值: 当前线程的 IPCThreadState 对象指针,失败时返回 NULL
IPCThreadState* IPCThreadState::self()
{// 检查是否已经初始化了线程局部存储(TLS)if (gHaveTLS) {// 使用 goto 标签,用于在初始化 TLS 后重新尝试获取
restart:// 获取 TLS 密钥const pthread_key_t k = gTLS;// 从当前线程的 TLS 中获取存储的 IPCThreadState 对象IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);// 如果 TLS 中已经存在对象,直接返回if (st) return st;// 如果 TLS 中不存在对象,创建一个新的 IPCThreadState 实例// 注意:IPCThreadState 的构造函数会自动将自身设置到当前线程的 TLS 中return new IPCThreadState;}// 如果系统正在关闭,发出警告并返回 NULLif (gShutdown) {ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");return NULL;}// 加锁,确保多线程环境下 TLS 初始化的线程安全pthread_mutex_lock(&gTLSMutex);// 双重检查:在锁内再次检查是否已经初始化 TLS(防止竞争条件)if (!gHaveTLS) {// 创建 TLS 密钥,并指定析构函数 threadDestructor// 当线程退出时,会自动调用 threadDestructor 清理资源int key_create_value = pthread_key_create(&gTLS, threadDestructor);if (key_create_value != 0) { // 创建密钥失败pthread_mutex_unlock(&gTLSMutex);ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",strerror(key_create_value));return NULL;}// 标记 TLS 已初始化gHaveTLS = true;}// 释放锁pthread_mutex_unlock(&gTLSMutex);// 跳转到 restart 标签,重新尝试从 TLS 获取 IPCThreadState 对象goto restart;
}

我们来看看IPCThreadState对象的构造方法。主要是对每个线程进行标记。可以看出来,每个binder线程之间都有差别,会拥有不同的IPCThreadState对象。

IPCThreadState::IPCThreadState(): mProcess(ProcessState::self()),mStrictModePolicy(0),mLastTransactionBinderFlags(0)
{pthread_setspecific(gTLS, this);clearCaller();mIn.setDataCapacity(256);mOut.setDataCapacity(256);
}

再回去看PoolThread中的threadLoop方法,在调用IPCThreadState的self方法以后,又调用了它的joinThreadPool方法。在这个方法中会进入一个循环,每次循环都会调用getAndExecuteCommand方法。

// 让当前线程加入 Binder 线程池,开始处理 IPC 事务
// 参数 isMain: 标识当前线程是否为主线程(通常是应用的主线程)
void IPCThreadState::joinThreadPool(bool isMain)
{// 记录日志,显示线程加入线程池的信息LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());// 根据线程类型向驱动发送不同的命令// 如果是主线程,发送 BC_ENTER_LOOPER,表示这是一个永久性循环线程// 如果是工作线程,发送 BC_REGISTER_LOOPER,表示这是一个可退出的线程mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);status_t result;do {// 处理待处理的引用计数减量(对象引用计数管理)processPendingDerefs();// 获取并执行下一个 Binder 命令,如果没有命令则等待result = getAndExecuteCommand();// 检查执行结果,处理异常情况if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {// 遇到意外错误,记录错误日志并中止进程ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",mProcess->mDriverFD, result);abort();}// 如果非主线程执行超时且没有工作可做,则退出线程池if(result == TIMED_OUT && !isMain) {break;}} while (result != -ECONNREFUSED && result != -EBADF); // 当驱动连接被拒绝或文件描述符错误时退出循环// 记录线程离开线程池的日志LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",(void*)pthread_self(), getpid(), result);// 向驱动发送退出循环的命令mOut.writeInt32(BC_EXIT_LOOPER);// 与驱动进行最后一次通信,确保退出命令被发送talkWithDriver(false);
}

那么来看看这个getAndExecuteCommand方法,在这个方法中首先是调用talkWithDriver方法与binder驱动进行通信,然后调用executeCommand方法执行具体的binder命令。

// 获取并执行一个 Binder 命令
// 返回值: 执行结果的状态码
status_t IPCThreadState::getAndExecuteCommand()
{status_t result;int32_t cmd;// 与 Binder 驱动通信,发送输出缓冲区的数据并接收输入数据result = talkWithDriver();if (result >= NO_ERROR) {  // 通信成功// 获取输入缓冲区中可用数据的大小size_t IN = mIn.dataAvail();// 如果可用数据小于一个 int32_t 的大小(命令的最小单位),直接返回if (IN < sizeof(int32_t)) return result;// 从输入缓冲区读取一个命令(32位整数)cmd = mIn.readInt32();// 如果启用了命令日志,记录正在处理的命令IF_LOG_COMMANDS() {alog << "Processing top-level Command: "<< getReturnString(cmd) << endl;}// 加锁,准备修改进程的线程计数状态pthread_mutex_lock(&mProcess->mThreadCountLock);// 增加当前正在执行命令的线程计数mProcess->mExecutingThreadsCount++;// 检查是否所有线程都在忙碌(达到最大线程数)并且饥饿计时尚未开始if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&mProcess->mStarvationStartTimeMs == 0) {// 记录线程池开始饥饿的时间点mProcess->mStarvationStartTimeMs = uptimeMillis();}// 释放锁pthread_mutex_unlock(&mProcess->mThreadCountLock);// 执行具体的命令result = executeCommand(cmd);// 再次加锁,更新线程计数状态pthread_mutex_lock(&mProcess->mThreadCountLock);// 减少当前正在执行命令的线程计数mProcess->mExecutingThreadsCount--;// 检查线程池是否从饥饿状态恢复(线程数低于最大值且有饥饿记录)if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&mProcess->mStarvationStartTimeMs != 0) {// 计算饥饿持续时间int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;// 如果饥饿时间超过100ms,记录错误日志if (starvationTimeMs > 100) {ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",mProcess->mMaxThreads, starvationTimeMs);}// 重置饥饿开始时间mProcess->mStarvationStartTimeMs = 0;}// 广播通知等待线程计数减少的条件变量pthread_cond_broadcast(&mProcess->mThreadCountDecrement);// 释放锁pthread_mutex_unlock(&mProcess->mThreadCountLock);}return result;
}

由于篇幅限制,关于与binder驱动通信及执行binder命令的相关问题,我们将在后续笔记中详细分析。

小结:我们已经知道了在ZygoteInit.nativeZygoteInit方法中,首先调用ProcessState.java的self方法与binder对象建立了连接并且进行了初始化。然后再调用startThreadPool方法创建了binder线程池,又为每个binder线程创建了IPCProcessState对象。最后调用了

IPCProcessState的joinThreadPool方法,在这个方法中会进入一个循环,在循环中不断处理binder命令。


文章转载自:

http://lvPOStz8.wrtxk.cn
http://vli5MC3r.wrtxk.cn
http://ANHh6Vny.wrtxk.cn
http://RAG87Bgi.wrtxk.cn
http://hYq5SIba.wrtxk.cn
http://dMB60LV9.wrtxk.cn
http://jQP9bsMI.wrtxk.cn
http://OhrzeFyV.wrtxk.cn
http://76B01SVu.wrtxk.cn
http://F5Kev7Oe.wrtxk.cn
http://QeRZ3m35.wrtxk.cn
http://Dcoh15u9.wrtxk.cn
http://M13q0O1d.wrtxk.cn
http://tAqCwIoV.wrtxk.cn
http://rPZztdFz.wrtxk.cn
http://uiNW5MwD.wrtxk.cn
http://ptH3M7FQ.wrtxk.cn
http://bkmpsNoe.wrtxk.cn
http://5a73y63w.wrtxk.cn
http://zHH63kXc.wrtxk.cn
http://jXqMEz9e.wrtxk.cn
http://5bVaZVLV.wrtxk.cn
http://PDfXeOH9.wrtxk.cn
http://3J53iR8N.wrtxk.cn
http://WlfsEVwq.wrtxk.cn
http://xSSVIjB7.wrtxk.cn
http://nmrxH8ah.wrtxk.cn
http://Ytvteoex.wrtxk.cn
http://YAIcZJSz.wrtxk.cn
http://IIuVvOy9.wrtxk.cn
http://www.dtcms.com/a/379081.html

相关文章:

  • sizeof 和 strlen
  • 2025年度4款录音转文字工具横向对比
  • 教资科三【信息技术】— 学科知识(简答题)精简背诵版
  • 滚动列表展示跟随弹框效果
  • readelf 和 ldd 查看文件的依赖
  • 基于社交媒体数据的公众情绪指数构建与重大事件影响分析
  • Cosign 实战:构建可信容器镜像的签名与验证体系
  • 定时器实战:LED闪烁与呼吸灯调试
  • docker部署Gitlab社区版,步骤以及外网访问出现502的解决方式
  • FairGuard aab包签名工具
  • 企业文件图纸全自动加密怎么设置?三步实现自动防护!
  • Redis C++ 实现笔记(I篇)
  • [css] 实现禁止文本被选中
  • MATLAB中进行视觉检测入门教程
  • 人工智能深度学习——多层感知器(人工神经网络)
  • 2025最新超详细FreeRTOS入门教程:第十二章 FreeRTOS调度器与时间片管理
  • 软考系统架构设计师之项目管理篇
  • OpenSTL PredRNNv2 模型复现与自定义数据集训练
  • 基于STM32的单片机开发复盘
  • Git 目录详解和基本操作(1)
  • 机器学习之K折交叉验证
  • Android Gradle Project (AGP) gradle-xxxx-src.zip无法正常下载问题解决方法
  • 图观 应用编辑器 产品介绍
  • 探讨Hyperband 等主要机器学习调优方法的机制和权衡
  • Apple产品发布会拆解:体验下放、设计极限、AI 入耳
  • 如何解决 Spring Bean 循环依赖
  • sdio NOT_AUTOGATING
  • 华为X考拉悠然 联合发布悠然智擎城市交通拥堵治理空间智能体方案
  • 《微服务事务管理》
  • CentOS 7 安装 MySQL 详细教程