手写一个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【三】
阅读建议:
- 如果有余力建议去阅读下我之前写的三篇【深入内核讲明白Android Binder】系列的文章,这三篇文章深入linux内核解析android binder源码,如果搞清楚了这三篇文章,再来看这篇使用C++应用Android binder的文章就非常容易理解。
- 如果直接看这篇文章也是可以的,但这篇文章只能使您停留在会使用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服务提供者(服务端),核心步骤:
- 打开驱动
- 获取servicemanager
- 向servicemanager中添加服务
- 死循环等待客户端发来数据,并进行处理和回复
/* 参考: 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服务使用者(客户端),实现核心步骤:
- 打开驱动
- 获得servicemanager
- 从servicemanager获得服务
- 调用服务的函数
#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对象的获取过程有两步:
- 通过BpServiceManager的getService方法获取指向hello服务的BpBinder对象。
- 将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 代理类如何发送数据?
代理类通过如下两个步骤发送数据
- 构造数据
- 调用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
- 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;
};
- 那么接着分析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服务有以下几个步骤:
- 打开驱动
- 注册服务
- 死循环等待数据
其中,打开驱动是通过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对象,即该对象是线程特有的,各自不同,那么它应该存在线程的局部空间里。
那么如何实现呢?
- 创建键:k = pthread_key_create
(k,v)- 为键设置值:每个线程为k设置不一样的value
如对于线程1可以调用pthread_setspecific(k, v1),设置value为v1,- 后续可以在不同线程中获得不同的值
如在线程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 编程能力,可以通过安装和配置特定插件来实现。以下是如何通过 Continue 和 Cline 插件集成 DeepSeek: 一、集成 DeepSeek 获取 DeepSeek API 密钥:访问 DeepSeek 官方网站,注册并获取 …...
使用 Three.js 实现热力渐变效果
大家好!我是 [数擎 AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步! 开发领域:前端开发 | A…...
Vue事件处理 - 绑定事件
Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue事件处理 - 绑定事件及事件处理 目录 事件处理 绑定方式 函数表达式 绑定函数名 输入框绑定事件 拿到输入框的值 传值加事件源 事件第三种写法 总结 事件处理 绑定方式 函数表达式 在按钮上使用函数表达式绑定事…...
DVWA靶场通关——SQL Injection篇
一,Low难度下unionget字符串select****注入 1,首先手工注入判断是否存在SQL注入漏洞,输入1 这是正常回显的结果,再键入1’ You have an error in your SQL syntax; check the manual that corresponds to your MySQL server ver…...
DeepSeek 助力 Vue 开发:打造丝滑的步骤条
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
今日学习总结
1.完成了P2242公路维修问题 2.完成了P10605下头论文 1.P2242 思考:建立单向链表,使用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,指明大模型发展方向,点燃大模型软件行业繁荣之火,目前大模型有100万个。 DeepSeek-V3,指明下一个阶段大模型发张方向,破壁: 资金壁垒:训练成本降低,适配丰富硬件,总…...
DeepSeek-Coder系列模型:智能编程助手的未来
文章目录 一、模型架构与核心功能1. 模型架构2. 核心功能 二、多语言支持与代码生成1. Python代码生成2. Java代码生成3. C代码生成4. JavaScript代码生成 三、仓库级代码理解1. 代码结构分析2. 上下文理解 四、FIM填充技术1. 函数自动填充2. 代码补全 五、应用场景1. 代码补全…...
微信小程序longpress以及touchend的bug,touchend不触发,touchend不执行
核心原因:bind:touchend里面不能放wx:if 举例: <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学习笔记-基础图元
前一章节: 三、OSG学习笔记-应用基础-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/145514021 代码: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
安全组一充当虚拟防火墙对于关联实例,在实例级别控制入站和出站流量。 网络访问控制列表(NACL)一充当防火墙关联子网,在子网级别控制入站和出站流量。 在专有网络中,安全组和网络ACL(NACL)一起帮助构建分层网络防御。 安全组在实例级别操作…...
Fiddler Classic(HTTP流量代理+半汉化)
目录 一、关于Fiddler (一) Fiddler Classic (二) Fiddler Everywhere (三) Fiddler Everywhere Reporter (四) FiddlerCore (五) 总结 二、 软件安全性 1. 软件安装包 2. 软件汉化dll 三、安装与半汉化 1. 正常打开安装包点击下一步安装即可,安装路径自…...
【hive】记一次hiveserver内存溢出排查,线程池未正确关闭导致
一、使用 MemoryAnalyzer软件打开hprof文件 很大有30G,win内存24GB,不用担心可以打开,ma软件能够生成索引文件,逐块分析内存,如下图。 大约需要4小时。 overview中开不到具体信息。 二、使用Leak Suspects功能继续…...
MySQL的字段类型
MySQL 字段类型可以简单分为三大类 数值类型:整型(TINYINT、SMALLINT、MEDIUMINT、INT 和 BIGINT)、浮点型(FLOAT 和 DOUBLE)、定点型(DECIMAL)字符串类型:CHAR、VARCHAR、TINYTEXT…...
HTML之JavaScript运算符
HTML之JavaScript运算符 1.算术运算符 - * / %除以0,结果为Infinity取余数,如果除数为0,结果为NaN NAN:Not A Number2.复合赋值运算符 - * / %/ 除以0,结果为Infinity% 如果除数为0,结果为NaN NaN:No…...
UE5--浅析委托原理(Delegate)
委托概述 委托是一种用于事件处理的机制。通过使用委托,可以将一个或多个函数绑定到一个事件上,在事件触发时自动调用这些函数。代理也叫做委托,比如:跳,跑,开枪,伤害等响应,就是注册一个委托回调,其作用就是提供一种消息机制,都知道消息的传递需要发送方和接收方,…...
Android13-系统服务大管家-ServiceManager进程-启动篇
文章目录 关注 ServiceMager 原因ServerManager需要掌握的知识资料参考ServiceManager 进程启动启动脚本涉及到的相关源码文件源码跟踪ServiceManager脚本启动位置ServiceManager关联脚本 Native层源码分析main.cpp流程打开驱动 initWithDriverinitmakeProcessState 构造方法op…...
网络安全溯源 思路 网络安全原理
网络安全背景 网络就是实现不同主机之间的通讯。网络出现之初利用TCP/IP协议簇的相关协议概念,已经满足了互连两台主机之间可以进行通讯的目的,虽然看似简简单单几句话,就描述了网络概念与网络出现的目的,但是为了真正实现两台主机…...
Mac(m1)本地部署deepseek-R1模型
1. 下载安装ollama 直接下载软件,下载完成之后,安装即可,安装完成之后,命令行中可出现ollama命令 2. 在ollama官网查看需要下载的模型下载命令 1. 在官网查看deepseek对应的模型 2. 选择使用电脑配置的模型 3. copy 对应模型的安…...
从零复现DeepSeek R1:从V3中对MoE、MLA、MTP的实现,到Open R1对R1中SFT、GRPO的实现
前言 虽然我司从23年起,便逐步从教育为主转型到了科技为主,但不代表教育业务便没有了 随着DeepSeek特别是R1、其次V3模型的大火,我司七月在线的大模型线上营群里一学员朋友DIFY问道:校长好,deepseek 的课程目前有多少…...
[EAI-033] SFT 记忆,RL 泛化,LLM和VLM的消融研究
Paper Card 论文标题:SFT Memorizes, RL Generalizes: A Comparative Study of Foundation Model Post-training 论文作者:Tianzhe Chu, Yuexiang Zhai, Jihan Yang, Shengbang Tong, Saining Xie, Dale Schuurmans, Quoc V. Le, Sergey Levine, Yi Ma 论…...
示例代码:C# MQTTS双向认证(客户端)(服务器EMQX)
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...
自制游戏——斗罗大陆
很简陋,没有图,请见谅 // 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个月多的系统准备,本人于2024年顺利通过DAMA China的CDGA(数据治理工程师)和CDGP(数据治理专家)双认证。现将备考经验与资源体系化整理,助力从业者高效通关。 🌟 认证价值与政策背景 根据…...
Vue全流程--Vue3.0与Vue2.0响应式原理对比
Vue2中数据的响应式 需要使用Vue.set这么一个api,修改数据 需要使用Vue.delete这么一个api,删除数据 数据代理这个当面的理解可以看看我前面文章Vue全流程--数据代理的理解以及在Vue中的应用-CSDN博客 Vue3中数据的响应式 Vue3使用proxy这个api实现…...
Spring中都应用了哪些设计模式?
好的!以下是您提到的八种设计模式在 Spring 中的简单示例: 1. 简单工厂模式 简单工厂模式通过传入参数来决定实例化哪个类。Spring 中的 BeanFactory 就是简单工厂模式的应用。 示例代码: // 1. 创建接口和具体实现类 public interface A…...
fastjson2学习大纲
一、基础篇 - JSON与fastjson2核心概念 JSON基础 JSON语法规范(RFC 8259)JSON数据类型与Java类型对应关系序列化/反序列化核心概念 fastjson2入门 与fastjson1的主要区别核心优势: 性能提升(JSONB二进制协议)更完善的…...
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.在网络架构中: 服务器通常是集中式计算资源,负责处理和存储数据;客户机是请求这些服务的终端设备,可能是个人电脑或移动设备;浏览器则是客户机上用来与服务器交互的工具,负责展示网页内容…...
从基础到人脸识别与目标检测
前言 从本文开始,我们将开始学习ROS机器视觉处理,刚开始先学习一部分外围的知识,为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本,系统采用Ubuntu20.04,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------------用的最多的版本,1.18-1.21 1.24------------>k8s的镜像不再使用docker,containerd k8s的作用: 用于自动部署,自动扩展和管理“容器化应…...
C#、.Net 中级高级架构管理面试题杂烩
1、简述值类型和引用类型的区别 存储位置:值类型变量直接存储数据的值,通常存储在栈上;引用类型变量存储的是对象在堆上的引用地址。 内存管理:值类型的内存由系统自动管理,当超出作用域时自动释放;引用类…...
ArrayList和LinkedList有什么区别?在什么情况下使用ArrayList更高效?
ArrayList和LinkedList在Java中是两种常用的数据结构,分别基于数组和链表实现。它们在性能、内存使用和适用场景上各有特点。 ArrayList与LinkedList的主要区别 数据结构: ArrayList:基于动态数组实现,元素存储在连续的内存空间…...
KITE提示词框架:引导大语言模型的高效新工具
大语言模型的应用日益广泛。然而,如何确保这些模型生成的内容在AI原生应用中符合预期,仍是一个需要不断探索的问题。以下内容来自于《AI 原生应用开发:提示工程原理与实战》一书(京东图书:https://item.jd.com/1013604…...
Spring 整合 MyBatis:核心知识点详解
一、Spring 整合 MyBatis 的优势 依赖注入:Spring 的 IOC 容器可以管理 MyBatis 的组件(如 SqlSessionFactory、Mapper 接口等),减少手动创建对象的繁琐。 事务管理:Spring 提供了声明式事务管理,可以轻松…...
centos docker安装
一、前置条件 安装gcc和c: yum -y install gcc yum -y install gcc-c 二、卸载旧版本 如果之前安装过Docker,需要先卸载旧版本: sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logr…...
推荐一款 免费的SSL,自动续期
支持自动续期 、泛域名 、可视化所有证书时效性 、可配置CDN 的一款工具。免费5个泛域名和1个自动更新。 链接 支持:nginx、通配符证书、七牛云、腾讯云、阿里云、CDN、OSS、LB(负载均衡) 执行自动部署脚本 提示系统过缺少crontab 安装cro…...
python-leetcode 24.回文链表
题目: 给定单链表的头节点head,判断该链表是否为回文链表,如果是,返回True,否则,返回False 输入:head[1,2,2,1] 输出:true 方法一:将值复制到数组中后用双指针法 有两种常用的列表实现&#…...
利用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…...
蓝桥杯试题:冒泡排序 选择排序
一、问题描述 在一个神秘的岛屿上,有一支探险队发现了一批宝藏,这批宝藏是以整数数组的形式存在的。每个宝藏上都标有一个数字,代表了其珍贵程度。然而,由于某种神奇的力量,这批宝藏的顺序被打乱了,探险队…...
curl与telnet的区别
协议支持:curl支持多种协议,如HTTP、HTTPS、FTP等,而telnet主要用于基于TCP协议的连接。 功能:curl是一个功能强大的工具,可以用来发送各种HTTP请求、下载文件等,而telnet主要用于在远程服务器上进行简单的…...
防火墙综合练习2
准备阶段 实验拓扑图如下: 试验要求如下: 需求一:完成相关配置 需求二:配置DHCP协议 需求三:防火墙安全区域配置 需求四:防火墙地址组信息 需求五:管理员 需求六:用户认证…...
leetcode_26删除有序数组中的重复项
1. 题意 给定一个重复数组,删除其中的重复项目。 2. 题解 双指针 一个指针指向有序不重复数组的最后一个数,另外一个数遍历整个数组,若两个指针对应用的数不相同,有序数组的指针右移,将数填入。 代码一 class Sol…...
SQLServer的创建,表创建,主键,约束,模糊查询
设置 注意: 设置完成之后 重新启动 创建数据库 注意: 这个目标路径必须要有该文件名的文件夹 -- 指向 master 数据库,告诉它我们要创建一个新的数据库操作了 use master go-- 创建数据库 create database StudentManageDB on primary (-- 以下四个组成部分缺一不可…...
钉钉位置偏移解决,钉钉虚拟定位打卡
虚拟定位打卡工具 一,介绍免费获取工具 一,介绍 提到上班打卡,职场人的内心戏估计能拍成一部连续剧。打卡,这俩字仿佛自带“紧箍咒”,让无数打工人又爱又恨。想象一下,你气喘吁吁地冲进办公室,…...