音视频学习阶段(二)Binder native 源码学习(一)

124 阅读9分钟

关于Binder的 FrameWork 相关的代码逻辑,我在我的简书FrameWork专栏 中有总结过,但是在学习 android native 的过程中发现Binder 仍然是非常重要的一部分,那么就先分析一下 binder native 的源码逻辑 由于我最近在看音视频相关的,所以这里就分析一下 main_mediaserver.cpp 的流程

所有源码均来自 Android 11

C++ main_mediaserver.cpp


int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);
    // 获取一个 ProcessState 这个过程非常重要
    sp<ProcessState> proc(ProcessState::self());
    // 获取一个 ServiceManager 0 这个级别就代表的是 ServiceManager 
    sp<IServiceManager> sm(defaultServiceManager());
    ALOGI("ServiceManager: %p", sm.get());
    AIcu_initializeIcuOrDie();
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
    registerExtensions();
    ::android::hardware::configureRpcThreadpool(16, false);
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    ::android::hardware::joinRpcThreadpool();
}

可以看到这里先是通过 ProcessState::self() 获取了一个 ProcessState ,我们来看看这个里面做了什么

sp<ProcessState> ProcessState::self()
{
    // 析构同步锁来获取一个单利 ProcessState
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != nullptr) {
        return gProcess;
    }
    gProcess = new ProcessState(kDefaultDriver);
    return gProcess;
}

这里利用同步锁获取一个单利的 ProcessState,继续跟踪 new ProcessState

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)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mBinderContextCheckFunc(nullptr)
    , mBinderContextUserData(nullptr)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
    , mCallRestriction(CallRestriction::NONE)
{
    if (mDriverFD >= 0) {
        //重要逻辑
	//调用mmap接口向Binder驱动中申请内核空间的内存
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        //也就是binder 的底层原来是通过 mmap 来实现的,mVMStart记录的就是共享内存的开始节点
        mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
            close(mDriverFD);
            mDriverFD = -1;
            mDriverName.clear();
        }
    }

#ifdef __ANDROID__
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened.  Terminating.", driver);
#endif
}

在 ProcessState 的初始化代码中看到了一段注释,就是利用mmap向内核空间申请内存,传入的就是 mDriverFD ,那么证明 在给 mDriverFD 赋值的操作就是非常重要的一步,继续跟踪一下 open_driver(driver),参数 driver 是一个常量

#ifdef __ANDROID_VNDK__
const char* kDefaultDriver = "/dev/vndbinder";
#else
const char* kDefaultDriver = "/dev/binder";
#endif

static int open_driver(const char *driver)
{
	//打开binder驱动
    int fd = open(driver, O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
	//验证binder版本
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            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));
        }
    } else {
        ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
    }
    return fd;
}

这里面使用了是C语言中的打开一个文件的方式来打开一个驱动,同时调用 ioctl 这个方法, 没有找到这个源码在哪里,同时也没有什么思路,但是百度了一下关于 ioctl 方面的东西, 这里有一篇文章 他这里说明了 ioctl 的作用,我这里摘抄一下 ioctl功能简介 open、write函数的功能无非就是为了进行用户空间和内核空间的数据交换,而ioctl呢? 大部分驱动除了需要具备读写设备的能力之外,还需要对设备具有控制能力,比如要求设备报告错误信息,弹出介质,设置波特率等。很明显我们这里就是验证版本号的功能,同时给binder 设置了 最大线程数 15 个,我个人猜测这里应该是最多有15个线程同时访问这个驱动文件,而不是线程只能有15个线程访问这个文件,并将这个数量通过 ioctl 方式写入的 binder文件中,在面试过程中有被问到过 Binder 线程数超量而引起的崩溃问题,那么解释就在这里了

到了这里我们就了解了 ProcessState::self() 的整个过程,首先通过同步锁获取了一个单利的 ProcessState ,如果存在就返回,不存在就 new 一个出来,在new 的过程中打开了 /dev/binder 这个驱动文件,验证了版本号,同时还写入了最大线程数,最后在打开驱动文件成功后,通过 mmap 的方式申请内核空间的内存,并将这段空间的开始位置交给 mVMStart

说道 mmap 就让我想起了 mmkv 中的用法,以及 mmap 配合 ProtoBuf 高效的文件映射,想了解的同学可以自行搜索一下,关于这些问题现在只是了解这个概念,还没有深入的研究

我们重新看一下 main_mediaserver.cpp

int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);

    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm(defaultServiceManager());
    ALOGI("ServiceManager: %p", sm.get());
    AIcu_initializeIcuOrDie();
    MediaPlayerService::instantiate();
    ResourceManagerService::instantiate();
    registerExtensions();
    ::android::hardware::configureRpcThreadpool(16, false);
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    ::android::hardware::joinRpcThreadpool();
}

我们来看看 defaultServiceManager 这个方法是如何获取到 IServiceManager 这个对象的

sp<IServiceManager> defaultServiceManager()
{
    std::call_once(gSmOnce, []() {
        sp<AidlServiceManager> sm = nullptr;
        while (sm == nullptr) {
			//sm 就是
            sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
            if (sm == nullptr) {
                ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());
                sleep(1);
            }
        }

        gDefaultServiceManager = new ServiceManagerShim(sm);
    });

    return gDefaultServiceManager;
}

这里也使用了一个C++的同步机制, 来循环的调用 ProcessState::self()->getContextObject(nullptr));在上面我们已经分析过了 ProcessState::self() 的流程,下面分析一下 getContextObject(nullptr))

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    //handle句柄0代表的就是 ServiceManager,所以这里调用getStrongProxyForHandle函数的参数为0
    sp<IBinder> context = getStrongProxyForHandle(0);

    if (context == nullptr) {
       ALOGW("Not able to get context object on %s.", mDriverName.c_str());
    }

    // The root object is special since we get it directly from the driver, it is never
    // written by Parcell::writeStrongBinder.
    internal::Stability::tryMarkCompilationUnit(context.get());

    return context;
}

这里就是调用了一下 getStrongProxyForHandle 这个方法,注意的是 这里传入的参数是 0 ,0 代表的就是 ServiceManager,至于是为什么 ,我们接着分析

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

	//查找或建立handle对应的handle_entry
    handle_entry* e = lookupHandleLocked(handle);

    if (e != nullptr) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  The
        // attemptIncWeak() is safe because we know the BpBinder destructor will always
        // call expungeHandle(), which acquires the same lock we are holding now.
        // We need to do this because there is a race condition between someone
        // releasing a reference on this BpBinder, and a new reference on its handle
        // arriving from the driver.
        IBinder* b = e->binder;
        // 如果binder 为空  ,另一个参数在Binder 创建时 添加的
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // 下面这段注释就是说明为什么 0 是 ServiceManager
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.
		//当handle为ServiceManager的特殊情况
		//需要确保在创建Binder引用之前,context manager已经被binder注册
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, nullptr, 0);
                if (status == DEAD_OBJECT)
                   return nullptr;
            }

            //创建BpBinder并保存下来以便后面再次查找
            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

这里的注释比较多,但是我一句也没有删掉,原因是通过注释可以让你了解这么调用的意义,他在这段代码当中先是调用了 lookupHandleLocked 这个方法 来获取一个 handle_entry 指针, 我们来看一下

//方法用于查找本进程中是否已经创建过要获取的 IBinder,如果没有获取到,就创建一个
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = nullptr;
        e.refs = nullptr;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return nullptr;
    }
    return &mHandleToObject.editItemAt(handle);
}

显示获取当前的 handle_obj 的大小,如果传入的 handle 大于 这个size ,证明需要创建 并插入 ,最后获取在获取一下,通过代码也能看到这里缓存的是 binder

我们再继续分析剩余的代码 那就是如果获取的 handle_entry 中的binder 为空,并且 handle 是0 的话,也就是需要初始化 ServiceManager ,就需要调用 IPCThreadState::self()->transact( 0, IBinder::PING_TRANSACTION, data, nullptr, 0); 这个方法,来确保一下 context manager的状态,这里同样到了系统级别指令的读写操作了,感兴趣的同学可以百度一下 IPCThreadState::transact , 我看了一下相应的文章,这里就不过多的纠结了,

接下来就到了重头戏 创建 BpBinder 了 BpBinder::create(handle); 我们来跟踪一下这个create 的过程

BpBinder* BpBinder::create(int32_t handle) {
    int32_t trackedUid = -1;
    if (sCountByUidEnabled) {
        trackedUid = IPCThreadState::self()->getCallingUid();
        AutoMutex _l(sTrackingLock);
        uint32_t trackedValue = sTrackingMap[trackedUid];
        if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) {
            if (sBinderProxyThrottleCreate) {
                return nullptr;
            }
        } else {
            if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
                ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
                      getuid(), trackedUid, trackedValue);
                sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
                if (sLimitCallback) sLimitCallback(trackedUid);
                if (sBinderProxyThrottleCreate) {
                    ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
                          " count drops below %d",
                          trackedUid, getuid(), sBinderProxyCountLowWatermark);
                    return nullptr;
                }
            }
        }
        sTrackingMap[trackedUid]++;
    }
    return new BpBinder(handle, trackedUid);
}

这里又查找了另一个 id ,通过这两个id 来创建 BpBinder

BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
    : mHandle(handle)
    , mStability(0)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(nullptr)
    , mTrackedUid(trackedUid)
{
    ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);

    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    // 这个对象就是 getStrongProxyForHandle 方法中 判断 binder 为空的另一个条件
    IPCThreadState::self()->incWeakHandle(handle, this);
}

先来看看 IPCThreadState::self() 方法干了什么

IPCThreadState* IPCThreadState::self()
{
    
    if (gHaveTLS.load(std::memory_order_acquire)) {
restart:
        const pthread_key_t k = gTLS;
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st;
        return new IPCThreadState;
    }

    // Racey, heuristic test for simultaneous shutdown.
    if (gShutdown.load(std::memory_order_relaxed)) {
        return nullptr;
    }

    pthread_mutex_lock(&gTLSMutex);
    if (!gHaveTLS.load(std::memory_order_relaxed)) {
        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 nullptr;
        }
        gHaveTLS.store(true, std::memory_order_release);
    }
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

这里的逻辑确实有点绕,我们这里重点关注第一个if 里面的操作,显示获取 gTLS ,然后在 利用这个 从 specific 中获取了一个 IPCThreadState,如果不为null 则返回,反之则创建,至于剩下的逻辑需要结合后面的代码一起分析才可以,这里先不用管, gTLS 是什么 specific 又从哪里获取的,继续分析 IPCThreadState 构造方法

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      mServingStackPointer(nullptr),
      mWorkSource(kUnsetWorkSource),
      mPropagateWorkSource(false),
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0),
      mCallRestriction(mProcess->mCallRestriction)
{
    pthread_setspecific(gTLS, this);
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

关于 tls 就是 Thread-Local Storage ,关于他的介绍我在网上看到一个比较不错的文章,传送门 , 如果让我用 java 的思路来说就是一个 threadLocal与 threadLoaclMap 的关系,这个数据只有当前线程能访问到

那么我们再回来捋一下 IPCThreadState 创建的流程就是 在创建 IPCThreadState 的过程中,需要先调用 ProcessState::self(),同时向 specific 中添加 gTLS,

到了这里创建 ServiceManager 的BpBinder 的流程就完成了, 我先再来回顾一下整个过程 首先调用 defaultServiceManager 来获取一个 IServiceManager ,在通过 ProcessState::self()->getContextObject(nullptr) 来获取, 通过这个 handle 句柄先去 handle_entry 中查找,如果没有就创建 BpBinder , 在创建 BpBinder 的同时还将 handle 句柄与 他自身使用 incWeakHandle 缓存起来