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

手写一个C++ Android Binder服务及源码分析

手写一个C++ Android Binder服务及源码分析

  • 前言
  • 一、 基于C语言编写Android Binder跨进程通信Demo总结及改进
  • 二、C++语言编写自己的Binder服务Demo
    • 1. binder服务demo功能介绍
    • 2. binder服务demo代码结构图
    • 3. binder服务demo代码实现
      • 3.1 IHelloService.h代码实现
      • 3.2 BnHelloService.cpp代码实现
      • 3.3 BpHelloService.cpp代码实现
      • 3.4 test_server.cpp代码实现
      • 3.5 test_client.cpp代码实现
      • 3.6 编译binder服务的Android.mk文件
  • 三、C++编写的Binder服务Demo源码解析
    • 1. test_client.cpp源码解析
      • 1.1 分析如何获得BpServiceManager对象
      • 1.2 分析如何获得BpHelloServie对象
      • 1.3 代理类如何发送数据?
    • 2. test_server.cpp源码解析
      • 2.1 分析数据传输机制ProcessState和IPCThreadState
      • 2.2 分析向servicemanager添加服务的具体过程
      • 2.3 分析server如何分辨client想使用哪一个服务,并调用对应的函数

前言

之前我写了三篇文章深入内核讲明白Android Binder,这三篇文章可以理解为是Android Binder的原理篇,本篇文章算是应用篇,讲明白如何使用C++语言编写自己的binder服务(C++只是利用语言优势,更好的封装了C语言实现的Android binder,方便开发人员使用binder),并深入源码搞清楚C++编写binder服务的逻辑。
深入内核讲明白Android Binder【一】
深入内核讲明白Android Binder【二】
深入内核讲明白Android Binder【三】

阅读建议:

  1. 如果有余力建议去阅读下我之前写的三篇【深入内核讲明白Android Binder】系列的文章,这三篇文章深入linux内核解析android binder源码,如果搞清楚了这三篇文章,再来看这篇使用C++应用Android binder的文章就非常容易理解。
  2. 如果直接看这篇文章也是可以的,但这篇文章只能使您停留在会使用C++语言编写自己的Android binder服务的层面上。

一、 基于C语言编写Android Binder跨进程通信Demo总结及改进

深入内核讲明白Android Binder【一】我们讲解了如何使用C语言编写自己的binder服务,但所有代码都写在一个c文件中。职责不够分明,代码不易重用,也不利于代码阅读和维护。
在这里插入图片描述

我们可以把上面的结构优化下面的结构,即定义一个头文件IHelloService.h包含服务端和客户端共用的函数接口,BnHelloService和BpHelloService充当代理,分别实现客户端和服务端的功能,分别供binder服务test_server和客户端test_client.c使用。而这也是C++语言编写binder服务的整体框架。
在这里插入图片描述

二、C++语言编写自己的Binder服务Demo

1. binder服务demo功能介绍

我们继续采用深入内核讲明白Android Binder【一】中C语言实现的binder服务功能。C++实现的binder服务也提供sayhello和sayhello_to两个函数供客户端调用。

2. binder服务demo代码结构图

  • IHelloService.h:定义服务端和客户端共用的接口类。
  • BnHelloService.cpp:服务端的代理类。BnHelloService类的具体实现(binder服务的本地实现)
  • BpHelloService.cpp:客户端的代理类。文件中定义BpHelloService类,继承IHelloService(客户端调用binder服务的实现)
  • test_server.cpp:binder服务提供者(服务端)
  • test_client.cpp:binder服务使用者(客户端)
    在这里插入图片描述

3. binder服务demo代码实现

3.1 IHelloService.h代码实现

  • 参考代码:frameworks\av\include\media\IMediaPlayerService.h

定义接口类IHelloService,类中有sayhello,sayhello_to两个纯虚函数。BnHelloService类和BpHelloService类会继承IHelloService实现这两个虚函数。

/* 参考: frameworks\av\include\media\IMediaPlayerService.h */#ifndef ANDROID_IHELLOERVICE_H
#define ANDROID_IHELLOERVICE_H#include <utils/Errors.h>  // for status_t
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>#define HELLO_SVR_CMD_SAYHELLO     1
#define HELLO_SVR_CMD_SAYHELLO_TO  2
#define HELLO_SVR_CMD_GET_FD       3namespace android {class IHelloService: public IInterface
{
public://声明接口DECLARE_META_INTERFACE(HelloService);virtual void sayhello(void) = 0;virtual int sayhello_to(const char *name) = 0;
};
//继承自IHelloService和BBinder
class BnHelloService: public BnInterface<IHelloService>
{
public:virtual status_t    onTransact( uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags = 0);virtual void sayhello(void);virtual int sayhello_to(const char *name);BnHelloService();
};
}//https://github.com/CyanogenMod/android_frameworks_native/blob/cm-14.1/include/binder/IInterface.h
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);virtual const String16&     getInterfaceDescriptor() const;typedef INTERFACE BaseInterface;protected:virtual IBinder*            onAsBinder();
};//通过预处理器运算符##(标记粘合符),声明接口
#define DECLARE_META_INTERFACE(INTERFACE)                               \static const android::String16 descriptor;                          \static android::sp<I##INTERFACE> asInterface(                       \const android::sp<android::IBinder>& obj);                  \virtual const android::String16& getInterfaceDescriptor() const;    \I##INTERFACE();                                                     \virtual ~I##INTERFACE();   

3.2 BnHelloService.cpp代码实现

  • 参考代码:frameworks\av\media\libmedia\IMediaPlayerService.cpp

BnHelloService作为服务端的代理类,是binder服务的本地实现

/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */#define LOG_TAG "HelloService"#include "IHelloService.h"namespace android {BnHelloService::BnHelloService()
{
}// 根据code,确定调用服务的哪一个函数
status_t BnHelloService::onTransact( uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags)
{/* 解析数据,调用sayhello/sayhello_to */switch (code) {case HELLO_SVR_CMD_SAYHELLO: {sayhello();reply->writeInt32(0);  /* no exception */return NO_ERROR;} break;case HELLO_SVR_CMD_SAYHELLO_TO: {/* 从data中取出参数 */int32_t policy =  data.readInt32();String16 name16_tmp = data.readString16(); /* IHelloService */String16 name16 = data.readString16();String8 name8(name16);int cnt = sayhello_to(name8.string());/* 把返回值写入reply传回去 */reply->writeInt32(0);  /* no exception */reply->writeInt32(cnt);return NO_ERROR;} break;       default:return BBinder::onTransact(code, data, reply, flags);}
}void BnHelloService::sayhello(void)
{static int cnt = 0;ALOGI("say hello : %d\n", ++cnt);}int BnHelloService::sayhello_to(const char *name)
{static int cnt = 0;ALOGI("say hello to %s : %d\n", name, ++cnt);return cnt;
}
}

3.3 BpHelloService.cpp代码实现

  • 参考代码:frameworks\av\media\libmedia\IMediaPlayerService.cpp

BpHelloService作为客户端的代理类,是客户端调用binder服务的具体实现。
客户端调用服务端函数的步骤:1. 构造数据;2. 向服务端发送数据;3. 获取服务端返回的数据


/* 参考: frameworks\av\media\libmedia\IMediaPlayerService.cpp */#include "IHelloService.h"namespace android {
//继承自IHelloService和BpRefBase
class BpHelloService: public BpInterface<IHelloService>
{
public:BpHelloService(const sp<IBinder>& impl): BpInterface<IHelloService>(impl){}void sayhello(void){/* 构造/发送数据 */Parcel data, reply;//为了与C实现的binder兼容,需要把传入的数据格式与C读取的数据格式一致data.writeInt32(0);data.writeString16(String16("IHelloService"));// 发送数据remote()->transact(HELLO_SVR_CMD_SAYHELLO, data, &reply);}int sayhello_to(const char *name){/* 构造/发送数据 */Parcel data, reply;int exception;data.writeInt32(0);data.writeString16(String16("IHelloService"));data.writeString16(String16(name));// 发送数据remote()->transact(HELLO_SVR_CMD_SAYHELLO_TO, data, &reply);// 获得服务返回的数据exception = reply.readInt32();if (exception)return -1;elsereturn reply.readInt32();}
};// 实现接口
IMPLEMENT_META_INTERFACE(HelloService, "android.media.IHelloService");}//https://github.com/CyanogenMod/android_frameworks_native/blob/cm-14.1/include/binder/IInterface.h
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:explicit                    BpInterface(const sp<IBinder>& remote);typedef INTERFACE BaseInterface;protected:virtual IBinder*            onAsBinder();
};//通过预处理器运算符##(标记粘合符),实现接口   #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \const android::String16 I##INTERFACE::descriptor(NAME);             \const android::String16&                                            \I##INTERFACE::getInterfaceDescriptor() const {              \return I##INTERFACE::descriptor;                                \}                                                                   \android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \const android::sp<android::IBinder>& obj)                   \{                                                                   \android::sp<I##INTERFACE> intr;                                 \if (obj != NULL) {                                              \intr = static_cast<I##INTERFACE*>(                          \obj->queryLocalInterface(                               \I##INTERFACE::descriptor).get());               \if (intr == NULL) {                                         \intr = new Bp##INTERFACE(obj);                          \}                                                           \}                                                               \return intr;                                                    \}                                                                   \I##INTERFACE::I##INTERFACE() { }                                    \I##INTERFACE::~I##INTERFACE() { }     

3.4 test_server.cpp代码实现

  • 参考代码:frameworks\av\media\mideaserver/main_mediaserver.cpp

test_server是binder服务提供者(服务端),核心步骤:

  1. 打开驱动
  2. 获取servicemanager
  3. 向servicemanager中添加服务
  4. 死循环等待客户端发来数据,并进行处理和回复

/* 参考: frameworks\av\media\mediaserver\Main_mediaserver.cpp */#define LOG_TAG "TestService"
//#define LOG_NDEBUG 0#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>#include "IHelloService.h"
#include "IGoodbyeService.h"#define SOCKET_BUFFER_SIZE      (32768U)using namespace android;/* usage : test_server  */
int main(void)
{/* 打开驱动, mmap */sp<ProcessState> proc(ProcessState::self());/* 获得BpServiceManager (servicemanager的代理类)*/sp<IServiceManager> sm = defaultServiceManager();/* 添加服务 */sm->addService(String16("hello"), new BnHelloService(sockets[1]));/* 循环体 */ProcessState::self()->startThreadPool(); // 创建子线程,死循环等待客户端发送来的数据IPCThreadState::self()->joinThreadPool(); // 创建主线程,死循环等待客户端发送来的数据return 0;
}

3.5 test_client.cpp代码实现

  • 参考代码:frameworks\av\media\mideaserver/main_mediaserver.cpp

test_client是binder服务使用者(客户端),实现核心步骤:

  1. 打开驱动
  2. 获得servicemanager
  3. 从servicemanager获得服务
  4. 调用服务的函数
#define LOG_TAG "TestService"
//#define LOG_NDEBUG 0#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <unistd.h>#include "IHelloService.h"
#include "IGoodbyeService.h"using namespace android;/* ./test_client <hello>* ./test_client <hello> <name>*/
int main(int argc, char **argv)
{int cnt;if (argc < 2){ALOGI("Usage:\n");ALOGI("%s <readfile>\n", argv[0]);ALOGI("%s <hello|goodbye>\n", argv[0]);ALOGI("%s <hello|goodbye> <name>\n", argv[0]);return -1;}/* getService *//* 打开驱动, mmap */sp<ProcessState> proc(ProcessState::self());/* 获得BpServiceManager (servicemanager的代理类) */sp<IServiceManager> sm = defaultServiceManager();if (strcmp(argv[1], "hello") == 0){sp<IBinder> binder =sm->getService(String16("hello"));if (binder == 0){ALOGI("can't get hello service\n");return -1;}/* service肯定是BpHelloServie指针 */sp<IHelloService> service =interface_cast<IHelloService>(binder);/* 调用Service的函数 */if (argc < 3) {service->sayhello();ALOGI("client call sayhello");}else {cnt = service->sayhello_to(argv[2]);ALOGI("client call sayhello_to, cnt = %d", cnt);}}return 0;
}

3.6 编译binder服务的Android.mk文件

  • 参考代码:frameworks\av\media\mideaserver/Android.mk
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES:= \BnHelloService.cpp \BpHelloService.cpp \BnGoodbyeService.cpp \BpGoodbyeService.cpp \test_server.cppLOCAL_SHARED_LIBRARIES := \libcutils \libutils \liblog \libbinder LOCAL_MODULE:= test_server
LOCAL_32_BIT_ONLY := trueinclude $(BUILD_EXECUTABLE)include $(CLEAR_VARS)LOCAL_SRC_FILES:= \BpHelloService.cpp \BpGoodbyeService.cpp \test_client.cppLOCAL_SHARED_LIBRARIES := \libcutils \libutils \liblog \libbinder LOCAL_MODULE:= test_client
LOCAL_32_BIT_ONLY := trueinclude $(BUILD_EXECUTABLE

三、C++编写的Binder服务Demo源码解析

一图简单回顾下【深入内核讲明白Android Binder】系列的文章中讲解的Binder跨进程通信。
在这里插入图片描述

1. test_client.cpp源码解析

1.1 分析如何获得BpServiceManager对象

BpServiceManager是servicemanager服务的代理对象,通过BpServiceManager调用servicemanager提供的服务函数,BpServiceManager源码链接IServiceManager.cpp,BpServiceManager继承关系图如下
在这里插入图片描述
BpServiceManager继承自IServiceManager和BpRefBase,而BpRefBase中的mRemote成员指向BpBinder对象,BpBinder中mHandle成员代表了服务的句柄,通过mHandle可以获取相应的服务。

//继承自IServiceManager和BpRefBase
class BpServiceManager : public BpInterface<IServiceManager>
{
public:BpServiceManager(const sp<IBinder>& impl): BpInterface<IServiceManager>(impl){}virtual sp<IBinder> getService(const String16& name) const{unsigned n;for (n = 0; n < 5; n++){sp<IBinder> svc = checkService(name);if (svc != NULL) return svc;ALOGI("Waiting for service %s...\n", String8(name).string());sleep(1);}return NULL;}virtual sp<IBinder> checkService( const String16& name) const{Parcel data, reply;data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);return reply.readStrongBinder();}virtual status_t addService(const String16& name, const sp<IBinder>& service,bool allowIsolated){Parcel data, reply;data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);data.writeStrongBinder(service);data.writeInt32(allowIsolated ? 1 : 0);status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);return err == NO_ERROR ? reply.readExceptionCode() : err;}virtual Vector<String16> listServices(){Vector<String16> res;int n = 0;for (;;) {Parcel data, reply;data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeInt32(n++);status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);if (err != NO_ERROR)break;res.add(reply.readString16());}return res;}
};IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");}; // namespace androidclass 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);inline  IBinder*        remote()                { return mRemote; }inline  IBinder*        remote() const          { return mRemote; }private:BpRefBase(const BpRefBase& o);BpRefBase&              operator=(const BpRefBase& o);IBinder* const          mRemote;//指向BpBinder对象RefBase::weakref_type*  mRefs;std::atomic<int32_t>    mState;
};}; // namespace androidclass BpBinder : public IBinder
{
public:BpBinder(int32_t handle);inline  int32_t     handle() const { return mHandle; }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 BpBinder*   remoteBinder();status_t    setConstantData(const void* data, size_t size);void        sendObituary();class ObjectManager{public:ObjectManager();~ObjectManager();void        attach( const void* objectID,void* object,void* cleanupCookie,IBinder::object_cleanup_func func);void*       find(const void* objectID) const;void        detach(const void* objectID);void        kill();private:ObjectManager(const ObjectManager&);ObjectManager& operator=(const ObjectManager&);struct entry_t{void* object;void* cleanupCookie;IBinder::object_cleanup_func func;};KeyedVector<const void*, entry_t> mObjects;};protected:virtual             ~BpBinder();virtual void        onFirstRef();virtual void        onLastStrongRef(const void* id);virtual bool        onIncStrongAttempted(uint32_t flags, const void* id);private:const   int32_t             mHandle;//最核心的数据struct Obituary {wp<DeathRecipient> recipient;void* cookie;uint32_t flags;};void                reportOneDeath(const Obituary& obit);bool                isDescriptorCached() const;mutable Mutex               mLock;volatile int32_t    mAlive;volatile int32_t    mObitsSent;Vector<Obituary>*   mObituaries;ObjectManager       mObjects;Parcel*             mConstantData;mutable String16            mDescriptorCache;
};

之前实现的C++Demo中可知我们是通过defaultServiceManager函数获得BpServiceManager

/* usage : test_server  */
int main(void)
{/* 打开驱动, mmap */sp<ProcessState> proc(ProcessState::self());/* 获得BpServiceManager */sp<IServiceManager> sm = defaultServiceManager();sm->addService(String16("hello"), new BnHelloService(sockets[1]));sm->addService(String16("goodbye"), new BnGoodbyeService());/* 循环体 */ProcessState::self()->startThreadPool();IPCThreadState::self()->joinThreadPool();return 0;
}

defaultServiceManager方法源码地址defaultServiceManager()

//IServiceManager.cpp
sp<IServiceManager> defaultServiceManager()
{   //单例模式if (gDefaultServiceManager != NULL) return gDefaultServiceManager;{AutoMutex _l(gDefaultServiceManagerLock);while (gDefaultServiceManager == NULL) { //把BpBinder(mHandle=0)对象转换为IServiceManager接口(BpServiceManager)gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL));if (gDefaultServiceManager == NULL)sleep(1);}}return gDefaultServiceManager;
}
//sp是一个智能指针类,当没有任何引用指向 `IServiceManager` 实例时,它会自动被销毁。
sp<IServiceManager> gDefaultServiceManager;

ProcessState::self()->getContextObject(NULL))获得BpBinder对象, new BpBinder(0)

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{return getStrongProxyForHandle(0);
}sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{sp<IBinder> result;AutoMutex _l(mLock);handle_entry* e = lookupHandleLocked(handle);if (e != NULL) {// 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.  See comment// in getWeakProxyForHandle() for more info about this.IBinder* b = e->binder;if (b == NULL || !e->refs->attemptIncWeak(this)) {if (handle == 0) {// 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.Parcel data;status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);if (status == DEAD_OBJECT)return NULL;}b = new BpBinder(handle); //传进来的handle等于0e->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;
}BpBinder::BpBinder(int32_t handle): mHandle(handle)//handle赋值给mHandle=0, mAlive(1), mObitsSent(0), mObituaries(NULL)
{ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);extendObjectLifetime(OBJECT_LIFETIME_WEAK);IPCThreadState::self()->incWeakHandle(handle);
}

interface_cast(ProcessState::self()->getContextObject(NULL));获得BpServiceManager对象,new BpServiceManager(new BpBinder(0))

interface_cast模板方法调用IServiceManager的asInterface方法,并把new BpBinder(0)传给asInterface方法。

  • IServiceManager的asInterface方法在IServiceManager类中通过DECLARE_META_INTERFACE(ServiceManager);进行声明
  • IServiceManager的asInterface方法在BpServiceManager类中通过IMPLEMENT_META_INTERFACE(ServiceManager, “android.os.IServiceManager”);进行实现
//源码:https://github.com/CyanogenMod/android_frameworks_native/blob/cm-14.1/include/binder/IInterface.h#L42
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{return INTERFACE::asInterface(obj);
}class IServiceManager : public IInterface
{
public://声明接口DECLARE_META_INTERFACE(ServiceManager);/*** Retrieve an existing service, blocking for a few seconds* if it doesn't yet exist.*/virtual sp<IBinder>         getService( const String16& name) const = 0;/*** Retrieve an existing service, non-blocking.*/virtual sp<IBinder>         checkService( const String16& name) const = 0;/*** Register a service.*/virtual status_t            addService( const String16& name,const sp<IBinder>& service,bool allowIsolated = false) = 0;/*** Return list of all existing services.*/virtual Vector<String16>    listServices() = 0;enum {GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,CHECK_SERVICE_TRANSACTION,ADD_SERVICE_TRANSACTION,LIST_SERVICES_TRANSACTION,};
};class BpServiceManager : public BpInterface<IServiceManager>
{
public:BpServiceManager(const sp<IBinder>& impl): BpInterface<IServiceManager>(impl){}virtual sp<IBinder> getService(const String16& name) const{unsigned n;for (n = 0; n < 5; n++){sp<IBinder> svc = checkService(name);if (svc != NULL) return svc;ALOGI("Waiting for service %s...\n", String8(name).string());sleep(1);}return NULL;}virtual sp<IBinder> checkService( const String16& name) const{Parcel data, reply;data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);return reply.readStrongBinder();}virtual status_t addService(const String16& name, const sp<IBinder>& service,bool allowIsolated){Parcel data, reply;data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);data.writeStrongBinder(service);data.writeInt32(allowIsolated ? 1 : 0);status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);return err == NO_ERROR ? reply.readExceptionCode() : err;}virtual Vector<String16> listServices(){Vector<String16> res;int n = 0;for (;;) {Parcel data, reply;data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeInt32(n++);status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);if (err != NO_ERROR)break;res.add(reply.readString16());}return res;}
};//实现接口
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");}; // namespace android//通过预处理器运算符##(标记粘合符),声明接口
#define DECLARE_META_INTERFACE(INTERFACE)                               \static const android::String16 descriptor;                          \static android::sp<I##INTERFACE> asInterface(                       \const android::sp<android::IBinder>& obj);                  \virtual const android::String16& getInterfaceDescriptor() const;    \I##INTERFACE();                                                     \virtual ~I##INTERFACE();   //通过预处理器运算符##(标记粘合符),实现接口   #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \const android::String16 I##INTERFACE::descriptor(NAME);             \const android::String16&                                            \I##INTERFACE::getInterfaceDescriptor() const {              \return I##INTERFACE::descriptor;                                \}                                                                   \android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \const android::sp<android::IBinder>& obj)                   \{                                                                   \android::sp<I##INTERFACE> intr;                                 \if (obj != NULL) {                                              \intr = static_cast<I##INTERFACE*>(                          \obj->queryLocalInterface(                               \I##INTERFACE::descriptor).get());               \if (intr == NULL) {                                         \intr = new Bp##INTERFACE(obj);                          \}                                                           \}                                                               \return intr;                                                    \}                                                                   \I##INTERFACE::I##INTERFACE() { }                                    \I##INTERFACE::~I##INTERFACE() { }      ##INTERFACE用ServiceManager替换,得到BpServiceManager对象

BpBinder在BpServiceManager中如何存储?
new BpServiceManager(new BpBinder(0)),最终将BpBinder对象存储到了BpRefBase对象的mRemote成员中

//BpBinder对象传给了impl
BpServiceManager(const sp<IBinder>& impl): BpInterface<IServiceManager>(impl){}//impl传给了remote
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote): BpRefBase(remote)
{
}//remote传给了mRemote
BpRefBase::BpRefBase(const sp<IBinder>& o): mRemote(o.get()), mRefs(NULL), mState(0)
{extendObjectLifetime(OBJECT_LIFETIME_WEAK);if (mRemote) {mRemote->incStrong(this);           // Removed on first IncStrong().mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.}
}

总结
defaultServiceManager方法构造了一个BpServiceManager对象,它里面的mRemote = new BpBinder(0),也就是mRmote->mHandle=0

1.2 分析如何获得BpHelloServie对象

BpHelloServie继承关系图,和上面讲到的BpServiceManager的继承关系类似。唯一不同的是BpServiceManager的handle是固定的0(代表servicemanager服务进程),而BpHelloServie的handle要通过BpServiceManager的getService方法获得。
在这里插入图片描述
BpHelloService是HelloService服务的代理,通过BpHelloServie可以调用HelloServie服务中提供的服务函数(sayhello/sayhello_to),BpHelloService对象的获取过程有两步:

  1. 通过BpServiceManager的getService方法获取指向hello服务的BpBinder对象。
  2. 将BpBinder对象转换为BpServiceManager对象。

下面通过源码进行验证

int main(int argc, char **argv)
{int cnt;if (argc < 2){ALOGI("Usage:\n");ALOGI("%s <readfile>\n", argv[0]);ALOGI("%s <hello|goodbye>\n", argv[0]);ALOGI("%s <hello|goodbye> <name>\n", argv[0]);return -1;}/* getService *//* 打开驱动, mmap */sp<ProcessState> proc(ProcessState::self());/* 获得BpServiceManager */sp<IServiceManager> sm = defaultServiceManager();if (strcmp(argv[1], "hello") == 0){// 从servicemanager获得hello服务的BpBinder对象sp<IBinder> binder =sm->getService(String16("hello"));if (binder == 0){ALOGI("can't get hello service\n");return -1;}/* 将binder转换为BpHelloService对象,这里的service肯定是BpHelloServie指针 */sp<IHelloService> service =interface_cast<IHelloService>(binder);/* 调用Service的函数 */if (argc < 3) {service->sayhello();ALOGI("client call sayhello");}else {cnt = service->sayhello_to(argv[2]);ALOGI("client call sayhello_to, cnt = %d", cnt);}}return 0;
}

sp binder = sm->getService(String16(“hello”))源码分析,这里binder是BpBinder对象,里面含有HelloService的handle

virtual sp<IBinder> getService(const String16& name) const
{unsigned n;for (n = 0; n < 5; n++){// 从servicemanager获取服务sp<IBinder> svc = checkService(name);if (svc != NULL) return svc;ALOGI("Waiting for service %s...\n", String8(name).string());sleep(1);}return NULL;
}virtual sp<IBinder> checkService( const String16& name) const
{Parcel data, reply;//构造数据data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);//name = "hello"//发送数据:给handle=0,即service_manager进程remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);//从收到的回复中取出HelloService的handle,构造BpBinderreturn reply.readStrongBinder();
}sp<IBinder> Parcel::readStrongBinder() const
{sp<IBinder> val;readStrongBinder(&val);return val;
}status_t Parcel::readStrongBinder(sp<IBinder>* val) const
{return unflatten_binder(ProcessState::self(), *this, val);
}status_t unflatten_binder(const sp<ProcessState>& proc,const Parcel& in, sp<IBinder>* out)
{const flat_binder_object* flat = in.readObject(false);if (flat) {switch (flat->type) {case BINDER_TYPE_BINDER:*out = reinterpret_cast<IBinder*>(flat->cookie);return finish_unflatten_binder(NULL, *flat, in);case BINDER_TYPE_HANDLE:// flat->handle是hello服务handle,客户端可以通过这个handle找到hello服务*out = proc->getStrongProxyForHandle(flat->handle);return finish_unflatten_binder(static_cast<BpBinder*>(out->get()), *flat, in);}}return BAD_TYPE;
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{sp<IBinder> result;AutoMutex _l(mLock);handle_entry* e = lookupHandleLocked(handle);if (e != NULL) {// 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.  See comment// in getWeakProxyForHandle() for more info about this.IBinder* b = e->binder;if (b == NULL || !e->refs->attemptIncWeak(this)) {if (handle == 0) {// 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.Parcel data;status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);if (status == DEAD_OBJECT)return NULL;}//构造BpBinder,handle是ServiceManager的handleb = new BpBinder(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;
}

sp service = interface_cast(binder),使用BpBinder(其中的handle是HelloService的handle)获得一个IHelloService的接口(其实是BpHelloService对象),这个过程和上面将BpBinder对象转换为BpServiceManager对象的过程一致,不再赘述。

1.3 代理类如何发送数据?

代理类通过如下两个步骤发送数据

  1. 构造数据
  2. 调用remote()->transact

如BpServiceManager发送数据源码

class BpServiceManager : public BpInterface<IServiceManager>
{
public:BpServiceManager(const sp<IBinder>& impl): BpInterface<IServiceManager>(impl){}virtual sp<IBinder> getService(const String16& name) const{unsigned n;for (n = 0; n < 5; n++){sp<IBinder> svc = checkService(name);if (svc != NULL) return svc;ALOGI("Waiting for service %s...\n", String8(name).string());sleep(1);}return NULL;}virtual sp<IBinder> checkService( const String16& name) const{Parcel data, reply;// 构造数据data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);// 调用remote()->transactremote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);return reply.readStrongBinder();}virtual status_t addService(const String16& name, const sp<IBinder>& service,bool allowIsolated){Parcel data, reply;// 构造数据data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);data.writeStrongBinder(service);data.writeInt32(allowIsolated ? 1 : 0);// 调用remote()->transactstatus_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);return err == NO_ERROR ? reply.readExceptionCode() : err;}......
};

如BpHelloService发送数据源码

class BpHelloService: public BpInterface<IHelloService>
{
public:BpHelloService(const sp<IBinder>& impl): BpInterface<IHelloService>(impl){}void sayhello(void){/* 构造/发送数据 */Parcel data, reply;data.writeInt32(0);data.writeString16(String16("IHelloService"));// 调用remote()->transactremote()->transact(HELLO_SVR_CMD_SAYHELLO, data, &reply);}int sayhello_to(const char *name){/* 构造/发送数据 */Parcel data, reply;int exception;data.writeInt32(0);data.writeString16(String16("IHelloService"));data.writeString16(String16(name));// 调用remote()->transactremote()->transact(HELLO_SVR_CMD_SAYHELLO_TO, data, &reply);exception = reply.readInt32();if (exception)return -1;elsereturn reply.readInt32();}......
};

我们来分析下remote()->transact

  1. remote()函数获得是BpRefBase中的mRemote,而mRemote指向的是BpBinder,这点从上面分析获得BpServiceManager和BpHelloService对象的时候可知。
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);inline  IBinder*        remote()                { return mRemote; }inline  IBinder*        remote() const          { return mRemote; }private:BpRefBase(const BpRefBase& o);BpRefBase&              operator=(const BpRefBase& o);IBinder* const          mRemote; // BpBinder对象RefBase::weakref_type*  mRefs;std::atomic<int32_t>    mState;
};
  1. 那么接着分析BpBinder中的transact函数
    transact调用IPCThreadState::self()->transact方法,接着调用waitForResponse方法,接着调用talkWithDriver方法,而talkWithDriver方法最终调用ioctl发送数据。
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) {//这儿的参数就和C的ioctl需要的参数差不多了,mHandle是指目的进程,code是指调用服务的哪个函数//data是携带的数据,reply是要回复的数据status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);if (status == DEAD_OBJECT) mAlive = 0;return status;}return DEAD_OBJECT;
}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 0if (code == 4) { // relayoutALOGI(">>>>>> CALLING transaction 4");} else {ALOGI(">>>>>> CALLING transaction %d", code);}#endifif (reply) {err = waitForResponse(reply);} else {Parcel fakeReply;err = waitForResponse(&fakeReply);}#if 0if (code == 4) { // relayoutALOGI("<<<<<< RETURNING transaction 4");} else {ALOGI("<<<<<< RETURNING transaction %d", code);}#endifIF_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;
}status_t IPCThreadState::talkWithDriver(bool doReceive)
{if (mProcess->mDriverFD <= 0) {return -EBADF;}binder_write_read bwr;// Is the read buffer empty?const bool needRead = mIn.dataPosition() >= mIn.dataSize();// We don't want to write anything if we are still reading// from data left in the input buffer and the caller// has requested to read the next data.const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;bwr.write_size = outAvail;bwr.write_buffer = (uintptr_t)mOut.data();// This is what we'll read.if (doReceive && needRead) {bwr.read_size = mIn.dataCapacity();bwr.read_buffer = (uintptr_t)mIn.data();} else {bwr.read_size = 0;bwr.read_buffer = 0;}IF_LOG_COMMANDS() {TextOutput::Bundle _b(alog);if (outAvail != 0) {alog << "Sending commands to driver: " << indent;const void* cmds = (const void*)bwr.write_buffer;const void* end = ((const uint8_t*)cmds)+bwr.write_size;alog << HexDump(cmds, bwr.write_size) << endl;while (cmds < end) cmds = printCommand(alog, cmds);alog << dedent;}alog << "Size of receive buffer: " << bwr.read_size<< ", needRead: " << needRead << ", doReceive: " << doReceive << endl;}// Return immediately if there is nothing to do.if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;bwr.write_consumed = 0;bwr.read_consumed = 0;status_t err;do {IF_LOG_COMMANDS() {alog << "About to read/write, write size = " << mOut.dataSize() << endl;}
#if defined(__ANDROID__)// 最终还是要调用ioctl发送数据if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)err = NO_ERROR;elseerr = -errno;
#elseerr = INVALID_OPERATION;
#endifif (mProcess->mDriverFD <= 0) {err = -EBADF;}IF_LOG_COMMANDS() {alog << "Finished read/write, write size = " << mOut.dataSize() << endl;}} while (err == -EINTR);IF_LOG_COMMANDS() {alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "<< bwr.write_consumed << " (of " << mOut.dataSize()<< "), read consumed: " << bwr.read_consumed << endl;}if (err >= NO_ERROR) {if (bwr.write_consumed > 0) {if (bwr.write_consumed < mOut.dataSize())mOut.remove(0, bwr.write_consumed);elsemOut.setDataSize(0);}if (bwr.read_consumed > 0) {mIn.setDataSize(bwr.read_consumed);mIn.setDataPosition(0);}IF_LOG_COMMANDS() {TextOutput::Bundle _b(alog);alog << "Remaining data size: " << mOut.dataSize() << endl;alog << "Received commands from driver: " << indent;const void* cmds = mIn.data();const void* end = mIn.data() + mIn.dataSize();alog << HexDump(cmds, mIn.dataSize()) << endl;while (cmds < end) cmds = printReturnCommand(alog, cmds);alog << dedent;}return NO_ERROR;}return err;
}

2. test_server.cpp源码解析

启动一个binder服务有以下几个步骤:

  1. 打开驱动
  2. 注册服务
  3. 死循环等待数据
    其中,打开驱动是通过ProcessState对象完成的,循环等待数据是由IPCThreadState创建的主线程和ProcessState创建的子线程完成的。

2.1 分析数据传输机制ProcessState和IPCThreadState

ProcessState对象打开驱动


/* usage : test_server  */
int main(void)
{/* 打开驱动, mmap */sp<ProcessState> proc(ProcessState::self());/* 获得BpServiceManager */sp<IServiceManager> sm = defaultServiceManager();sm->addService(String16("hello"), new BnHelloService(sockets[1]));sm->addService(String16("goodbye"), new BnGoodbyeService());/* 创建循环体,等待被被唤醒:读取数据,解析数据,处理数据,回复数据 */ProcessState::self()->startThreadPool();//创建子线程的IPCThreadStateIPCThreadState::self()->joinThreadPool();//创建主线程的IPCThreadState,并循环等待数据return 0;
}

ProcessState::self()打开驱动

sp<ProcessState> ProcessState::self()
{Mutex::Autolock _l(gProcessMutex);if (gProcess != NULL) {return gProcess;}gProcess = new ProcessState;return gProcess;
}ProcessState::ProcessState(): mDriverFD(open_driver())//打开驱动,并将fd赋值给mDriverFD, mVMStart(MAP_FAILED), mThreadCountLock(PTHREAD_MUTEX_INITIALIZER), mThreadCountDecrement(PTHREAD_COND_INITIALIZER), mExecutingThreadsCount(0), mMaxThreads(DEFAULT_MAX_BINDER_THREADS), mStarvationStartTimeMs(0), mManagesContexts(false), mBinderContextCheckFunc(NULL), mBinderContextUserData(NULL), mThreadPoolStarted(false), mThreadPoolSeq(1)
{if (mDriverFD >= 0) {// mmap,提供一块虚拟地址空间来接收事务// mmap the binder, providing a chunk of virtual address space to receive transactions.mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);if (mVMStart == MAP_FAILED) {// *sigh*ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");close(mDriverFD);mDriverFD = -1;}}LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}static int open_driver()
{int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);if (fd >= 0) {int vers = 0;status_t result = ioctl(fd, BINDER_VERSION, &vers);if (result == -1) {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 does not match user space protocol!");close(fd);fd = -1;}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 '/dev/binder' failed: %s\n", strerror(errno));}return fd;
}

IPCThreadState::self()->joinThreadPool();创建主线程IPCThreadState对象,并循环等待客户端数据

补充知识点:线程特有数据(Thread Special Data)
主线程创建多个子线程,每个子线程有自己的IPCThreadState对象,即该对象是线程特有的,各自不同,那么它应该存在线程的局部空间里。
那么如何实现呢?

  1. 创建键:k = pthread_key_create
    (k,v)
  2. 为键设置值:每个线程为k设置不一样的value
    如对于线程1可以调用pthread_setspecific(k, v1),设置value为v1,
  3. 后续可以在不同线程中获得不同的值
    如在线程1中调用pthread_getspecific(k)就可以得到v1
// 保证每个线程只有一个IPCThreadState对象,该对象通过pthread_getspecific函数获得
IPCThreadState* IPCThreadState::self()
{if (gHaveTLS) {
restart:const pthread_key_t k = gTLS;//获得值IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);if (st) return st;//创建IPCThreadState对象,作为已经创建的键的值return new IPCThreadState;}if (gShutdown) {ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");return NULL;}//多线程下也只会创建一个键pthread_mutex_lock(&gTLSMutex);if (!gHaveTLS) {//创建键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;}gHaveTLS = true;}pthread_mutex_unlock(&gTLSMutex);goto restart;
}IPCThreadState::IPCThreadState()//mProcess = ProcessState::self(),里面含有open驱动的句柄 mDriverFD: mProcess(ProcessState::self()),mMyThreadId(gettid()),mStrictModePolicy(0),mLastTransactionBinderFlags(0)
{//将IPCThreadState对象设置为已经创建的键的值pthread_setspecific(gTLS, this);clearCaller();mIn.setDataCapacity(256);mOut.setDataCapacity(256);
}

IPCThreadState::self()保证每个线程只有一个IPCThreadState对象,该对象通过pthread_getspecific函数获得。IPCThreadState::self()->joinThreadPool()方法循环等待 读取,解析,处理,返回数据

// 循环等待 读取,解析,处理,返回数据
void IPCThreadState::joinThreadPool(bool isMain)
{LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);// This thread may have been spawned by a thread that was in the background// scheduling group, so first we will make sure it is in the foreground// one to avoid performing an initial transaction in the background.set_sched_policy(mMyThreadId, SP_FOREGROUND);status_t result;//循环等待数据do {processPendingDerefs();// now get the next command to be processed, waiting if necessaryresult = 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();}// Let this thread exit the thread pool if it is no longer// needed and it is not the main process thread.if(result == TIMED_OUT && !isMain) {break;}} while (result != -ECONNREFUSED && result != -EBADF);LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",(void*)pthread_self(), getpid(), (void*)result);mOut.writeInt32(BC_EXIT_LOOPER);talkWithDriver(false);
}status_t IPCThreadState::getAndExecuteCommand()
{status_t result;int32_t cmd;//调用ioctl获取数据result = talkWithDriver();if (result >= NO_ERROR) {size_t IN = mIn.dataAvail();if (IN < sizeof(int32_t)) return result;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;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);// After executing the command, ensure that the thread is returned to the// foreground cgroup before rejoining the pool.  The driver takes care of// restoring the priority, but doesn't do anything with cgroups so we// need to take care of that here in userspace.  Note that we do make// sure to go in the foreground after executing a transaction, but// there are other callbacks into user code that could have changed// our group so we want to make absolutely sure it is put back.set_sched_policy(mMyThreadId, SP_FOREGROUND);}return result;
}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_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;......}if (result != NO_ERROR) {mLastError = result;}return result;
}

ProcessState::self()->startThreadPool(),创建子线程的IPCThreadState,并循序等待数据

void ProcessState::startThreadPool()
{AutoMutex _l(mLock);if (!mThreadPoolStarted) {mThreadPoolStarted = true;spawnPooledThread(true);}
}void ProcessState::spawnPooledThread(bool isMain)
{if (mThreadPoolStarted) {String8 name = makeBinderThreadName();ALOGV("Spawning new pooled thread, name=%s\n", name.string());//创建子线程sp<Thread> t = new PoolThread(isMain);t->run(name.string());}
}class PoolThread : public Thread
{
public:PoolThread(bool isMain): mIsMain(isMain){}protected:virtual bool threadLoop(){//为子线程创建IPCThreadState,并进行循环等待数据IPCThreadState::self()->joinThreadPool(mIsMain);return false;}const bool mIsMain;
};

2.2 分析向servicemanager添加服务的具体过程

对于不同的服务,构造flat_binder_object结构体,里面的binder/cookie对于不同的服务它的值不一样

/* 获得BpServiceManager */
sp<IServiceManager> sm = defaultServiceManager();sm->addService(String16("hello"), new BnHelloService());
virtual status_t addService(const String16& name, const sp<IBinder>& service,bool allowIsolated)
{Parcel data, reply;// 构造数据data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());data.writeString16(name);data.writeStrongBinder(service);// service = BnHelloService对象data.writeInt32(allowIsolated ? 1 : 0);// 发送数据status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);return err == NO_ERROR ? reply.readExceptionCode() : err;
}status_t writeStrongBinder(const sp<T>& val) {return writeStrongBinder(T::asBinder(val)); // val = BnHelloService对象
}status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{return flatten_binder(ProcessState::self(), val, this);// val = BnHelloService对象
}status_t flatten_binder(const sp<ProcessState>& /*proc*/,const sp<IBinder>& binder, Parcel* out)// binder = BnHelloService对象
{//构造flat_binder_objectflat_binder_object obj;obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;if (binder != NULL) {IBinder *local = binder->localBinder();//local 指向 BnHelloService对象if (!local) {BpBinder *proxy = binder->remoteBinder();if (proxy == NULL) {ALOGE("null proxy");}const int32_t handle = proxy ? proxy->handle() : 0;obj.type = BINDER_TYPE_HANDLE;obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */obj.handle = handle;obj.cookie = 0;} else {obj.type = BINDER_TYPE_BINDER;obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());obj.cookie = reinterpret_cast<uintptr_t>(local); // 指向 BnHelloService对象}} else {obj.type = BINDER_TYPE_BINDER;obj.binder = 0;obj.cookie = 0;}return finish_flatten_binder(binder, obj, out);
}BBinder* BBinder::localBinder()
{return this;
}

2.3 分析server如何分辨client想使用哪一个服务,并调用对应的函数

server收到的数据里含有flat_binder_object结构体,它可以根据binder/cookie分析client想使用哪一个服务,把cookie转为BnXXX对象,然后调用它的函数

void IPCThreadState::joinThreadPool(bool isMain)
{LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);// This thread may have been spawned by a thread that was in the background// scheduling group, so first we will make sure it is in the foreground// one to avoid performing an initial transaction in the background.set_sched_policy(mMyThreadId, SP_FOREGROUND);status_t result;do {processPendingDerefs();// now get the next command to be processed, waiting if necessaryresult = 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();}// Let this thread exit the thread pool if it is no longer// needed and it is not the main process thread.if(result == TIMED_OUT && !isMain) {break;}} while (result != -ECONNREFUSED && result != -EBADF);LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",(void*)pthread_self(), getpid(), (void*)result);mOut.writeInt32(BC_EXIT_LOOPER);talkWithDriver(false);
}status_t IPCThreadState::getAndExecuteCommand()
{status_t result;int32_t cmd;//调用ioctl获取数据result = talkWithDriver();if (result >= NO_ERROR) {size_t IN = mIn.dataAvail();if (IN < sizeof(int32_t)) return result;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;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);// After executing the command, ensure that the thread is returned to the// foreground cgroup before rejoining the pool.  The driver takes care of// restoring the priority, but doesn't do anything with cgroups so we// need to take care of that here in userspace.  Note that we do make// sure to go in the foreground after executing a transaction, but// there are other callbacks into user code that could have changed// our group so we want to make absolutely sure it is put back.set_sched_policy(mMyThreadId, SP_FOREGROUND);}return result;
}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_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)) {//reinterpret_cast<BBinder*>(tr.cookie) 通过cookie构造指向BnHelloService对象的BBinder//然后调用BBinder的transact函数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;......}if (result != NO_ERROR) {mLastError = result;}return result;
}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://调用BnHelloService实现的onTransact方法err = onTransact(code, data, reply, flags);break;}if (reply != NULL) {reply->setDataPosition(0);}return err;
}status_t BnHelloService::onTransact( uint32_t code,const Parcel& data,Parcel* reply,uint32_t flags)
{/* 解析数据,调用sayhello/sayhello_to */switch (code) {case HELLO_SVR_CMD_SAYHELLO: {sayhello();reply->writeInt32(0);  /* no exception */return NO_ERROR;} break;case HELLO_SVR_CMD_SAYHELLO_TO: {/* 从data中取出参数 */int32_t policy =  data.readInt32();String16 name16_tmp = data.readString16(); /* IHelloService */String16 name16 = data.readString16();String8 name8(name16);int cnt = sayhello_to(name8.string());/* 把返回值写入reply传回去 */reply->writeInt32(0);  /* no exception */reply->writeInt32(cnt);return NO_ERROR;} break;case HELLO_SVR_CMD_GET_FD: {int fd = this->get_fd();reply->writeInt32(0);  /* no exception *//* 参考:* frameworks\base\core\jni\android_view_InputChannel.cpp* android_view_InputChannel_nativeWriteToParcel*/reply->writeDupFileDescriptor(fd);return NO_ERROR;} break;default:return BBinder::onTransact(code, data, reply, flags);}
}

相关文章:

手写一个C++ Android Binder服务及源码分析

手写一个C Android Binder服务及源码分析 前言一、 基于C语言编写Android Binder跨进程通信Demo总结及改进二、C语言编写自己的Binder服务Demo1. binder服务demo功能介绍2. binder服务demo代码结构图3. binder服务demo代码实现3.1 IHelloService.h代码实现3.2 BnHelloService.c…...

【AIGC】在VSCode中集成 DeepSeek(OPEN AI同理)

在 Visual Studio Code (VSCode) 中集成 AI 编程能力&#xff0c;可以通过安装和配置特定插件来实现。以下是如何通过 Continue 和 Cline 插件集成 DeepSeek&#xff1a; 一、集成 DeepSeek 获取 DeepSeek API 密钥&#xff1a;访问 DeepSeek 官方网站&#xff0c;注册并获取 …...

使用 Three.js 实现热力渐变效果

大家好&#xff01;我是 [数擎 AI]&#xff0c;一位热爱探索新技术的前端开发者&#xff0c;在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情&#xff0c;欢迎关注我的文章&#xff0c;我们一起成长、进步&#xff01; 开发领域&#xff1a;前端开发 | A…...

Vue事件处理 - 绑定事件

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue事件处理 - 绑定事件及事件处理 目录 事件处理 绑定方式 函数表达式 绑定函数名 输入框绑定事件 拿到输入框的值 传值加事件源 事件第三种写法 总结 事件处理 绑定方式 函数表达式 在按钮上使用函数表达式绑定事…...

DVWA靶场通关——SQL Injection篇

一&#xff0c;Low难度下unionget字符串select****注入 1&#xff0c;首先手工注入判断是否存在SQL注入漏洞&#xff0c;输入1 这是正常回显的结果&#xff0c;再键入1’ You have an error in your SQL syntax; check the manual that corresponds to your MySQL server ver…...

DeepSeek 助力 Vue 开发:打造丝滑的步骤条

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…...

今日学习总结

1.完成了P2242公路维修问题 2.完成了P10605下头论文 1.P2242 思考&#xff1a;建立单向链表&#xff0c;使用qsort降序排序。 #include<stdio.h> #include<stdlib.h> #include<stdbool.h> #include<string.h> int n,m; int a[15005],b[15005],ans;…...

Transformer 的辉煌与大模型方向确立,点燃AGI之火把

GPT3&#xff0c;指明大模型发展方向&#xff0c;点燃大模型软件行业繁荣之火&#xff0c;目前大模型有100万个。 DeepSeek-V3&#xff0c;指明下一个阶段大模型发张方向&#xff0c;破壁&#xff1a; 资金壁垒&#xff1a;训练成本降低&#xff0c;适配丰富硬件&#xff0c;总…...

DeepSeek-Coder系列模型:智能编程助手的未来

文章目录 一、模型架构与核心功能1. 模型架构2. 核心功能 二、多语言支持与代码生成1. Python代码生成2. Java代码生成3. C代码生成4. JavaScript代码生成 三、仓库级代码理解1. 代码结构分析2. 上下文理解 四、FIM填充技术1. 函数自动填充2. 代码补全 五、应用场景1. 代码补全…...

微信小程序longpress以及touchend的bug,touchend不触发,touchend不执行

核心原因&#xff1a;bind&#xff1a;touchend里面不能放wx:if 举例&#xff1a; <view bind:longpress"longpressBtn" bind:touchend"touchendBtn"><view wx:if"{{isRecording}}" >松开发送</view><view wx:else"…...

多租户架构设计与实现:基于 PostgreSQL 和 Node.js

多租户架构设计与实现:基于 PostgreSQL 和 Node.js 引言 多租户架构(Multi-tenancy)是现代 SaaS(Software as a Service)应用的核心设计模式之一。它允许多个租户共享同一套应用实例,同时确保数据隔离和安全性。本文将详细介绍多租户架构的设计方案,并基于 PostgreSQL…...

四、OSG学习笔记-基础图元

前一章节&#xff1a; 三、OSG学习笔记-应用基础-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/145514021 代码&#xff1a;CuiQingCheng/OsgStudy - Gitee.com 一、绘制盒子模型 下面一个简单的 demo #include<windows.h> #include<osg/Node&…...

windows平台本地部署DeepSeek大模型+Open WebUI网页界面(可以离线使用)

环境准备: 确定部署方案请参考:DeepSeek-R1系列(1.5b/7b/8b/32b/70b/761b)大模型部署需要什么硬件条件-CSDN博客 根据本人电脑配置:windows11 + i9-13900HX+RTX4060+DDR5 5600 32G内存 确定部署方案:DeepSeek-R1:7b + Ollama + Open WebUI 1. 安装 Ollama Ollama 是一…...

功能架构元模型

功能架构的元模型是对功能架构进行描述和建模的基础框架,它有助于统一不同团队对系统的理解,并为系统的设计和开发提供一致的标准和规范。虽然具体的元模型可能因不同的应用领域和特定需求而有所差异,但一般来说,功能架构的元模型可以涵盖以下几个方面: 组件/模块元模型:…...

云计算——AWS Solutions Architect – Associate(saa)4.安全组和NACL

安全组一充当虚拟防火墙对于关联实例&#xff0c;在实例级别控制入站和出站流量。 网络访问控制列表(NACL)一充当防火墙关联子网&#xff0c;在子网级别控制入站和出站流量。 在专有网络中&#xff0c;安全组和网络ACL(NACL)一起帮助构建分层网络防御。 安全组在实例级别操作…...

Fiddler Classic(HTTP流量代理+半汉化)

目录 一、关于Fiddler (一) Fiddler Classic (二) Fiddler Everywhere (三) Fiddler Everywhere Reporter (四) FiddlerCore (五) 总结 二、 软件安全性 1. 软件安装包 2. 软件汉化dll 三、安装与半汉化 1. 正常打开安装包点击下一步安装即可&#xff0c;安装路径自…...

【hive】记一次hiveserver内存溢出排查,线程池未正确关闭导致

一、使用 MemoryAnalyzer软件打开hprof文件 很大有30G&#xff0c;win内存24GB&#xff0c;不用担心可以打开&#xff0c;ma软件能够生成索引文件&#xff0c;逐块分析内存&#xff0c;如下图。 大约需要4小时。 overview中开不到具体信息。 二、使用Leak Suspects功能继续…...

MySQL的字段类型

MySQL 字段类型可以简单分为三大类 数值类型&#xff1a;整型&#xff08;TINYINT、SMALLINT、MEDIUMINT、INT 和 BIGINT&#xff09;、浮点型&#xff08;FLOAT 和 DOUBLE&#xff09;、定点型&#xff08;DECIMAL&#xff09;字符串类型&#xff1a;CHAR、VARCHAR、TINYTEXT…...

HTML之JavaScript运算符

HTML之JavaScript运算符 1.算术运算符 - * / %除以0&#xff0c;结果为Infinity取余数&#xff0c;如果除数为0&#xff0c;结果为NaN NAN:Not A Number2.复合赋值运算符 - * / %/ 除以0&#xff0c;结果为Infinity% 如果除数为0&#xff0c;结果为NaN NaN:No…...

UE5--浅析委托原理(Delegate)

委托概述 委托是一种用于事件处理的机制。通过使用委托,可以将一个或多个函数绑定到一个事件上,在事件触发时自动调用这些函数。代理也叫做委托,比如:跳,跑,开枪,伤害等响应,就是注册一个委托回调,其作用就是提供一种消息机制,都知道消息的传递需要发送方和接收方,…...

Android13-系统服务大管家-ServiceManager进程-启动篇

文章目录 关注 ServiceMager 原因ServerManager需要掌握的知识资料参考ServiceManager 进程启动启动脚本涉及到的相关源码文件源码跟踪ServiceManager脚本启动位置ServiceManager关联脚本 Native层源码分析main.cpp流程打开驱动 initWithDriverinitmakeProcessState 构造方法op…...

网络安全溯源 思路 网络安全原理

网络安全背景 网络就是实现不同主机之间的通讯。网络出现之初利用TCP/IP协议簇的相关协议概念&#xff0c;已经满足了互连两台主机之间可以进行通讯的目的&#xff0c;虽然看似简简单单几句话&#xff0c;就描述了网络概念与网络出现的目的&#xff0c;但是为了真正实现两台主机…...

Mac(m1)本地部署deepseek-R1模型

1. 下载安装ollama 直接下载软件&#xff0c;下载完成之后&#xff0c;安装即可&#xff0c;安装完成之后&#xff0c;命令行中可出现ollama命令 2. 在ollama官网查看需要下载的模型下载命令 1. 在官网查看deepseek对应的模型 2. 选择使用电脑配置的模型 3. copy 对应模型的安…...

从零复现DeepSeek R1:从V3中对MoE、MLA、MTP的实现,到Open R1对R1中SFT、GRPO的实现

前言 虽然我司从23年起&#xff0c;便逐步从教育为主转型到了科技为主&#xff0c;但不代表教育业务便没有了 随着DeepSeek特别是R1、其次V3模型的大火&#xff0c;我司七月在线的大模型线上营群里一学员朋友DIFY问道&#xff1a;校长好&#xff0c;deepseek 的课程目前有多少…...

[EAI-033] SFT 记忆,RL 泛化,LLM和VLM的消融研究

Paper Card 论文标题&#xff1a;SFT Memorizes, RL Generalizes: A Comparative Study of Foundation Model Post-training 论文作者&#xff1a;Tianzhe Chu, Yuexiang Zhai, Jihan Yang, Shengbang Tong, Saining Xie, Dale Schuurmans, Quoc V. Le, Sergey Levine, Yi Ma 论…...

示例代码:C# MQTTS双向认证(客户端)(服务器EMQX)

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…...

自制游戏——斗罗大陆

很简陋&#xff0c;没有图&#xff0c;请见谅 // mine[0] 级数 // mine[1] 战力 //mine[2] 1 白虎 //mine[2] 2 昊天锤 //mine[2] 3 蓝银草 #include <bits/stdc.h> using namespace std; int mine[100],live3, dou 1, luo 1, da 1, bag[1000], huan 0, lia…...

数据治理双证通关经验分享 | CDGA/CDGP备考全指南

历经1个月多的系统准备&#xff0c;本人于2024年顺利通过DAMA China的CDGA&#xff08;数据治理工程师&#xff09;和CDGP&#xff08;数据治理专家&#xff09;双认证。现将备考经验与资源体系化整理&#xff0c;助力从业者高效通关。 &#x1f31f; 认证价值与政策背景 根据…...

Vue全流程--Vue3.0与Vue2.0响应式原理对比

Vue2中数据的响应式 需要使用Vue.set这么一个api&#xff0c;修改数据 需要使用Vue.delete这么一个api&#xff0c;删除数据 数据代理这个当面的理解可以看看我前面文章Vue全流程--数据代理的理解以及在Vue中的应用-CSDN博客 Vue3中数据的响应式 Vue3使用proxy这个api实现…...

Spring中都应用了哪些设计模式?

好的&#xff01;以下是您提到的八种设计模式在 Spring 中的简单示例&#xff1a; 1. 简单工厂模式 简单工厂模式通过传入参数来决定实例化哪个类。Spring 中的 BeanFactory 就是简单工厂模式的应用。 示例代码&#xff1a; // 1. 创建接口和具体实现类 public interface A…...

fastjson2学习大纲

一、基础篇 - JSON与fastjson2核心概念 JSON基础 JSON语法规范&#xff08;RFC 8259&#xff09;JSON数据类型与Java类型对应关系序列化/反序列化核心概念 fastjson2入门 与fastjson1的主要区别核心优势&#xff1a; 性能提升&#xff08;JSONB二进制协议&#xff09;更完善的…...

k8s部署elasticsearch

前置环境:已部署k8s集群,ip地址为 192.168.10.1~192.168.10.5,总共5台机器。 1. 创建provisioner制备器(如果已存在,则不需要) 制备器的具体部署方式,参考我之前的文章:k8s部署rabbitmq-CSDN博客 2. 编写wms-elk-data-sc.yaml配置文件 apiVersion: storage.k8s.io/…...

BS架构(笔记整理)

楔子.基本概念 1.在网络架构中&#xff1a; 服务器通常是集中式计算资源&#xff0c;负责处理和存储数据&#xff1b;客户机是请求这些服务的终端设备&#xff0c;可能是个人电脑或移动设备&#xff1b;浏览器则是客户机上用来与服务器交互的工具&#xff0c;负责展示网页内容…...

从基础到人脸识别与目标检测

前言 从本文开始&#xff0c;我们将开始学习ROS机器视觉处理&#xff0c;刚开始先学习一部分外围的知识&#xff0c;为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本&#xff0c;系统采用Ubuntu20.04&#xff0c;ROS采用noetic。 颜…...

ArcGIS Pro SDK (二十七)自定义许可

ArcGIS Pro SDK (二十七)自定义许可 环境:Visual Studio 2022 + .NET6 + ArcGIS Pro SDK 3.0 文章目录 ArcGIS Pro SDK (二十七)自定义许可1 在Config.xaml中添加扩展配置2 在Module1.cs中实现接口IExtensionConfig1 在Config.xaml中添加扩展配置 <modules><inse…...

一、kubernetes k8s

k8s概述: k8s的全称:kubernetes k8s k8s的版本:1.30 1.20------------用的最多的版本&#xff0c;1.18-1.21 1.24------------>k8s的镜像不再使用docker&#xff0c;containerd k8s的作用&#xff1a; 用于自动部署&#xff0c;自动扩展和管理“容器化应…...

C#、.Net 中级高级架构管理面试题杂烩

1、简述值类型和引用类型的区别 存储位置&#xff1a;值类型变量直接存储数据的值&#xff0c;通常存储在栈上&#xff1b;引用类型变量存储的是对象在堆上的引用地址。 内存管理&#xff1a;值类型的内存由系统自动管理&#xff0c;当超出作用域时自动释放&#xff1b;引用类…...

ArrayList和LinkedList有什么区别?在什么情况下使用ArrayList更高效?

ArrayList和LinkedList在Java中是两种常用的数据结构&#xff0c;分别基于数组和链表实现。它们在性能、内存使用和适用场景上各有特点。 ArrayList与LinkedList的主要区别 数据结构&#xff1a; ArrayList&#xff1a;基于动态数组实现&#xff0c;元素存储在连续的内存空间…...

KITE提示词框架:引导大语言模型的高效新工具

大语言模型的应用日益广泛。然而&#xff0c;如何确保这些模型生成的内容在AI原生应用中符合预期&#xff0c;仍是一个需要不断探索的问题。以下内容来自于《AI 原生应用开发&#xff1a;提示工程原理与实战》一书&#xff08;京东图书&#xff1a;https://item.jd.com/1013604…...

Spring 整合 MyBatis:核心知识点详解

一、Spring 整合 MyBatis 的优势 依赖注入&#xff1a;Spring 的 IOC 容器可以管理 MyBatis 的组件&#xff08;如 SqlSessionFactory、Mapper 接口等&#xff09;&#xff0c;减少手动创建对象的繁琐。 事务管理&#xff1a;Spring 提供了声明式事务管理&#xff0c;可以轻松…...

centos docker安装

一、前置条件 安装gcc和c: yum -y install gcc yum -y install gcc-c 二、卸载旧版本 如果之前安装过Docker&#xff0c;需要先卸载旧版本&#xff1a; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logr…...

推荐一款 免费的SSL,自动续期

支持自动续期 、泛域名 、可视化所有证书时效性 、可配置CDN 的一款工具。免费5个泛域名和1个自动更新。 链接 支持&#xff1a;nginx、通配符证书、七牛云、腾讯云、阿里云、CDN、OSS、LB&#xff08;负载均衡&#xff09; 执行自动部署脚本 提示系统过缺少crontab 安装cro…...

python-leetcode 24.回文链表

题目&#xff1a; 给定单链表的头节点head,判断该链表是否为回文链表&#xff0c;如果是&#xff0c;返回True,否则&#xff0c;返回False 输入&#xff1a;head[1,2,2,1] 输出&#xff1a;true 方法一&#xff1a;将值复制到数组中后用双指针法 有两种常用的列表实现&#…...

利用kali linux 进行自动化渗透测试

本方案旨在自动化创建渗透测试全流程 一、架构 1.智能信息收集体系 class IntelligentOSINT:def __init__(self, target):self.target targetself.intelligence_sources [OSINT_Platforms,DeepWeb_Crawlers, SocialMedia_Trackers,ML_Correlation_Engine]def advanced_col…...

蓝桥杯试题:冒泡排序 选择排序

一、问题描述 在一个神秘的岛屿上&#xff0c;有一支探险队发现了一批宝藏&#xff0c;这批宝藏是以整数数组的形式存在的。每个宝藏上都标有一个数字&#xff0c;代表了其珍贵程度。然而&#xff0c;由于某种神奇的力量&#xff0c;这批宝藏的顺序被打乱了&#xff0c;探险队…...

curl与telnet的区别

协议支持&#xff1a;curl支持多种协议&#xff0c;如HTTP、HTTPS、FTP等&#xff0c;而telnet主要用于基于TCP协议的连接。 功能&#xff1a;curl是一个功能强大的工具&#xff0c;可以用来发送各种HTTP请求、下载文件等&#xff0c;而telnet主要用于在远程服务器上进行简单的…...

防火墙综合练习2

准备阶段 实验拓扑图如下&#xff1a; 试验要求如下&#xff1a; 需求一&#xff1a;完成相关配置 需求二&#xff1a;配置DHCP协议 需求三&#xff1a;防火墙安全区域配置 需求四&#xff1a;防火墙地址组信息 需求五&#xff1a;管理员 需求六&#xff1a;用户认证…...

leetcode_26删除有序数组中的重复项

1. 题意 给定一个重复数组&#xff0c;删除其中的重复项目。 2. 题解 双指针 一个指针指向有序不重复数组的最后一个数&#xff0c;另外一个数遍历整个数组&#xff0c;若两个指针对应用的数不相同&#xff0c;有序数组的指针右移&#xff0c;将数填入。 代码一 class Sol…...

SQLServer的创建,表创建,主键,约束,模糊查询

设置 注意: 设置完成之后 重新启动 创建数据库 注意: 这个目标路径必须要有该文件名的文件夹 -- 指向 master 数据库&#xff0c;告诉它我们要创建一个新的数据库操作了 use master go-- 创建数据库 create database StudentManageDB on primary (-- 以下四个组成部分缺一不可…...

钉钉位置偏移解决,钉钉虚拟定位打卡

虚拟定位打卡工具 一&#xff0c;介绍免费获取工具 一&#xff0c;介绍 提到上班打卡&#xff0c;职场人的内心戏估计能拍成一部连续剧。打卡&#xff0c;这俩字仿佛自带“紧箍咒”&#xff0c;让无数打工人又爱又恨。想象一下&#xff0c;你气喘吁吁地冲进办公室&#xff0c;…...