Android系统中的Binder通信机制分析(6)- Binder通信机制详解

news2024/11/28 3:39:40

声明

  • 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾…
  • 文中参考了很多书籍及博客内容,可能涉及的比较多先不具体列出来了;
  • 本文使用的代码是LineageOS的cm-14.1,对应Android 7.1.2,可以参考我的另一篇博客:cm-14.1 Android系统启动过程分析(1)-如何下载Nexus5的LineageOS14.1(cm-14.1)系统源码并编译、刷机

1 Binder封装库

在 Android 系统中,在各个层次都有和 Binder 有关的实现。其中主要的 Binder 库由本地原生代码实现。Binder库核心部分的各个头文件结构如下所示:

  • Parcel.h : 定义类 Parcel,为在 IPC 中传输的数据定义容器。
  • IBinder.h : 定义类 IBinder,是 Binder 对象的抽象接口。
  • Binder.h : 定义类 BBinder 和 BpRefBase 分别为 IBinder 类的继承者和服务器端使用的基类。
  • BpBinder.h : 定义类 BpBinder,为客户端基类。
  • IInterface.h : 定义类 IInterface、模板类 BnInterface、模板类 BpInterface,这几个类是具体 Binder 实现中需要使用的内容。
  • ProcessState.h : 定义类ProcessState,表示进程的状态。
  • IPCThreadState.h : 定义类 IPCThreadState,表示 IPC 线程的状态。

  从 Binder 系统对外接口的角度,IInterface 用于表示一个“接口”;BnInterface<>模板类用于表示本地的服务器端,它继承自IBinder; BpInterface 模板类用于表示客户端的代理者,它可以指向一个IBinder。
  从 Binder 实现的角度,核心的内容是 BBinder 和 BpBinder 两个类,它们分别代表了最基本的服务器端及客户端。在使用的过程中,通常函数调用动作及参数都被转换成用 index 标记的数据流,通过 BpBinder.transact() 传输到远程 BBinder.onTransact(),由其完成实际操作,再将返回值写回。
  ProcessState 和 IPCThreadState 实际上是 Binder 的适配层,它们和 Linux 内核中的 Binder 驱动打交道,提供 Binder 系统实现的支持。

1.1 Binder的3层结构

在Android 系统中,Java 和C++层都定义有同样功能的供应用程序使用的 Binder 接口,它们实际上都是调用原生 Binder 库的实现在这里插入图片描述

  1. Binder 驱动部分
    驱动部分位于 Binder 结构的最底层,这部分用于实现 Binder的设备驱动,主要实现如下功能。
    • 组织Binder的服务节点
    • 调用Binder相关的处理线程
    • 完成实际的 Binder传输
  2. Binder Adapter 层
    Binder Adapter 层是对 Binder 驱动的封装,主要功能是操作 Binder 驱动。应用程序无须直接和 Binder 驱动程序关联,关联文件包括 IPCThreadState.cpp、ProcessStatecpp 和 Parcel.cpp 中的一些内容。Binder 核心库是 Binder 框架的核心实现,主要包括IBinder、Binder (服务器端)和 BpBinder (客户端)。
  3. 顶层
    顶层的 Binder 框架和具体的客户端/服务端都分别有 Java 和 C++ 两种实现方案,主要供应用程序使用,例如:摄像头和多媒体,这部分通过调用 Binder 的核心库来实现。

  在文件 frameworks/native/include/binder/IBinder.h 中定义了IBinder类,基本结构如下:

class IBinder : public virtual RefBase
{
public:
    enum {
        FIRST_CALL_TRANSACTION  = 0x00000001,
        LAST_CALL_TRANSACTION   = 0x00ffffff,

        PING_TRANSACTION        = B_PACK_CHARS('_','P','N','G'),
        DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
        SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
        INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
        SYSPROPS_TRANSACTION    = B_PACK_CHARS('_', 'S', 'P', 'R'),

        // Corresponds to TF_ONE_WAY -- an asynchronous call.
        FLAG_ONEWAY             = 0x00000001
    };

                          IBinder();

    /**
     * Check if this IBinder implements the interface named by
     * @a descriptor.  If it does, the base pointer to it is returned,
     * which you can safely static_cast<> to the concrete C++ interface.
     */
    virtual sp<IInterface>  queryLocalInterface(const String16& descriptor);

    /**
     * Return the canonical name of the interface provided by this IBinder
     * object.
     */
    virtual const String16& getInterfaceDescriptor() const = 0;

    virtual bool            isBinderAlive() const = 0;
    virtual status_t        pingBinder() = 0;
    virtual status_t        dump(int fd, const Vector<String16>& args) = 0;
    static  status_t        shellCommand(const sp<IBinder>& target, int in, int out, int err,
                                         Vector<String16>& args,
                                         const sp<IResultReceiver>& resultReceiver);

    virtual status_t        transact(   uint32_t code,
                                        const Parcel& data,
                                        Parcel* reply,
                                        uint32_t flags = 0) = 0;
......
    class DeathRecipient : public virtual RefBase
    {
    public:
        virtual void binderDied(const wp<IBinder>& who) = 0;
    };
}
......

  IBinder 可以视为一个 Binder 通信中的传输工具。其中的 transact() 函数用于通信,当调用这个函数时,表示通过通信进行远程的调用,data 和 reply 参数分别表示参数和返回值。IBinder 类中的大多数函数为纯虚函数 (virtal ······ = 0) 是没有实现的,需要继承者来实现。

  在文件 frameworks/native/include/binder/IInterface.h 中,分别定义了类 IInterface、类模板 BnInterface 和 BpInterface 。其中,类模板 BnInterface 和 BpInterface 用于实现 Service 组件和 Client 组件

class IInterface : public virtual RefBase
{
public:
            IInterface();
            static sp<IBinder>  asBinder(const IInterface*);
            static sp<IBinder>  asBinder(const sp<IInterface>&);

protected:
    virtual                     ~IInterface();
    virtual IBinder*            onAsBinder() = 0;
};

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
    virtual const String16&     getInterfaceDescriptor() const;

protected:
    virtual IBinder*            onAsBinder();
};

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);

protected:
    virtual IBinder*            onAsBinder();
};

  在使用这两个模板时,起到了双继承的作用。使用者定义一个接口 interface,然后使用 BnInterface 和 BpInterface 两个模板结合自己的接口,构建自己的 BnXXX 和 BpXXX 两个类

1.2 BBinder类

  类模板 BnInterface 继承于类 BBinder,BBinder 是服务的载体,和 binder 驱动共同工作,保证客户的请求最终是对一个 Binder 对象 (BBinder 类) 的调用。从 Binder 驱动的角度,每一个服务就是一个BBinder 类,Binder 驱动负责找出服务对应的 BBinder 类,然后把这个 BBinder 类返回给 IPCThreadState,IPCThreadState 调用 BBinder 的 transact() 。BBinder 的 transact() 又会调用 onTransact() 。BBinder::onTransact() 是虚函数,所以实际是调用 BnXXXService::onTransact(),这样就可在 BnXXXService::onTransact() 中完成具体的服务函数的调用。整个 BnXXXService 的类关系图如下所示。

Server组件的实现原理
在这里插入图片描述
Client组件的实现原理
在这里插入图片描述
由图可知,BnXXXService 包含如下两部分:

  • IXXXService:服务的主体的接口。
  • BBinder:是服务的载体,和 Binder 驱动共同工作,保证客户的请求最终是对一个 Binder 对象(BBinder类)的调用。

  每一个服务就是一个 BBinder 类,Binder 驱动负责找出服务对应的 BBinder 类,然后把这个 BBinder 类返回给 IPCThreadState,IPCThreadState 调用 BBinder 的 transact() 。BBinder 的 transact() 又会调用 onTransact() 。 BBinderon::Transact() 是虚函数,所以实际是调用 BnXXXService::onTransact() ,这样就可在 BnXXXService::onTransact() 中完成具体的服务函数的调用。代码位置:frameworks/native/include/binder/Binder.h

class BBinder : public IBinder
{
public:
                        BBinder();

    virtual const String16& getInterfaceDescriptor() const;
    virtual bool        isBinderAlive() const;
    virtual status_t    pingBinder();
    virtual status_t    dump(int fd, const Vector<String16>& args);

    virtual status_t    transact(   uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);

    virtual status_t    linkToDeath(const sp<DeathRecipient>& recipient,
                                    void* cookie = NULL,
                                    uint32_t flags = 0);

    virtual status_t    unlinkToDeath(  const wp<DeathRecipient>& recipient,
                                        void* cookie = NULL,
                                        uint32_t flags = 0,
                                        wp<DeathRecipient>* outRecipient = NULL);

    virtual void        attachObject(   const void* objectID,
                                        void* object,
                                        void* cleanupCookie,
                                        object_cleanup_func func);
    virtual void*       findObject(const void* objectID) const;
    virtual void        detachObject(const void* objectID);

    virtual BBinder*    localBinder();

protected:
    virtual             ~BBinder();

    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);

private:
                        BBinder(const BBinder& o);
            BBinder&    operator=(const BBinder& o);

    class Extras;

    std::atomic<Extras*> mExtras;
            void*       mReserved0;
};

  在类 BBinder 中当一个 Binder 代理对象通过 Binder 驱动程序向一个 Binder 本地对象发出一个进程通信请求时,Binder 驱动程序会调用该 Binder 本地对象的成员函数 transac() 来处理这个请求。函数 transact() 在文件 frameworks/native/libs/binder/Binder.cpp 中实现,具体代码如下所示。

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    data.setDataPosition(0);

    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
            err = onTransact(code, data, reply, flags);
            break;
    }

    if (reply != NULL) {
        reply->setDataPosition(0);
    }

    return err;
}

  在上述代码中,PING_TRANSACTION 请求用来检查对象是否还存在,此处只是简单地把 pingBinder 的返回值返回给调用者,将其他的请求交给 onTransact 来处理onTransact 是在 BBinder 中声明的一个 protected 类型的虚函数,此功能在其子类中实现,功能是分发和业务相关的进程间通信请求。具体实现代码如下:

status_t BBinder::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/)
{
    switch (code) {
        case INTERFACE_TRANSACTION:
            reply->writeString16(getInterfaceDescriptor());
            return NO_ERROR;

        case DUMP_TRANSACTION: {
            int fd = data.readFileDescriptor();
            int argc = data.readInt32();
            Vector<String16> args;
            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
               args.add(data.readString16());
            }
            return dump(fd, args);
        }

        case SHELL_COMMAND_TRANSACTION: {
            int in = data.readFileDescriptor();
            int out = data.readFileDescriptor();
            int err = data.readFileDescriptor();
            int argc = data.readInt32();
            Vector<String16> args;
            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
               args.add(data.readString16());
            }
            sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
                    data.readStrongBinder());

            // XXX can't add virtuals until binaries are updated.
            //return shellCommand(in, out, err, args, resultReceiver);
            if (resultReceiver != NULL) {
                resultReceiver->send(INVALID_OPERATION);
            }
        }

        case SYSPROPS_TRANSACTION: {
            report_sysprop_change();
            return NO_ERROR;
        }

        default:
            return UNKNOWN_TRANSACTION;
    }
}

1.3 类BpRefBase

  类模板 BpInterface 继承于类 BpRefBase,起到代理作用。BpReBase 以上是业务逻辑(要实现什么功能),BpRefBase 以下是数据传输(通过 binder 如何将功能实现)。在文件 frameworks/native/include/binder/Binder.h 中,定义类BpRefBase 的代码。

class BpRefBase : public virtual RefBase
{
protected:
                            BpRefBase(const sp<IBinder>& o);
    virtual                 ~BpRefBase();
    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
    
	//BpRefBase 类当中的 remote() 函数指向了一个IBinder。BpRefBase给代理端使用,代理端获得IBinder之后使用它来进行传输。
    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }

private:
                            BpRefBase(const BpRefBase& o);
    BpRefBase&              operator=(const BpRefBase& o);

    IBinder* const          mRemote;
    RefBase::weakref_type*  mRefs;
    std::atomic<int32_t>    mState;
};

  类 BpRefBase 中的成员函数 transact() 用于向运行在 Server 进程中的 Service 组件发送进程之间的通信请求,这是通过 Binder 驱动程序间接实现的。函数 transact() 的具体实现代码如下。

/*
	code:  表示请求的ID号
	data:  表示请求的参数
	reply: 表示返回的结果
	flags: 是一些额外的标识,例如FLAG_ONEWAY,通常为 0
*/
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

1.4 类IPCThreadState

  前面介绍的类 BBinder 和类 BpRefBase 都是通过类 IPCThreadState 和 Binder 的驱动程序交互实现的。类 IPCThreadState 在文件 frameworks/native/include/binder/IPCThreadState.h 中实现,具体实现代码如下。

namespace android {

class IPCThreadState
{
public:
    static  IPCThreadState*     self();
    static  IPCThreadState*     selfOrNull();  // self(), but won't instantiate

            sp<ProcessState>    process();

            status_t            clearLastError();

            pid_t               getCallingPid() const;
            uid_t               getCallingUid() const;

            void                setStrictModePolicy(int32_t policy);
            int32_t             getStrictModePolicy() const;

            void                setLastTransactionBinderFlags(int32_t flags);
            int32_t             getLastTransactionBinderFlags() const;

            int64_t             clearCallingIdentity();
            void                restoreCallingIdentity(int64_t token);

            int                 setupPolling(int* fd);
            status_t            handlePolledCommands();
            void                flushCommands();

            void                joinThreadPool(bool isMain = true);

            // Stop the local process.
            void                stopProcess(bool immediate = true);

            status_t            transact(int32_t handle,
                                         uint32_t code, const Parcel& data,
                                         Parcel* reply, uint32_t flags);

            void                incStrongHandle(int32_t handle);
            void                decStrongHandle(int32_t handle);
            void                incWeakHandle(int32_t handle);
            void                decWeakHandle(int32_t handle);
            status_t            attemptIncStrongHandle(int32_t handle);
    static  void                expungeHandle(int32_t handle, IBinder* binder);
            status_t            requestDeathNotification(   int32_t handle,
                                                            BpBinder* proxy);
            status_t            clearDeathNotification( int32_t handle,
                                                        BpBinder* proxy);

    static  void                shutdown();

    // Call this to disable switching threads to background scheduling when
    // receiving incoming IPC calls.  This is specifically here for the
    // Android system process, since it expects to have background apps calling
    // in to it but doesn't want to acquire locks in its services while in
    // the background.
    static  void                disableBackgroundScheduling(bool disable);

            // Call blocks until the number of executing binder threads is less than
            // the maximum number of binder threads threads allowed for this process.
            void                blockUntilThreadAvailable();

private:
                                IPCThreadState();
                                ~IPCThreadState();

            status_t            sendReply(const Parcel& reply, uint32_t flags);
            status_t            waitForResponse(Parcel *reply,
                                                status_t *acquireResult=NULL);
            status_t            talkWithDriver(bool doReceive=true);
            status_t            writeTransactionData(int32_t cmd,
                                                     uint32_t binderFlags,
                                                     int32_t handle,
                                                     uint32_t code,
                                                     const Parcel& data,
                                                     status_t* statusBuffer);
            status_t            getAndExecuteCommand();
            status_t            executeCommand(int32_t command);
            void                processPendingDerefs();

            void                clearCaller();

    static  void                threadDestructor(void *st);
    static  void                freeBuffer(Parcel* parcel,
                                           const uint8_t* data, size_t dataSize,
                                           const binder_size_t* objects, size_t objectsSize,
                                           void* cookie);

    const   sp<ProcessState>    mProcess;
            Vector<BBinder*>    mPendingStrongDerefs;
            Vector<RefBase::weakref_type*> mPendingWeakDerefs;

            Parcel              mIn;
            Parcel              mOut;
            status_t            mLastError;
            pid_t               mCallingPid;
            uid_t               mCallingUid;
            int32_t             mStrictModePolicy;
            int32_t             mLastTransactionBinderFlags;
};

  在类 IPCThreadState 中,成员函数用于实现数据处理。在 transact 请求中将请求的数据经过 Binder 设备发送给 Service,Service 处理完请求后,又将结果原路返回给客户端。函数 transact() 在文件 frameworks/native/libs/binder/IPCThreadState.cpp 中定义,具体实现代码如下。

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;

    IF_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }

    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif

        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

1.5 IPermissionController

1.6 IServiceManager

1.7 Binder库中的其它部分

3 初始化 Java 层 Binder 框架

  虽然 Java 层 Binder 系统是 Native 层 Binder 系统的一个镜像,但这个镜像终归还需借助 Native 层Binder 系统来开展工作,即它和 Native 层 Binder 有着千丝万缕的关系,故一定要在 Java 层 Binder 正式工作之前建立这种关系。本节讲解Java 层 Binder 框架的初始化过程。

3.1 搭建交互关系

  在Android系统中,函数 register_android_os_Binder() (专门负责搭建 Java Binder 和 Native Binder 的交互关系)。此函数在文件 frameworks/base/core/jni/android_util_Binder.cpp 中实现。

int register_android_os_Binder(JNIEnv* env)
{
	//初始化Java Binder类和Native层的关系
    if (int_register_android_os_Binder(env) < 0)
        return -1;
    //初始化Java BinderInternel类和Native层的关系
    if (int_register_android_os_BinderInternal(env) < 0)
        return -1;
    //初始化Java BinderProxy类和Native层的关系
    if (int_register_android_os_BinderProxy(env) < 0)
        return -1;

    jclass clazz = FindClassOrDie(env, "android/util/Log");
    gLogOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gLogOffsets.mLogE = GetStaticMethodIDOrDie(env, clazz, "e",
            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I");

    clazz = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
    gParcelFileDescriptorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gParcelFileDescriptorOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>",
                                                                 "(Ljava/io/FileDescriptor;)V");

    clazz = FindClassOrDie(env, "android/os/StrictMode");
    gStrictModeCallbackOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gStrictModeCallbackOffsets.mCallback = GetStaticMethodIDOrDie(env, clazz,
            "onBinderStrictModePolicyChange", "(I)V");

    return 0;
}

  根据上面的代码可知,函数 register_android_os_Binder() 完成了 Java 层 Binder 架构中最重要的3个类的初始化工作。下面将详细分析 Binder 类的初始化进程。

3.2 实现 Binder 类的初始化

  函数 int_register_android_os_Binder() 实现了 Binder 类的初始化工作,此函数在文件 android_util_Binder.cpp 中实现,具体实现代码如下所示。

static int int_register_android_os_Binder(JNIEnv* env)
{
	//kBinderPathName为 Java 层中 Binder 类的全路径名,android/os/Binder
    jclass clazz = FindClassOrDie(env, kBinderPathName);

	/*
		gBinderOffsets 是一个静态类对象,专门保存Binder类的一些在JNI 层中使用的信息,
		如:成员函数execTransact()的 methodID,Binder 类中成员mObject的fieldID
	*/
    gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
    gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
	//注册Binder类中Native函数的实现
    return RegisterMethodsOrDie(
        env, kBinderPathName,
        gBinderMethods, NELEM(gBinderMethods));
}

  从上面的代码可知,gBinderOffsets 对象保存了和 Binder 类相关的某些在JNI层中使用的信息。下一个初始化的类是 BinderInternal,其代码位于 int_register_android_os_BinderInternal() 函数中。此函数在文件 android_util_Binder.cpp 中实现,具体实现代码如下。

static int int_register_android_os_BinderInternal(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, kBinderInternalPathName);

    gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");

    return RegisterMethodsOrDie(
        env, kBinderInternalPathName,
        gBinderInternalMethods, NELEM(gBinderInternalMethods));
}

  由此可见,int_register_android_os_BinderInternal() 的功能和 int_register_android_os_Binder() 的功能类似,主要包括以下两个方面:

  • 获取一些有用的 methodID 和 fieldID。这表明JNI层一定会向上调用 Java 层的函数。
  • 注册相关类中native函数的实现。

3.3 实现 BinderProxy 类的初始化

  函数 int_register_android_os_BinderProxy() 完成了 BinderProxy 类的初始化工作,此函数在文件android_util_Binder.cpp 中实现,具体实现代码如下。

static int int_register_android_os_BinderProxy(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, "java/lang/Error");
    //gErrorOffsets用来和Error类交互
    gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);

    clazz = FindClassOrDie(env, kBinderProxyPathName);
    //gBinderProxyOffsets用来和BinderProxy打交道
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
            "(Landroid/os/IBinder$DeathRecipient;)V");
	//获取BinderProxy的一些信息
    gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
    gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",
                                                "Ljava/lang/ref/WeakReference;");
    gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");

	clazz = FindClassOrDie(env, "java/lang/Class");
    //gClassOffsets用来和Class类交互
    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
    
	// 注册BinderProxy native 函数的实现
    return RegisterMethodsOrDie(
        env, kBinderProxyPathName,
        gBinderProxyMethods, NELEM(gBinderProxyMethods));
}

  根据上面的代码可知,int_register_android_os_BinderProxy() 函数除了初始化 BinderProxy 类外,还获取了 Error 类的一些信息。到此为止,Java Binder 几个重要成员的初始化已完成,同时在代码中定义了几个全局静态对象,分别是gBinderOffsets、gBinderInternalOffsets 和 gBinderProxyOffsets。

4 实体对象 binder_node

  在Android 系统中,bindcr node 用来描述一个 Binder 实体对象。每个Service 组件在 Binder 动程序中都对应有一个 Binder 实体对象,用来描述它在内核中的状态。Android 系统的 Binder 通信框架如图 5-2 所示。
在这里插入图片描述

4.1 定义实体对象

Binder 实体对象binder_node的定义在内核代码 binder.c 中。

struct binder_node {
    int debug_id;							//调试id
    struct binder_work work;				//描述一个待处理的工作项
    union {
        struct rb_node rb_node;				//挂载到宿主进程binder_proc的成员变量nodes红黑树的节点
        struct hlist_node dead_node;		//当宿主进程死亡,该Binder实体对象将挂载到全局binder_dead_nodes 链表中
    };
    struct binder_proc *proc;				//指向该Binder线程的宿主进程
    struct hlist_head refs;					//保存所有引用该Binder实体对象的Binder引用对象
    int internal_strong_refs;				//Binder实体对象的强引用计数
    int local_weak_refs;					//Binder实体对象的弱引用计数
    int local_strong_refs;
    binder_uintptr_t ptr;
    binder_uintptr_t cookie;
    unsigned has_strong_ref:1;
    unsigned pending_strong_ref:1;
    unsigned has_weak_ref:1;
    unsigned pending_weak_ref:1;
    unsigned has_async_transaction:1;		//标示该Binder实体对象是否正在处理一个异步事务
    unsigned accept_fds:1;					//设置该Binder实体对象是否可以接收包含有文件描述符的IPC数据
    unsigned min_priority:8;				//Binder实体对象要求处理线程应具备的最小线程优先级
    struct list_head async_todo;			//异步事务队列
};

  通过上述代码可知,在Binder 驱动中,用户空间中的每一个 Binder 本地对象都对应有一个 Binder 实体对象。各个成员的具体说明如下所示:

  • proc : 指向 Binder 实体对象的宿主进程,宿主进程使用红黑树来维护其内部的所有 Binder 实体对象。
  • rbnode : 用来挂载到宿主进程 proc 的 Binder 实体对象红黑树中的节点。
  • dead_node : 如果该 Binder 实体对象的宿主进程已经死亡,该 Binder 实体就通过成员变量 dead_node 保存到全局链表 binder_dead_nodes。
  • refs : 一个Binder 实体对象可以被多个 client 引用,成员变量 refs 用来保存所有引用该 Binder
    实体的 Binder 引用对象。
  • internal_strong_refs 和 local_strong_refs : 都是用来描述 Binder 实体对象的强引用计数。
  • local_weak_refs : 用来描述 Binder 实体对象的弱引用计数。
  • ptr 和 cookie : 分别指向用户空间地址,cookie 指向 BBinder 的地址,ptr 指向 BBinder 对象的引用计数地址。
  • has_async_transaction : 描述一个 Binder 实体对象是否正在处理一个异步事务,当 Binder 驱动指定某个线程来处理某一事务时,首先将该事务保存到指定线程的 todo 队列中,表示要由该线程来处理该事务。如果是异步事务,Binder 驱动程序就会将它保存在目标 Binder 实体对象的一个异步事务队列 async_todo 中。
  • min_priority : 表示一个 Binder 实体对象在处理来自 client 进程请求时,要求处理线程的最小线程优先级。

4.2 增加引用计数

  在 Binder 驱动程序中,使用函数 binder_inc_node() 来增加一个 Binder 实体对象的引用计数。函数binder_inc_node() 在文件内核代码 binder.c 中定义,具体实现代码如下。

/**
	各个参数的具体说明如下所示:
	node : 表示要增加引用计数的 Binder 实体对象。
	strong : 表示要增加强引用计数还是要增加弱引用计数。
	internal : 用于区分增加的是内部引用计数还是外部引用计数。
	target_list : 用于指向一个目标进程或目标线程的 todo 队列,当不是null值时表示增加了Binder实体对象的引用计数后,需要对应增加它所引用的Binder本地对象的引用计数。
*/
static int binder_inc_node(struct binder_node *node, int strong, int internal,
               struct list_head *target_list)
{
    if (strong) {
        if (internal) {
            if (target_list == NULL &&
                node->internal_strong_refs == 0 &&
                !(node == binder_context_mgr_node &&
                node->has_strong_ref)) {
                pr_err("invalid inc strong node for %d\n",
                    node->debug_id);
                return -EINVAL;
            }
            node->internal_strong_refs++;
        } else
            node->local_strong_refs++;
        if (!node->has_strong_ref && target_list) {
            list_del_init(&node->work.entry);
            list_add_tail(&node->work.entry, target_list);
        }
    } else {
        if (!internal)
            node->local_weak_refs++;
        if (!node->has_weak_ref && list_empty(&node->work.entry)) {
            if (target_list == NULL) {
                pr_err("invalid inc weak node for %d\n",
                    node->debug_id);
                return -EINVAL;
            }
            list_add_tail(&node->work.entry, target_list);
        }
    }
    return 0;
}

4.3 减少引用计数

  与函数 binder_inc_node() 相反,在 Binder 驱动程序中,使用函数 binder_dec_node() 来减少一个 Binder 实体对象的引用计数。函数 binder_dec_node() 会减少 internal_strong_refs、local_strong_refs 或 local_weak_refs 的使用计数并删除节点的 work.entry 链表。

static int binder_dec_node(struct binder_node *node, int strong, int internal)
{
    if (strong) {
        if (internal)
            node->internal_strong_refs--;
        else
            node->local_strong_refs--;
        if (node->local_strong_refs || node->internal_strong_refs)
            return 0;
    } else {
        if (!internal)
            node->local_weak_refs--;
        if (node->local_weak_refs || !hlist_empty(&node->refs))
            return 0;
    }
    if (node->proc && (node->has_strong_ref || node->has_weak_ref)) {
        if (list_empty(&node->work.entry)) {
            list_add_tail(&node->work.entry, &node->proc->todo);
            wake_up_interruptible(&node->proc->wait);
        }
    } else {
        if (hlist_empty(&node->refs) && !node->local_strong_refs &&
            !node->local_weak_refs) {
            list_del_init(&node->work.entry);
            if (node->proc) {
                rb_erase(&node->rb_node, &node->proc->nodes);
                binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                         "refless node %d deleted\n",
                         node->debug_id);
            } else {
                hlist_del(&node->dead_node);
                binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                         "dead node %d deleted\n",
                         node->debug_id);
            }
            kfree(node);
            binder_stats_deleted(BINDER_STAT_NODE);
        }
    }

    return 0;
}

5 本地对象BBinder

  因为 Binder 的功能就是在本地执行其他进程的功能,所以对于 Binder 机制来说,不但是 Android 系统中的一个完美的IPC机制,其实也是 Android 的一种远程过程调用(RPC)机制。当进程通过 Binder 获取将要调用的进程服务时,不但可以是一个本地对象,也可以是一个远程服务的引用。也就是说,Binder 不但可以与本地进程通信,还可以与远程进程通信。此处的本地进程就是本节所讲解的本地对象,而远程进程就是远程服务的一个引用。

5.1 引用了运行的本地对象

  在Android 系统中,Binder 驱动程序通过函数 binder_thread_read() 引用了运行在 Server 进程中的Binder 本地对象,此函数在内核代码 binder.c 中定义。当 service_manager 运行时此函数会一直等待,直到有请求到达为止。

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	int ret = 0;
	int wait_for_proc_work;

	if (*consumed == 0) {
		if (put_user_preempt_disabled(BR_NOOP, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
	}

retry:
	wait_for_proc_work = thread->transaction_stack == NULL &&
				list_empty(&thread->todo);

	if (thread->return_error != BR_OK && ptr < end) {
		if (thread->return_error2 != BR_OK) {
			if (put_user_preempt_disabled(thread->return_error2, (uint32_t __user *)ptr))
				return -EFAULT;
			ptr += sizeof(uint32_t);
			binder_stat_br(proc, thread, thread->return_error2);
			if (ptr == end)
				goto done;
			thread->return_error2 = BR_OK;
		}
		if (put_user_preempt_disabled(thread->return_error, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		binder_stat_br(proc, thread, thread->return_error);
		thread->return_error = BR_OK;
		goto done;
	}


	thread->looper |= BINDER_LOOPER_STATE_WAITING;
	if (wait_for_proc_work)
		proc->ready_threads++;

	binder_unlock(__func__);

	trace_binder_wait_for_work(wait_for_proc_work,
				   !!thread->transaction_stack,
				   !list_empty(&thread->todo));
	if (wait_for_proc_work) {
		if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
					BINDER_LOOPER_STATE_ENTERED))) {
			binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n",
				proc->pid, thread->pid, thread->looper);
			wait_event_interruptible(binder_user_error_wait,
						 binder_stop_on_user_error < 2);
		}
		binder_set_nice(proc->default_priority);
		if (non_block) {
			if (!binder_has_proc_work(proc, thread))
				ret = -EAGAIN;
		} else
			ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
	} else {
		if (non_block) {
			if (!binder_has_thread_work(thread))
				ret = -EAGAIN;
		} else
			ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
	}

	binder_lock(__func__);

	if (wait_for_proc_work)
		proc->ready_threads--;
	thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

	if (ret)
		return ret;

	while (1) {
		uint32_t cmd;
		//将用户传进来的transact 参数复制到本地变量 struct binder_transaction_data tr 中
		struct binder_transaction_data tr;
		struct binder_work *w;
		struct binder_transaction *t = NULL;
		//由于thread->todo非空,执行下列语句
		if (!list_empty(&thread->todo))
			w = list_first_entry(&thread->todo, struct binder_work, entry);
		else if (!list_empty(&proc->todo) && wait_for_proc_work)
			//ServiceManager 被唤醒之后,就进入while 循环开始处理事务了
			//此处 wait_for_proc_work 等于1,并且 proc-todo 不为空,所以从 proc-todo 列表中得到第一个工作项
			w = list_first_entry(&proc->todo, struct binder_work, entry);
		else {
			if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
				goto retry;
			break;
		}

		if (end - ptr < sizeof(tr) + 4)
			break;

		switch (w->type) {
		//函数调用binder_transaction进一步处理
		case BINDER_WORK_TRANSACTION: {
			//因为这个工作项的类型为BINDER_WORK_TRANSACTION,所以通过下面语句得到事务项
			t = container_of(w, struct binder_transaction, work);
		} break;
		//因为w->type 为BINDER_WORK_TRANSACTION_COMPLETE
		//这是在上面的 binder_transaction()函数中设置的,于是执行下面的代码
		case BINDER_WORK_TRANSACTION_COMPLETE: {
			cmd = BR_TRANSACTION_COMPLETE;
			if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
				return -EFAULT;
			ptr += sizeof(uint32_t);

			binder_stat_br(proc, thread, cmd);
			binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
				     "%d:%d BR_TRANSACTION_COMPLETE\n",
				     proc->pid, thread->pid);

			//将w从thread->todo删除
			list_del(&w->entry);
			kfree(w);
			binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
		} break;
		case BINDER_WORK_NODE: {
			struct binder_node *node = container_of(w, struct binder_node, work);
			uint32_t cmd = BR_NOOP;
			const char *cmd_name;
			int strong = node->internal_strong_refs || node->local_strong_refs;
			int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;
			if (weak && !node->has_weak_ref) {
				cmd = BR_INCREFS;
				cmd_name = "BR_INCREFS";
				node->has_weak_ref = 1;
				node->pending_weak_ref = 1;
				node->local_weak_refs++;
			} else if (strong && !node->has_strong_ref) {
				cmd = BR_ACQUIRE;
				cmd_name = "BR_ACQUIRE";
				node->has_strong_ref = 1;
				node->pending_strong_ref = 1;
				node->local_strong_refs++;
			} else if (!strong && node->has_strong_ref) {
				cmd = BR_RELEASE;
				cmd_name = "BR_RELEASE";
				node->has_strong_ref = 0;
			} else if (!weak && node->has_weak_ref) {
				cmd = BR_DECREFS;
				cmd_name = "BR_DECREFS";
				node->has_weak_ref = 0;
			}
			if (cmd != BR_NOOP) {
				if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
					return -EFAULT;
				ptr += sizeof(uint32_t);
				if (put_user_preempt_disabled(node->ptr,
					     (binder_uintptr_t __user *)ptr))
					return -EFAULT;
				ptr += sizeof(binder_uintptr_t);
				if (put_user_preempt_disabled(node->cookie,
					     (binder_uintptr_t __user *)ptr))
					return -EFAULT;
				ptr += sizeof(binder_uintptr_t);

				binder_stat_br(proc, thread, cmd);
				binder_debug(BINDER_DEBUG_USER_REFS,
					     "%d:%d %s %d u%016llx c%016llx\n",
					     proc->pid, thread->pid, cmd_name,
					     node->debug_id,
					     (u64)node->ptr, (u64)node->cookie);
			} else {
				list_del_init(&w->entry);
				if (!weak && !strong) {
					binder_debug(BINDER_DEBUG_INTERNAL_REFS,
						     "%d:%d node %d u%016llx c%016llx deleted\n",
						     proc->pid, thread->pid, node->debug_id,
						     (u64)node->ptr, (u64)node->cookie);
					rb_erase(&node->rb_node, &proc->nodes);
					kfree(node);
					binder_stats_deleted(BINDER_STAT_NODE);
				} else {
					binder_debug(BINDER_DEBUG_INTERNAL_REFS,
						     "%d:%d node %d u%016llx c%016llx state unchanged\n",
						     proc->pid, thread->pid, node->debug_id,
						     (u64)node->ptr, (u64)node->cookie);
				}
			}
		} break;
		case BINDER_WORK_DEAD_BINDER:
		case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
		case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
			struct binder_ref_death *death;
			uint32_t cmd;

			death = container_of(w, struct binder_ref_death, work);
			if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
				cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
			else
				cmd = BR_DEAD_BINDER;
			if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
				return -EFAULT;
			ptr += sizeof(uint32_t);
			if (put_user_preempt_disabled(death->cookie,
				     (binder_uintptr_t __user *)ptr))
				return -EFAULT;
			ptr += sizeof(binder_uintptr_t);
			binder_stat_br(proc, thread, cmd);
			binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
				     "%d:%d %s %016llx\n",
				      proc->pid, thread->pid,
				      cmd == BR_DEAD_BINDER ?
				      "BR_DEAD_BINDER" :
				      "BR_CLEAR_DEATH_NOTIFICATION_DONE",
				      (u64)death->cookie);

			if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
				list_del(&w->entry);
				kfree(death);
				binder_stats_deleted(BINDER_STAT_DEATH);
			} else
				list_move(&w->entry, &proc->delivered_death);
			if (cmd == BR_DEAD_BINDER)
				goto done; /* DEAD_BINDER notifications can cause transactions */
		} break;
		}

		if (!t)
			continue;

		BUG_ON(t->buffer == NULL);
		//把事务项t中的数据复制到本地局部变量struct binder_transaction_data tr 中
		if (t->buffer->target_node) {
			struct binder_node *target_node = t->buffer->target_node;
			tr.target.ptr = target_node->ptr;
			tr.cookie =  target_node->cookie;
			t->saved_priority = task_nice(current);
			if (t->priority < target_node->min_priority &&
			    !(t->flags & TF_ONE_WAY))
				binder_set_nice(t->priority);
			else if (!(t->flags & TF_ONE_WAY) ||
				 t->saved_priority > target_node->min_priority)
				binder_set_nice(target_node->min_priority);
			cmd = BR_TRANSACTION;
		} else {
			tr.target.ptr = 0;
			tr.cookie = 0;
			cmd = BR_REPLY;
		}
		tr.code = t->code;
		tr.flags = t->flags;
		tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);

		if (t->from) {
			struct task_struct *sender = t->from->proc->tsk;
			tr.sender_pid = task_tgid_nr_ns(sender,
							task_active_pid_ns(current));
		} else {
			tr.sender_pid = 0;
		}

		tr.data_size = t->buffer->data_size;
		tr.offsets_size = t->buffer->offsets_size;
		/*
			t->buffer->data 所指向的地址是内核空间的,如果要把数据返回给 Service Manager 进程的用户空间
			而Service Manager 进程的用户空间是不能访问内核空间的数据的,所以需要进一步处理
			在具体处理时,Binder 机制使用类似浅拷贝的方法,通过在用户空间分配一个虚拟地址
			然后让这个用户空间虚拟地址与 t->buffer->data 这个内核空间虚拟地址指向同一个物理地址
			在此只需将t->buffer->data 加上一个偏移值 proc->user_buffer_offset
			就可以得到t->buffer->data 对应的用户空间虚拟地址了
			在调整了tr.data.ptr.buffer 值后,需要一起调整tr.data.ptr.offsets 的值
		*/
		tr.data.ptr.buffer = (binder_uintptr_t)(
					(uintptr_t)t->buffer->data +
					proc->user_buffer_offset);
		tr.data.ptr.offsets = tr.data.ptr.buffer +
					ALIGN(t->buffer->data_size,
					    sizeof(void *));
		
		//把tr的内容复制到用户传进来的缓冲区,指针ptr 指向这个用户缓冲区的地址
		if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
			return -EFAULT;
		ptr += sizeof(tr);
		
		//上述代码只是对 tr.data.ptr.bufferr 和 tr.data.ptr.ffsets 的内容进行了浅拷贝工作
		trace_binder_transaction_received(t);
		binder_stat_br(proc, thread, cmd);
		binder_debug(BINDER_DEBUG_TRANSACTION,
			     "%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
			     proc->pid, thread->pid,
			     (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
			     "BR_REPLY",
			     t->debug_id, t->from ? t->from->proc->pid : 0,
			     t->from ? t->from->pid : 0, cmd,
			     t->buffer->data_size, t->buffer->offsets_size,
			     (u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
			     
		//从todo列表中删除,因为已经处理了这个事务
		list_del(&t->work.entry);
		t->buffer->allow_user_free = 1;
		//如果cmd== BR_TRANSACTION && !(t->fags &TF_ONE_WAY)为true
		//说明虽然在驱动程序中已经处理完了这个事务,但是仍然要在Service Manager 完成之后等待回复确认
		if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
			//把当前事务t放在thread->transaction_stack 队列的头部
			t->to_parent = thread->transaction_stack;
			t->to_thread = thread;
			thread->transaction_stack = t;
		} else {	//如果为false 则不需要等待回复了,而是直接删除事务t
			t->buffer->transaction = NULL;
			kfree(t);
			binder_stats_deleted(BINDER_STAT_TRANSACTION);
		}
		break;
	}

done:

	*consumed = ptr - buffer;
	if (proc->requested_threads + proc->ready_threads == 0 &&
	    proc->requested_threads_started < proc->max_threads &&
	    (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
	     BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
	     /*spawn a new thread if we leave this out */) {
		proc->requested_threads++;
		binder_debug(BINDER_DEBUG_THREADS,
			     "%d:%d BR_SPAWN_LOOPER\n",
			     proc->pid, thread->pid);
		if (put_user_preempt_disabled(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
			return -EFAULT;
		binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
	}
	return 0;
}

由此可见,Binder 驱动程序是通过如下 4 个协议来引用运行在 Server 进程中的 Binder 本地对象的:

  • BR_INCREFS
  • BR_ACQUIRE
  • BR_DECREFS
  • BR_RELEASE

5.2 处理接口协议

  在文件 frameworks/native/libs/binder/IPCThreadState.cpp 中,通过使用类成员函数executeCommand() 来处理上节中的4个接口协议。

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
    
    switch ((uint32_t)cmd) {
    case BR_ERROR:
        result = mIn.readInt32();
        break;
        
    case BR_OK:
        break;
        
    case BR_ACQUIRE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        ALOG_ASSERT(refs->refBase() == obj,
                   "BR_ACQUIRE: object %p does not match cookie %p (expected %p)",
                   refs, obj, refs->refBase());
        obj->incStrong(mProcess.get());
        IF_LOG_REMOTEREFS() {
            LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj);
            obj->printRefs();
        }
        mOut.writeInt32(BC_ACQUIRE_DONE);
        mOut.writePointer((uintptr_t)refs);
        mOut.writePointer((uintptr_t)obj);
        break;
        
    case BR_RELEASE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        ALOG_ASSERT(refs->refBase() == obj,
                   "BR_RELEASE: object %p does not match cookie %p (expected %p)",
                   refs, obj, refs->refBase());
        IF_LOG_REMOTEREFS() {
            LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj);
            obj->printRefs();
        }
        mPendingStrongDerefs.push(obj);
        break;
        
    case BR_INCREFS:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        refs->incWeak(mProcess.get());
        mOut.writeInt32(BC_INCREFS_DONE);
        mOut.writePointer((uintptr_t)refs);
        mOut.writePointer((uintptr_t)obj);
        break;
        
    case BR_DECREFS:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        // NOTE: This assertion is not valid, because the object may no
        // longer exist (thus the (BBinder*)cast above resulting in a different
        // memory address).
        //ALOG_ASSERT(refs->refBase() == obj,
        //           "BR_DECREFS: object %p does not match cookie %p (expected %p)",
        //           refs, obj, refs->refBase());
        mPendingWeakDerefs.push(refs);
        break;
        
    case BR_ATTEMPT_ACQUIRE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
         
        {
            const bool success = refs->attemptIncStrong(mProcess.get());
            ALOG_ASSERT(success && refs->refBase() == obj,
                       "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)",
                       refs, obj, refs->refBase());
            
            mOut.writeInt32(BC_ACQUIRE_RESULT);
            mOut.writeInt32((int32_t)success);
        }
        break;
    
    case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            ALOG_ASSERT(result == NO_ERROR,
                "Not enough command data for brTRANSACTION");
            if (result != NO_ERROR) break;
            
            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
            
            const pid_t origPid = mCallingPid;
            const uid_t origUid = mCallingUid;
            const int32_t origStrictModePolicy = mStrictModePolicy;
            const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;

            mCallingPid = tr.sender_pid;
            mCallingUid = tr.sender_euid;
            mLastTransactionBinderFlags = tr.flags;

            int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
            if (gDisableBackgroundScheduling) {
                if (curPrio > ANDROID_PRIORITY_NORMAL) {
                    // We have inherited a reduced priority from the caller, but do not
                    // want to run in that state in this process.  The driver set our
                    // priority already (though not our scheduling class), so bounce
                    // it back to the default before invoking the transaction.
                    setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
                }
            } else {
                if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
                    // We want to use the inherited priority from the caller.
                    // Ensure this thread is in the background scheduling class,
                    // since the driver won't modify scheduling classes for us.
                    // The scheduling group is reset to default by the caller
                    // once this method returns after the transaction is complete.
                    set_sched_policy(mMyThreadId, SP_BACKGROUND);
                }
            }

            //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);

            Parcel reply;
            status_t error;
            IF_LOG_TRANSACTIONS() {
                TextOutput::Bundle _b(alog);
                alog << "BR_TRANSACTION thr " << (void*)pthread_self()
                    << " / obj " << tr.target.ptr << " / code "
                    << TypeCode(tr.code) << ": " << indent << buffer
                    << dedent << endl
                    << "Data addr = "
                    << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
                    << ", offsets addr="
                    << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
            }
            if (tr.target.ptr) {
                // We only have a weak reference on the target object, so we must first try to
                // safely acquire a strong reference before doing anything else with it.
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                } else {
                    error = UNKNOWN_TRANSACTION;
                }

            } else {
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }

            //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
            //     mCallingPid, origPid, origUid);
            
            if ((tr.flags & TF_ONE_WAY) == 0) {
                LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                if (error < NO_ERROR) reply.setError(error);
                sendReply(reply, 0);
            } else {
                LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
            }
            
            mCallingPid = origPid;
            mCallingUid = origUid;
            mStrictModePolicy = origStrictModePolicy;
            mLastTransactionBinderFlags = origTransactionBinderFlags;

            IF_LOG_TRANSACTIONS() {
                TextOutput::Bundle _b(alog);
                alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
                    << tr.target.ptr << ": " << indent << reply << dedent << endl;
            }
            
        }
        break;
    
    case BR_DEAD_BINDER:
        {
            BpBinder *proxy = (BpBinder*)mIn.readPointer();
            proxy->sendObituary();
            mOut.writeInt32(BC_DEAD_BINDER_DONE);
            mOut.writePointer((uintptr_t)proxy);
        } break;
        
    case BR_CLEAR_DEATH_NOTIFICATION_DONE:
        {
            BpBinder *proxy = (BpBinder*)mIn.readPointer();
            proxy->getWeakRefs()->decWeak(proxy);
        } break;
        
    case BR_FINISHED:
        result = TIMED_OUT;
        break;
        
    case BR_NOOP:
        break;
        
    case BR_SPAWN_LOOPER:
        mProcess->spawnPooledThread(false);
        break;
        
    default:
        printf("*** BAD COMMAND %d received from Binder driver\n", cmd);
        result = UNKNOWN_ERROR;
        break;
    }

    if (result != NO_ERROR) {
        mLastError = result;
    }
    
    return result;
}

  通过上述代码可知,在函数 executeCommand() 中会调用 BBinder::transact() 来处理 Client 端的请求。当需要多个线程提供服务时,驱动会请求创建新线程。具体创建某线程的过程,可以通过上述函数处理 BR_SPAWN_LOOPER 协议的过程获得。

6 引用对象 binder_ref

struct binder_ref {
	/* Lookups needed: */
	/*   node + proc => ref (transaction) */
	/*   desc + proc => ref (transaction, inc/dec ref) */
	/*   node => refs + procs (proc exit) */
	int debug_id;						//调试id
	struct rb_node rb_node_desc;		//挂载到宿主对象binder_proc的红黑树refs_by_desc中的节点
	struct rb_node rb_node_node;		//挂载到宿主对象binder_proc的红黑树refs_by_node中的节点
	struct hlist_node node_entry;		//挂载到Binder实体对象的refs链表中的节点
	struct binder_proc *proc;			//Binder引用对象的宿主进程binder_proc
	struct binder_node *node;			//Binder引用对象所引用的Binder实体对象
	uint32_t desc;						//Binder引用对象的句柄值
	int strong;							//强引用计数
	int weak;							//弱引用计数
	struct binder_ref_death *death;		//注册死亡接收通知
};

  在 Binder 机制中,binder_ref 用来描述一个 Binder 引用对象,每一个 client 在 Binder 驱动中都有个 binder 引用对象。各个成员变量的具体说明如下所示。

  • 成员变量 node : 保存了该 Binder 引用对象所引用的 Binder 实体对象,Binder 实体对象使用链表保存了所有引用该实体对象的 Binder 引用对象。
  • node_entry : 是该 Binder 引用对象所引用的实体对象的成员变量 refs 链表中的节点。
  • desc : 是一个句柄值,用来描述一个 Binder 引用对象。
  • node : 当 Client 进程通过句柄值来访问某个 Service 时,Binder 驱动程序可以通过该句柄值找到对应的 Binder引用对象,然后根据该 Binder 引用对象的成员变量 node 找到对应的 Binder 实体对象,最后通过该 Binder 实体对象找到要访问的 Service。
  • proc : 执行该 Binder 引用对象的宿主进程。
  • rb_node_desc 和 rb_node_node : 是 binder_proc 中红黑树 refs_by_desc 和 refs_by_node 的节点。

Binder 驱动程序存在如下4个重要的协议,用于增加和减少 Binder 引用对象的强引用计数和弱引用计数:

  • BR_INCREFS
  • BR_ACQUIRE
  • BR_DECREFS
  • BR_RELEASE

  上述计数处理功能通过函数 binder_thread_write() 实现,此函数在内核驱动 binder.c 中定义。协议BR_INCREFS 和 BR_ACOUIRE 用于增加一个 Binder 引用对象的强引用计数和弱引用计数,此功能是通过调用函数 binder_inc_ref() 实现的,具体代码如下所示。

static int binder_inc_ref(struct binder_ref *ref, int strong,
			  struct list_head *target_list)
{
	int ret;
	if (strong) {
		if (ref->strong == 0) {
			ret = binder_inc_node(ref->node, 1, 1, target_list);
			if (ret)
				return ret;
		}
		ref->strong++;
	} else {
		if (ref->weak == 0) {
			ret = binder_inc_node(ref->node, 0, 1, target_list);
			if (ret)
				return ret;
		}
		ref->weak++;
	}
	return 0;
}

  而协议 BR_RELEASE 和 BR_DECREFS 用于减少一个 Binder 引用对象的强引用计数和弱引用计数,此功能是通过调用函数 binder_dec_ref() 实现的,具体代码如下:

static int binder_dec_ref(struct binder_ref **ptr_to_ref, int strong)
{
	struct binder_ref *ref = *ptr_to_ref;
	if (strong) {
		if (ref->strong == 0) {
			binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n",
					  ref->proc->pid, ref->debug_id,
					  ref->desc, ref->strong, ref->weak);
			return -EINVAL;
		}
		ref->strong--;
		if (ref->strong == 0) {
			int ret;
			ret = binder_dec_node(ref->node, strong, 1);
			if (ret)
				return ret;
		}
	} else {
		if (ref->weak == 0) {
			binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n",
					  ref->proc->pid, ref->debug_id,
					  ref->desc, ref->strong, ref->weak);
			return -EINVAL;
		}
		ref->weak--;
	}
	if (ref->strong == 0 && ref->weak == 0) {
		binder_delete_ref(ref);
		*ptr_to_ref = NULL;
	}
	return 0;
}

再看函数 binder_delete_ref(),功能是销毁binder ref对象。

static void binder_delete_ref(struct binder_ref *ref)
{
	binder_debug(BINDER_DEBUG_INTERNAL_REFS,
		     "%d delete ref %d desc %d for node %d\n",
		      ref->proc->pid, ref->debug_id, ref->desc,
		      ref->node->debug_id);

	rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
	rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
	if (ref->strong)
		binder_dec_node(ref->node, 1, 1);
	hlist_del(&ref->node_entry);
	binder_dec_node(ref->node, 0, 1);
	if (ref->death) {
		binder_debug(BINDER_DEBUG_DEAD_BINDER,
			     "%d delete ref %d desc %d has death notification\n",
			      ref->proc->pid, ref->debug_id, ref->desc);
		list_del(&ref->death->work.entry);
		kfree(ref->death);
		binder_stats_deleted(BINDER_STAT_DEATH);
	}
	kfree(ref);
	binder_stats_deleted(BINDER_STAT_REF);
}

7 代理对象 BpBinder

  在Android 系统中,代理对象 BpBinder 是远程对象在当前进程的代理,它实现了 IBinder 接口。对于初学者来说,BBinder 与 BpBinder 这两者容易混淆。**其实这两者很好区分,对于Service 来说继承了BBinder(BnInterface),因为 BBinder 有 onTransact() 消息处理函数。而对于与 Service 通信的 Client 来说,需要继承 BpBinder(BpInterface),因为 BpBinder 有消息传递函数 transact() **

7.1 创建 Binder 代理对象

  以 cameraService 的 client 为例,文件 Camera.cpp 中的函数 getCameraService() 能够获取远程CameraService 的IBinder 对象,然后通过如下代码进行了重构,得到了 BpCameraService 对象。

mCameraService = interface_cast<ICameraService>(binder);

而BpCameraService 继承了 BpInterface,并传入了BBinder。

cameraService:
	defaultServiceManager()->addService(
		String16("media.camera"), new CameraService());

  在IPC传递的过程中,IBinder 指针不可缺少。这个指针对一个进程来说就像是 socket 的 ID 一样是唯一的。无论这个 IBinder 是 BBinder 还是 BpBinder,它们都是在重构 BpBinder 或者 BBinder 时把 IBinder 作为参数传入的。在Android系统中,创建 Binder 代理对象的方法在文件 frameworks/native/libs/binder/BpBinder.cpp 中定义,具体代码如下所示。

BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);
	//设置Binder代理对象的生命周期受到弱引用的计数影响
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    //调用当前线程内部的IPCThreadState的成员函数incWeakHandle()增加相应的Binder引用对象的弱引用计数
    IPCThreadState::self()->incWeakHandle(handle);
}

在文件frameworks/native/libs/binder/IPCThreadState.cpp 中,定义成员函数incWeakHandle() 的代码。

void IPCThreadState::incWeakHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
    mOut.writeInt32(BC_INCREFS);
    mOut.writeInt32(handle);
}

7.2 销毁 Binder 代理对象

  当销毁一个Binder代理对象时,线程会调用内部的IPCThreadState对象的成员函数decWeakHandle0来减少相应的 Binder 引用对象的弱引用计数。函数decWeakHandle0的具体实现代码如下所示。

void IPCThreadState::decWeakHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
    mOut.writeInt32(BC_DECREFS);
    mOut.writeInt32(handle);
}

在Binder 代理对象中,其 transact0函数的实现代码如下所示。

/*
	各个参数的具体说明如下所示:
	code:表示请求的ID号。
	data:表示请求的参数。
	reply:表示返回的结果。
	flags:表示一些额外的标识,如FLAG_ONEWAY,通常为0
*/
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

上述 transact() 函数只是简单调用了 IPCThreadState::self() 的 transact,IPCThreadState::transact 中的代码如下。

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;

    IF_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }

    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif

        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;

        cmd = (uint32_t)mIn.readInt32();

        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;

        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;

        case BR_ACQUIRE_RESULT:
            {
                ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;

        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
                    continue;
                }
            }
            goto finish;

        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }

    return err;
}

  在上述代码中,通过内核模块 transact 将请求发送给服务端。当服务端处理完请求之后,会沿着原路返回结果给调用者。在此可以看出请求是同步操作,它会等待直到结果返回为止。这样在 BpBinder 之上进行简单包装之后,就可以得到与服务对象相同的接口,调用者无须关心调用的对象是远程的还是本地的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/590514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SER5 5500U 黑苹果系统安装教程

注意事项&#xff1a; 安装黑苹果系统需要一定的技术和操作经验&#xff0c;而且存在一定的风险&#xff0c;安装前请注意备硬盘内的数据&#xff0c;以免数据丢失&#xff0c;不推荐普通用户进行尝试&#xff0c;本系统及引导由黑果小兵独家制作&#xff0c;更多黑苹果教程百度…

安装CHATGPT保姆级教程(windows版)

ai包链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1tKuG4OfkewlDRU292vx8mw?pwdtw8t 提取码&#xff1a;tw8t 一、安装篇 安装python&#xff0c;使用软件包中的python安装程序安装后检查是否安装成功&#xff0c;cmd窗口运行命令&#xff1a; python –vers…

【九章斩题录】C/C++:替换空格(JZ5)

精品题解 &#x1f525; 《九章斩题录》 &#x1f448; 猛戳订阅 &#x1f4dc; 目录&#xff1a; JZ5 - 替换空格 「 法一 」暴力美学 「 法二 」另开数组 「 法三 」反向替换&#xff08;利用 rfind replace&#xff09; 「 整活 」不用C&#xff0c;Python 一行代码搞…

C#基本语法

关键字 标识符命名规则 标识符中只能出现英文字母、数字、下划线&#xff0c;以及这几种字符&#xff0c;不能出现诸如"空格&#xff0c;&#xff01;"等这些字符&#xff1b;标识符名称只能以下划线&#xff0c;字母以及打头&#xff0c;不可以用数字作为标识符名…

企业如何运用CRM实现企业数字化战略布局

随着数字化时代的到来&#xff0c;越来越多的企业开始意识到数字化转型的重要性&#xff0c;而CRM&#xff08;客户关系管理&#xff09;系统则是企业数字化战略布局中的重要一环&#xff0c;也逐渐受到越来越多企业的关注。 那么&#xff0c;企业如何运用CRM实现数字化战略布局…

自动化测试实战(一)12306火车票网站自动登录工具

还记得2011年春运&#xff0c;12306火车票预订网站经常崩溃无法登录吗。 今天我们就开发一个12306网站自动登录软件。 帮助您轻松订票 Web的原理就是&#xff0c;浏览器发送一个Request给Web服务器&#xff0c;Web服务器处理完这个请求之后发送一个HTTP Response给浏览器。 如…

Mybatis源码细节探究:MappedStatement和Cache对象对照关系研究

给自己的每日一句 不从恶人的计谋&#xff0c;不站罪人的道路&#xff0c;不坐亵慢人的座位&#xff0c;惟喜爱耶和华的律法&#xff0c;昼夜思想&#xff0c;这人便为有福&#xff01;他要像一棵树栽在溪水旁&#xff0c;按时候结果子&#xff0c;叶子也不枯干。凡他所做的尽…

《面试1v1》类加载过程

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a; 你了解Java的类加载过程吗?跟我聊聊classes是如何加载到JVM中的。 候选人&#xff1a; Java的类加载过程由加载、验证、准备、解析和初始化5个…

5月底了,现在不想着跳槽可就晚了

前两天跟朋友感慨&#xff0c;今年的铜三铁四、裁员、疫情导致好多人都没拿到offer!现在已经5月底了&#xff0c;具体金九银十只剩下三个月。 对于想跳槽的职场人来说&#xff0c;绝对要从现在开始做准备了。这时候&#xff0c;很多高薪技术岗、管理岗的缺口和市场需求也出来了…

五、常用提升物体的方式

机器人在运动中伸展和提升功能&#xff0c;历来是比赛中机器人的一个重要的性能指标&#xff0c;因为按 比赛要求&#xff0c;机器人在上场前必须经过体积大小的检测&#xff0c;而在场地中如果能够有更好的伸展性 能将对比赛成绩十分有利&#xff0c;因此机器人提升自身高度以…

SAP-MM发票校验容差详解

MIRO发票校验容差详解 MIRO发票校验容差是指收货业务与发票校验业务之间的差异&#xff0c;这种差异可 能是物料价格差异、收货数量差异、收货金额差异等等&#xff0c;总之&#xff0c;发票校验的容差 内容最为丰富&#xff0c;容差类型有很多种&#xff0c;如下表所示&…

【ChatGPT插件第一期】28个ChatGPT插件,让你的学习、工作、生活效率翻倍!

文章目录 人工智能福利文章什么是ChatGPT插件如何使用ChatGPT插件28个插件全解读写在最后 人工智能福利文章 【分享几个国内免费可用的ChatGPT镜像】【10几个类ChatGPT国内AI大模型】【用《文心一言》1分钟写一篇博客简直yyds】【用讯飞星火大模型1分钟写一个精美的PPT】 Cha…

Linux I2C驱动分析4 - GPIO模拟I2C

一. 前言 在嵌入式开发中&#xff0c;由于芯片的I2C接口有限&#xff0c;或者出于硬件画板的方便&#xff0c;我们都需要将普通的GPIO模拟I2C接口使用。出于对这样的需求&#xff0c;Linux-2.6.x已经有相关代码了&#xff0c;Linux-3.x有标准的内核选项支持该功能&#xff0c;内…

模特信息管理系统的开发与实现(ASP.NET,SQLServer)

需求分析 模特信息管理系统主要给商家和模特用户提供服务&#xff0c;系统分为前台和后台两部分。 本研究课题重点主要包括&#xff1a;活动管理&#xff0c;商家管理&#xff0c;模特管理&#xff0c;系统公告管理和活动报名管理。 活动管理模块主要实现活动更新、活动添加、活…

生鲜农产品冷链物流配送路径优化模型构建及算法实现

摘要&#xff1a;本案例讲述的案例为生鲜农产品冷链物流配送路径优化&#xff0c;涉及的目标函数成本包括碳排放成本、固定成本、运输成本、货损变质成本、时间惩罚成本。 目标种类&#xff1a;单目标模型。 求解方法&#xff1a;基础版蚁群算法改进版蚁群算法。 整体对标层…

快速掌握EasyExcel在web场景中的应用(读和写)

目录 一、引入依赖 二、设置表头 三、web下载模板 四、测试下载功能 五、复杂表头 六、写入数据 七、格式优化 7.1 日期自定义转换 7.2 列宽行高注解 八、动态表头 九、动态表格写入数据 一、引入依赖 <dependency><groupId>com.alibaba</groupId>…

Linux——生产者消费者模型和信号量

目录​​​​​​​ 基于BlockingQueue的生产者消费者模型 概念 条件变量的第二个参数的作用 锁的作用 生产者消费者模型的高效性 生产者而言&#xff0c;向blockqueue里面放置任务 消费者而言&#xff0c;从blockqueue里面拿取任务&#xff1a; 总结 完整代码(不含存储…

从零开始搭建一个moveit2简单机械臂模型

文章目录 前言一、设计一个简单机械臂二、构造创建文件关系CMakeLists.txt 修改增加如下&#xff1a;package.xml 修改增加如下&#xff1a;urdf.rviz 全文如下&#xff1a;demo.launch.py 全文如下&#xff1a; launch rviz构造link构造joint 总结 前言 在网上搜了许多文章&a…

游戏互动,用Python点燃【儿童节】的欢乐!

当孩子们踏入人生的旅途时&#xff0c;他们需要的并不仅仅是学习知识&#xff0c;更需要的是在快乐的氛围中成长。六一儿童节即将来临&#xff0c;让我们用Python代码为孩子们送去一份特别的礼物吧&#xff01; 在本篇文章中&#xff0c;我会带领大家一起探索如何利用Python代…

php中文字符串提取方法,preg_replace 和preg_match_all区别

在php中&#xff0c;可以利用以下两种函数来只提取字符串的中文字符 preg_replace()函数 preg_match_all()函数 方法1&#xff1a;使用preg_match_all()函数 preg_match_all()函数配合正则表达式“/[\x{4e00}-\x{9fff}]/u”可以过滤字符串&#xff0c;只获取中文字符。 会…