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

发布于:2025-09-14 ⋅ 阅读:(24) ⋅ 点赞:(0)

前文回顾:

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: 偏移量为0
        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;            // 标记为无效值
            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;
    }

    // 如果系统正在关闭,发出警告并返回 NULL
    if (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命令。