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

教学平台网站建设合同网站建设的招标文件

教学平台网站建设合同,网站建设的招标文件,显示网站建设中,付费阅读wordpress主题我们知道#xff0c;同一个程序中的两个函数之间能直接调用的根本原因是处于相同的内存空间中。 比如有以下两个函数A和B#xff1a; /*Simple.c*/ void A() { B(); } void B() { }因为是在一个内存空间中#xff0c;虚拟地址的映射规则完全一致#xff0c;所以函数A和B之…我们知道同一个程序中的两个函数之间能直接调用的根本原因是处于相同的内存空间中。 比如有以下两个函数A和B /*Simple.c*/ void A() { B(); } void B() { }因为是在一个内存空间中虚拟地址的映射规则完全一致所以函数A和B之间的调用关系很简单如图所示。 两个不同的进程如某Application1和ActivityManagerService所在的进程它们是没有办法直接通过内存地址来访问到对方内部的函数或者变量的如图所示。 既然无法“直接”访问到对方进程的内存空间那有没有“间接”的方法呢简而言之这就是Binder所要做的工作如图6-3所示。 Binder是Android中使用最广泛的IPC机制。Binder包括 Binder驱动 Service Manager Binder Client Binder Server。 如果统观Binder中的各个组成元素就会惊奇地发现它和TCP/IP网络有很多相似之处 Binder驱动→路由器 Service Manager→DNS Binder Client→客户端 Binder Server→服务器。 TCP/IP中一个典型的服务连接过程比如客户端通过浏览器访问Google主页如图6-4所示。 Client向DNS查询Google.com的IP地址。DNS将查询结果返回Client。Client发起连接。Client在得到Google.com的IP地址后就可以据此来向Google服务器发起连接了。Router是整个通信结构中的基础它所担负的责任是将数据包投递到用户设定的目标IP中。 首先在TCP/IP参考模型中对于IP层及以上的用户来说IP地址是他们彼此沟通的凭证——任何用户在整个互联网中的IP标志符都是唯一的。 其次Router是构建一个通信网络的基础它可以根据用户填写的目标IP正确地把数据包发送到位。 最后DNS角色并不是必需的它的出现是为了帮助人们使复杂难记的IP地址与可读性更强的域名建立关联并提供查询功能。而客户端能使用DNS的前提是它已经正确配置了DNS服务器的IP地址。 Binder的原型如图6-5所示 Binder的本质目标用一句话来描述就是进程1客户端希望与进程2服务器进行互访。但因为它们之间是跨进程跨网络的所以必须借助于Binder驱动路由器来把请求正确投递到对方所在进程网络中。而参与通信的进程们需要持有Binder“颁发”的唯一标志IP地址如图6-6所示。 和TCP/IP网络类似Binder中的“DNS”也并不是必需的——前提是客户端能记住它要访问的进程的Binder标志IP地址而且要特别注意这个标志是“动态IP”这就意味着即使客户端记住了本次通信过程中目标进程的唯一标志下一次访问仍然需要重新获取这无疑加大了客户端的难度。“DNS”的出现可以完美地解决这个问题用于管理Binder标志与可读性更强的“域名”间的对应关系并向用户提供查询功能。 在Binder机制中DNS的角色就是Service Manager 既然Service Manager是DNS那么它的“IP地址”是什么呢Binder机制对此做了特别规定。Service Manager在Binder通信过程中的唯一标志永远都是0。 智能指针 智能指针在整个Android工程中使用很广泛特别是在Binder的源码实现中更可谓“比比皆是”。 1.1智能指针的设计理念 Java和C/C的一个重大区别就是它没有“指针”的概念。这并不代表Java不需要使用指针而是这个“超级武器”被隐藏了起来。 C/C项目中常见的指针问题可以归纳为 指针没有初始化new了对象后没有及时delete野指针 Android中的智能指针实现包括强指针和弱指针两种 1.2 强指针sp frameworks/native/include/utils/StrongPointer.h template typename T class sp { public:inline sp() : m_ptr(0) { }sp(T* other);/*常用构造函数*/…/*其他构造函数*/sp();/*析构函数*/sp operator (T* other);// 重载运算符“”…inline T operator* () const { return *m_ptr; }// 重载运算符“*”inline T* operator- () const { return m_ptr; }// 重载运算符“-”inline T* get() const { return m_ptr; }… private:templatetypename Y friend class sp;templatetypename Y friend class wp;void set_pointer(T* ptr);T* m_ptr; };运算符等号的实现为 templatetypename T spT spT::operator (T* other) {if (other) other-incStrong(this);/*增加引用计数*/if (m_ptr) m_ptr-decStrong(this);m_ptr other;return *this; }析构函数 templatetypename T spT::sp() {if (m_ptr) m_ptr-decStrong(this);/*减小引用计数*/ }1.3 弱指针wp 有个场景父对象parent指向子对象child然 后子对象又指向父对象这就存在了循环引用的现象。 struct CDad { CChild *myChild; }; struct CChild {CDad *myDad; };假设这两个类都具有引用计数器的功能。 因为CDad指向了CChild所以后者的引用计数器不为零。 而CChild又指向了CDad同样也会使它的计数器不为零。 内存回收者发现两者都是处于“被需要”的状态当然不能释放从而形成了恶性循环。 解决这个矛盾的一种有效方法是采用“弱引用”。具体措施如下。 CDad使用强指针来引用CChild而CChild只使用弱引用来指向父类。双方规定当强引用计数为0时不论弱引用是否为0都可以delete自己在Android系统中这个规则是可以调整的。这样只要有一方得到了释放就可以成功避免死锁。会不会导致野指针的问题没错的确会有这方面的顾虑。比如CDad因为强指针计数已经到0根据规则生命周期就结束了但此时CChild还持有其父类的弱引用显然如果CChild此时用这个指针来访问CDad将引发致命的问题。鉴于此我们还要特别规定 弱指针必须先升级为强指针才能访问它所指向的目标对象。 template typename T class wp { public:typedef typename RefBase::weakref_type weakref_type;inline wp() : m_ptr(0) { }wp(T* other);//构造函数…/*其他构造函数省略*/wp(); wp operator (T* other);//运算符重载…void set_object_and_refs(T* other, weakref_type* refs);spT promote() const;/*升级为强指针*/…/*其他方法省略*/ private:templatetypename Y friend class sp;templatetypename Y friend class wp;T* m_ptr;weakref_type* m_refs; };总结 1.智能指针分为强指针sp和弱指针wp两种。 2.通常情况下目标对象的父类是RefBase——这个基类提供了一个weakref_impl类型的引用计数器可以同时进行强弱引用的控制内部由mStrong和mWeak提供计数。 3.当incStrong增加强引用的也会增加弱引用。 4.当incWeak时只增加弱引用计数。 5.使用者可以通过extendObjectLifetime设置引用计数器的规则不同规则下对删除目标对象的时机判断也是不一样的。 6.使用者可以根据程序需求来选择合适的智能指针类型和计数器规则 进程间的数据传递载体——Parcel 进程间的数据传递如果只是一个int型数值不断复制直到目标进程即可。但如果是某个对象呢我们可以想象下同一进程间的对象传递都是通过引用来做的因而本质上就是传递了一个内存地址。这种方式在跨进程的情况下就无能为力了。由于采用了虚拟内存机制两个进程都有自己独立的内存地址空间所以跨进程传递的地址值是无效的。 进程间的数据传递是Binder机制中的重要一环Android系统中担负这一重任的就是Parcel。Parcel是一种数据的载体用于承载希望通过IBinder发送的相关信息包括数据和对象引用。如果把对象在进程A中占据的内存相关数据打包起来然后寄送到进程B中由B在自己的进程空间中“复现”这个对象是否可行呢Parcel就具备这种打包和重组的能力。 1Parcel设置相关 存入的数据越多Parcel所占内存空间也就越大。我们可以通过以下方法来进行相关设置。 dataSize()获取当前已经存储的数据大小。 setDataCapacity (int size)设置Parcel的空间大小显然存储的数据不能大于这个值。 setDataPosition (int pos)改变Parcel中的读写位置必须介于0和dataSize()间。 dataAvail()当前Parcel中的可读数据大小。 dataCapacity()当前Parcel的存储能力。 dataPosition()数据的当前位置值有点类似于游标。 dataSize()当前Parcel所包含的数据大小。 2Primitives 原始类型数据的读写操作。比如 writeByte(byte)写入一个byte。 readByte()读取一个byte。 writeDouble(double)写入一个double。 readDouble()读取一个double。 3Primitive Arrays 原始数据类型数组的读写操作通常是先写入用4个字节表示的数据大小值接着才写入数据本身。另外用户既可以选择将数据读入现有的数组空间中也可以让Parcel返回一个新的数组。此类方法如下 writeBooleanArray(boolean[])写入布尔数组。 readBooleanArray(boolean[])读取布尔数组。 boolean[]createBooleanArray()读取并返回一个布尔数组。 writeByteArray(byte[])写入字节数组。 writeByteArray(byte[], int, int)和上面几个不同的是这个函数最后面的两个参数分别表示数组中需要被写入的数据起点以及需要写入多少。 readByteArray(byte[])读取字节数组。 byte[]createByteArray()读取并返回一个数组。 如果写入数据时系统发现已经超出了Parcel的存储能力它会自动申请所需的内存空间并扩展dataCapacity而且每次写入都是从dataPosition()开始的。 4Parcelables 遵循Parcelable协议的对象可以通过Parcel来存取如开发人员经常用到的bundle就是继承自Parcelable的。与这类对象相关的Parcel操作包括 writeParcelable(Parcelable, int)将这个Parcelable类的名字和内容写入Parcel中实际上它是通过回调此Parcelable的writeToParcel()方法来写入数据的。 readParcelable(ClassLoader)读取并返回一个新的Parcelable对象。 writeParcelableArray(T[], int)写入Parcelable对象数组。 readParcelableArray(ClassLoader)读取并返回一个Parcelable对象数组 5Bundles 上面已提到Bundle继承自Parcelable是一种特殊的type-safe的容器。Bundle的最大特点就是采用键值对的方式存储数据并在一定程度上优化了读取效率。这个类型的Parcel操作包括 writeBundel(Bundle)将Bundle写入parcel。 readBundle()读取并返回一个新的Bundle对象。 readBundle(ClassLoader)读取并返回一个新的Bundle对象ClassLoader用于Bundle获取对应的Parcelable对象。 6Active Objects Parcel的另一个强大武器就是可以读写Active Objects。什么是Active Objects呢通常我们存入Parcel的是对象的内容而Active Objects写入的则是它们的特殊标志引用。所以在从Parcel中读取这些对象时大家看到的并不是重新创建的对象实例而是原来那个被写入的实例。可以猜想到能够以这种方式传输的对象不会很多目前主要有两类。 1Binder。Binder一方面是Android系统IPC通信的核心机制之一另一方面也是一个对象。利用Parcel将Binder对象写入读取时就能得到原始的Binder对象或者是它的特殊代理实现最终操作的 还是原始Binder对象。与此相关的操作包括 writeStrongBinder(IBinder) writeStrongInterface(IInterface) readStrongBinder() … 2FileDescriptor。FileDescriptor是Linux 中的文件描述符可以通过Parcel的如下方法进行传递。 writeFileDescriptor(FileDescriptor), readFileDescriptor() 因为传递后的对象仍然会基于和原对象相同的文件流进行操作因而可以认为是Active Object的一种。 7Untyped Containers 它是用于读写标准的任意类型的java容器。包括 writeArray(Object[]), readArray(ClassLoader), writeList(List),readList(List, ClassLoader)等 Parcel所支持的类型很多足以满足开发者的数据传递请求。如果要给Parcel找个类比的话它更像集装箱。理由如下 1.货物无关性 即它并不排斥所运输的货物种类电子产品可以汽车也行或者零部件也同样接受。 2.不同货物需要不同的打包和卸货方案 比如运载易碎物品和坚硬物品的装箱卸货方式就肯定会有很大不同。 3.远程运输和组装 集装箱的货物通常是要跨洋运输的有点类似于Parcel的跨进程能力。不过集装箱运输公司本身并不负责所运送货物的后期组装。举个例子汽车厂商需要把一辆整车先拆卸成零部件后才能进行装货运输到达目的地后货运公司只需要把货物完整无缺地交由接收方即可并不负有组装成整车的义务。而Parcel则更加敬业它会依据协议打包和重组所用的协议必须是配套的来为接收方提供完整还原 出原始数据对象的业务。 writeString的实现原理 结合一个范例来详细讲解writeString的实现原理范例是ServiceManagerProxy的getService()方法中对Parcel的操作 Parcel data Parcel.obtain(); … data.writeInterfaceToken(IServiceManager.descriptor); data.writeString(name);第一句代码用于获得一个Parcel对象最终是创建了一个本地的Parcel实例并做了全面的初始化操作。 第二句中的writeInterfaceToken用于写入IBinder接口标志所带参数是String类型的如IServiceManager.descriptor “android.os.IServiceManager”。 第三句通过writeString在Parcel中写入需要向ServiceManager查询的Service名称 Parcel在整个IPC中的内部传递过程比较烦琐特别在承载Binder数据时更是需要多次转换因而容易让人失去方向。但不管过程如何曲折有一点是始终不变的。那就是写入方和读取方所使用的协议必须是完全一致的。 来看看写入方ServiceManagerProxy都“装”了些什么东西到“集装箱”中 status_t Parcel::writeInterfaceToken(const String16 interface) {writeInt32(IPCThreadState::self()-getStrictModePolicy() |STRICT_MODE_PENALTY_GATHER);return writeString16(interface); }等价于 writeInterfaceToken→writeInt32(policyvalue)writeString16(interface) 其中interface就是android.os.IServiceManager。 status_t Parcel::writeInt32(int32_t val) {return writeAligned(val); }这个函数的实现很简单——只包含了一句代码。从函数名来判断它是将val值按照对齐方式写入Parcel的存储空间中。换句话说就是将数据写入mDataPos起始的mData中当然内部还需要判断当前的存储能力是否满足要求、是否要申请新的内存等 status_t Parcel::writeString16(const String16 str) {return writeString16(str.string(), str.size()); } status_t Parcel::writeString16(const char16_t* str, size_t len) {if (str NULL) return writeInt32 (-1); //str不为空status_t err writeInt32(len); //先写入数据长度if (err NO_ERROR) {len * sizeof(char16_t); //长度*单位大小占用的空间uint8_t* data (uint8_t*)writeInplace(lensizeof(char16_t));if (data) {memcpy(data, str, len);/*将数据复制到data所指向的位置中 */*reinterpret_castchar16_t*(datalen) 0;return NO_ERROR;}err mError;}return err; }整个writeString16的处理过程并不难理解首先要填写数据的长度占据4个字节然后计算出数据所需占据的空间大小最后才将数据复制到相应位置中——writeInplace就是用来计算复制数据的目标地址的。 写入一个String(writeString16)的步骤 writeInt32(len) memcpy padding有些情况下不需padding。而且源码实现中这一步是在memcpy之前。 回到ServiceManagerProxy中的getService里 data.writeInterfaceToken(IServiceManager.descriptor); data.writeString(name);我们把上面两个语句进行分解就得到写入方的工作了 WriteInterfaceTokenwriteInt32(policyvalue)writeString16(interface) writeString16(interface) writeInt32(len)写入数据本身填充 读取方是Service_manager.c int svcmgr_handler(struct binder_state *bs, struct binder_txn *txn,struct binder_io *msg, struct binder_io *reply) {…uint32_t strict_policy;…strict_policy bio_get_uint32(msg); //取得policy值s bio_get_string16(msg, len); //取得一个String16,即上面写入的interfaceif ((len ! (sizeof(svcmgr_id) / 2)) ||memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {/*判断Interface是否正确*/fprintf(stderr,invalid id %s\n, str8(s));return -1;}上面代码段用于判断收到的interface是否正确。其中 uint16_t svcmgr_id[] { ‘a’,‘n’,‘d’,‘r’,‘o’,‘i’,‘d’,‘.’,‘o’,‘s’,‘.’, ‘I’,‘S’,‘e’,‘r’,‘v’,‘i’,‘c’,‘e’,‘M’,‘a’,‘n’,‘a’,‘g’,‘e’,‘r’ }; 和前面的android.os.IServiceManager是一样的 switch(txn-code) {case SVC_MGR_GET_SERVICE:case SVC_MGR_CHECK_SERVICE:s bio_get_string16(msg, len);//获取要查询的service nameptr do_find_service(bs, s, len, txn-sender_euid);if (!ptr)break;bio_put_ref(reply, ptr);return 0;可以看到ServiceManager对数据的读取过程和数据的写入过程确实完全一致。 Binder驱动与协议 Android系统是基于Linux内核的因而它所依赖的Binder驱动也必须是一个标准的Linux驱动。具体而言Binder Driver会将自己注册成一个misc device并向上层提供一个/dev/binder节点——值得一提的是Binder节点并不对应真实的硬件设备。Binder驱动运行于内核态可以提供open()ioctl()mmap()等常用的文件操作。 Linux中的字符设备通常要经过alloc_chrdev_region()cdev_init()等一系列操作才能在内核中注册自己。而misc类型驱动则相对简单只需要调用misc_register()就可轻松解决。比如Binder中与驱动注册相关的代码 /*drivers/staging/android/Binder.c*/ static struct miscdevice binder_miscdev {.minor MISC_DYNAMIC_MINOR, /*动态分配次设备号*/.name binder, /*驱动名称*/.fops binder_fops /*Binder驱动支持的文件操作*/ }; static int __init binder_init(void) { …ret misc_register(binder_miscdev); /*驱动注册*/… }Binder驱动还需要填写file_operations结构体。如下所示 /*drivers/staging/android/Binder.c*/ static const struct file_operations binder_fops {.owner THIS_MODULE,.poll binder_poll,.unlocked_ioctl binder_ioctl,.mmap binder_mmap,.open binder_open,.flush binder_flush,.release binder_release, };Binder驱动总共为上层应用提供了6个接口——其中使用最多的就是binder_ioctlbinder_mmap和binder_open。 1.1 打开Binder驱动——binder_open /*如果没有特别说明以下的函数都在Binder.c中*/ static int binder_open(struct inode *nodp, struct file *filp) {struct binder_proc *proc;…proc kzalloc(sizeof(*proc), GFP_KERNEL);/*分配空间*/if (proc NULL)return -ENOMEM;Binder驱动已经为用户创建了一个它自己的binder_proc实体之后用户对Binder设备的操作将以这个对象为基础。 1.2  binder_mmap mmap()可以把设备指定的内存块直接映射到应用程序的内存空间中。对于Binder驱动来说上层用户调用的mmap()最终就对应了binder_mmap()。假设有两个进程A和B其中进程B通过open()和mmap()后与Binder驱动建立了联系如图6-13所示。 1.对于应用程序而言它通过mmap()返回值得到一个内存地址当然这是虚拟地址这个地址通过虚拟内存转换分段、分页后最终将指向物理内存的某个位置。 2.对于Binder驱动而言它也有一个指针binder_proc-buffer指向某个虚拟内存地址。而经过虚拟内存转换后它和应用程序中指向的物理内存处于同一个位置。 这时Binder和应用程序就拥有了若干共用的物理内存块。换句话说它们对各自内存地址的操作实际上是在同一块内存中执行的。 我们再把进程A加进来 右半部分Binder驱动通过copy_from_user()把进程A中的某段数据复制到其binder_proc-buffer所指向的内存空间中。因为binder_proc-buffer在物理内存中的位置和进程B是共享的因而进程B可以直接访问到这段数据。也就是说Binder驱动只用了一次复制就实现了进程A和B间的数据共享。 1.3  binder_ioctl 这是Binder接口函数中工作量最大的一个它承担了Binder驱动的大部分业务。 “DNS”服务器——ServiceManager(Binder Server) ServiceManager以下简称SM的功能可以类比为互联网中的“DNS”服务器“IP地址”为0。另外和DNS本身也是服务器一样SM也是一个标准的Binder Server。 1.1 ServiceManager的启动 既然是DNS那么在用户可以浏览网页前就必须就位。SM也是同样的道理它要保证在有人使用Binder机制前就处于正常的工作状态。那么具体来说它是什么时候运行起来的呢我们很自然地会想到应该是在init程序解析init.rc时启动的。事实的确如此。如下所示 /*init.rc*/ service servicemanager /system/bin/servicemanagerclass coreuser systemgroup systemcriticalonrestart restart zygoteonrestart restart mediaonrestart restart surfaceflingeronrestart restart drm这个servicemanager是用C/C编写的源码路径在工程的/frameworks/native/cmds/ servicemanager目录中可以先来看看它的make文件 LOCAL_PATH: $(call my-dir) … include $(CLEAR_VARS) LOCAL_SHARED_LIBRARIES : liblog LOCAL_SRC_FILES : service_manager.c binder.c LOCAL_MODULE : servicemanager /*生成的可执行文件名为 servicemanager*/ include $(BUILD_EXECUTABLE)/*编译可执行文件*/1.2 ServiceManager的构建 先来看看SM启动后都做了哪些工作 /*frameworks/native/cmds/servicemanager/Service_manager.c*/ int main(int argc, char **argv) {struct binder_state *bs;void *svcmgr BINDER_SERVICE_MANAGER;bs binder_open(128*1024);if (binder_become_context_manager(bs)) { /*将自己设置为 Binder“大管家”整个Android 系统只允许一个ServiceManager存在因而如果后面还有人 调用这个函数就会失败*/ALOGE(canno t become context manager (%s)\n, strerror(errno));return -1;}svcmgr_handle svcmgr;binder_loop(bs, svcmgr_handler); //进入循环等待客户的请求return 0; }main函数里主要做了以下几件事 打开Binder设备做好初始化 将自己设置为Binder大管家 进入主循环。 那么具体来说需要做哪些初始化呢 /*frameworks/native/cmds/servicemanager/Binder.c */ struct binder_state *binder_open(unsigned mapsize) {struct binder_state *bs; /*这个结构体记录了SM中有关于Binder的所有 信息如fd、map的大小等*/bs malloc(sizeof(*bs));…bs-fd open(/dev/binder, O_RDWR); //打开Binder驱动节点…bs-mapsize mapsize; //mapsize是SM自己设的为128*1024即 128Kbs-mapped mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs- fd, 0);…return bs; fail_map:close(bs-fd); //关闭file fail_open:free(bs);return 0; }根据上面代码段中的参数设置可知 1.由Binder驱动决定被映射到进程空间中的内存起始地址 2.映射区块大小为128KB 3.映射区只读 4.映射区的改变是私有的不需要保存文件 5.从文件的起始地址开始映射 下面来看看main函数中的第二步操作即将servicemanager注册成Binder机制的“大管家” int binder_become_context_manager(struct binder_state *bs) {return ioctl(bs-fd, BINDER_SET_CONTEXT_MGR, 0); }所有准备工作已经就绪SM可以开始等待客户端的请求——这一部分工作才是SM的重点和难点。我们先从binder_loop()入手来看看SM是如何处理请求的分段阅读 void binder_loop(struct binder_state *bs, binder_handler func) {int res;struct binder_write_read bwr; /*这是执行BINDER_WRITE_READ命令 所需的数据格式*/unsigned readbuf[32];/*一次读取容量*/在Binder Server进入循环前它要先告知Binder驱动这一状态变化。下面这段代码就是为了完成这项工作 bwr.write_size 0;//这里只是先初始化为0下面还会再赋值bwr.write_consumed 0;bwr.write_buffer 0; readbuf[0] BC_ENTER_LOOPER;/*命令*/binder_write(bs, readbuf, sizeof(unsigned));然后SM就进入了循环。循环体中需要做些什么? 1.从消息队列中读取消息。 2.如果消息是“退出命令”则马上结束循环如果消息为空则继续读取或者等待一段时间后再读取如果消息不为空且不是退出命令则根据具体情况进行处理。 3.如此循环往复直到退出。 不过SM中没有消息队列它的“消息”或称“命令”是从Binder驱动那里获得的 for (;;) {bwr.read_size sizeof(readbuf);/*readbuf的大小为32个 unsigned*/bwr.read_consumed 0;bwr.read_buffer (unsigned) readbuf;/*读取的消息存储到 readbuf中*/res ioctl(bs-fd, BINDER_WRITE_READ, bwr); //读取“消 息”…res binder_parse(bs, 0, readbuf, bwr.read_consumed, func);//处理这条消息if (res 0) {ALOGE(binder_loop: unexpected reply?!\n);break;}if (res 0) {ALOGE(binder_loop: io error %d %s\n, res, strerror(errno));break;}} }/*binder_loop结束*/由此可见SM遵循以下几个步骤 1.从Binder驱动读取消息 通过发送BINDER_WRITE_READ命令实现——这个命令既可读取也可写入具体要看bwr.write_size和bwr.read_size。因为这里write_size的初始化值为0而read_size为sizeof(readbuf)所以Binder驱动只执行读取操作。 2.处理消息 调用binder_parse来解析消息。 3.不断循环而且永远不会主动退出除非出现致命错误 1.3 获取ServiceManager服务——设计思考 如果要访问SM(Binder Server)的服务流程应该是怎么样的呢 无非就是以下几步 打开Binder设备执行mmap通过Binder驱动向SM发送请求SM的handle为0获得结果。 不要怀疑核心工作确实只有这些。不过一些具体细节还需要再商榷比如 向SM发起请求的Binder Client可能是Android的APK应用程序所以SM必须要提供Java层接口如果每个Binder Client都要亲力亲为地执行以上几个步骤来获取SM服务那么可想而知会浪费不少时间。Android系统当然也想到了这一点所以它会提供更好的封装来使整个SM调用过程更精简实用。如果应用程序代码中每次使用SM服务或者其他Binder Server服务都需要打开一次Binder驱动、执行mmap其后果就是消耗的系统资源会越来越多直到崩溃。一个有效的解决办法是每个进程只允许打开一次Binder设备且只做一次内存映射——所有需要使用Binder驱动的线程共享这一资源。 问题转化为如果让我们来设计一个符合上述要求的BinderClient应该怎么做 1ProcessState和IPCThreadState 首先能想到的是要创建一个类来专门管理每个应用进程中的Binder操作——更为重要的是执行Binder驱动的一系列命令对上层用户必须是“透明的”。这个类就是ProcessState。仅有ProcessState是不够的进程中的每一个线程都应该有与Binder驱动自由沟通的权利——而且基于Binder的IPC是阻塞的这样能保证个别thread在做进程间通信时不会卡死整个应用程序。与Binder驱动进行实际命令通信的是IPCThreadState。 2Proxy 有了上面两个类应用程序现在可以与Binder驱动通信了。原则上我们还是可以通过发送BINDER_WRITE_READ等Binder支持的命令来与其交互从而一步步得到SM提供的服务。到还需要对SM提供的服务进行封装把这个SM服务的封装取名为ServiceManagerProxy。 /*应用程序获取SM服务示例*/ //Step1. 创建ServiceManagerProxy ServiceManagerProxy sm new ServiceManagerProxy(new BpBinder(HANDLE)); //Step2. 通过ServiceManagerProxy获取SM的某项服务 IBinder wms_binder sm.getService(window);应用程序只需要两步就可以得到ServiceManager提供的服务。 如何实现这个目标 1ServiceManagerProxy的接口。ServiceManagerProxy所能提供的服务和服务端的SM必须是一致的如getServiceaddService等。把这些方法提取出来就是ServiceManagerProxy的接口。我们给 它取名为IServiceManager如下所示大家先忽略它的参数 public interface IServiceManager {public IBinder getService(String name) throws RemoteException;public IBinder checkService(String name) throws RemoteException;public void addService(String name, IBinder service, boolean allowIsolated)throws RemoteException;public String[] listServices() throws RemoteException; }很显然ServiceManagerProxy需要继承自IServiceManager如图所示。 2接口实现。以getService为例要取得ServiceManager的这个服务至少有两部分工作。 1.与Binder建立关系 因为进程中已经有了ProcessState和IPCThreadState这两个专门与Binder驱动通信的类所以Java层代码使用Binder驱动实际上是基于它们来完成的。我们称为BpBinder。 2.向Binder发送命令从而获得SM提供的服务。 总结如下 Binder架构 它的主体包括驱动、SM、Binder Client和Binder Server。Binder驱动Service Manager SM既是Binder框架的支撑者同时也是一个标准的Server。 可以用一张图来概括Binder机制如图所示获取SM服务主要组成元素。 图中Client表示Binder Client即使用Binder机制来获取服务的客户端。它的内部结构由下而上依次为 ProcessState/IPCThreadState→BpBinder→Proxy→User。不论是Client或者Service Manager它们的工作都是基于Binder Driver完成的。 1.4 ServiceManagerProxy 前一小节思考“设计意图”时我们曾通过一小段伪代码描述了ServiceManagerProxy的一种实现方案——Android系统中的具体实现与此基本类似只不过它在ServiceManagerProxy上又加了一层封装即ServiceManager.java。 这样应用程序使用ServiceManager就更加方便了连ServiceManagerProxy对象都不用创建如下所示 ServiceManager.getService(name); getService的内部实现 public static IBinder getService(String name) {try {IBinder service sCache.get(name);//查询缓存if (service ! null) {return service;//从缓存中找到结果直接返回} else {return getIServiceManager().getService(name);//向SM发起查询}} catch (RemoteException e) {Log.e(TAG, error in getService, e);}return null;}private static IServiceManager getIServiceManager() {if (sServiceManager ! null) {return sServiceManager;//返回一个IServiceManager对象}// Find the service managersServiceManager ServiceManagerNative.asInterface(BinderInternal.getContextObjec t());return sServiceManager;}ServiceManagerNative /*frameworks/base/core/java/android/os/ServiceManagerNative.java */static public IServiceManager asInterface(IBinder obj){if (obj null) {return null;}IServiceManager in (IServiceManager)obj.queryLocalInterface(descriptor);if (in ! null) {return in;}return new ServiceManagerProxy(obj);}从这个函数的注释可以发现它负责将一个Binder对象转换成IServiceManager并在必要的时候创建ServiceManagerProxy。 ServiceManagerProxy必定是要与Binder驱动通信的因而它的构造函数中传入了IBinder对象 public ServiceManagerProxy(IBinder remote) {mRemote remote; }可以看到它只是简单地记录了这个IBinder对象。这就像我们电话订餐一样IBinder是餐馆的电话号码通常都是先把它记下来等需要的时候再通过这个号码获取餐馆提供的服务。比如getService()这个接口 public IBinder getService(String name) throws RemoteException {Parcel data Parcel.obtain();Parcel reply Parcel.obtain();data.writeInterfaceToken(IServiceManager.descriptor);data.writeString(name);mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);/*利用IBinder对象执行命令*/IBinder binder reply.readStrongBinder();reply.recycle();data.recycle();return binder;}这个函数实现分为以下3部分。 准备数据IBinder.transact 利用IBinder的transact将请求发送出去而不用理会Binder驱动的openmmap以及一大堆具体的Binder协议中的命令。所以这个IBinder一定会在内部使用ProcessState和IPCThreadState来与Binder驱动进行通信获取结果 实际工作只有下面这句 mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);这里需要注意一下就是客户端和服务器端所使用的业务代码要一致如上面的GET_SERVICE_TRANSACTION。它的定义是 int GET_SERVICE_TRANSACTION IBinder.FIRST_CALL_TRANSACTION;按照IBinder的定义 int FIRST_CALL_TRANSACTION 0x00000001;所以这个业务码是1。再来看Service_manager.c中的业务码说明 enum {SVC_MGR_GET_SERVICE 1, //对应的就是上面的那个业务SVC_MGR_CHECK_SERVICE,SVC_MGR_ADD_SERVICE,SVC_MGR_LIST_SERVICES, };这样客户端与服务器端的业务代码就保持一致了。 1.5 IBinder和BpBinder 在创建ServiceManagerProxy时传入了一个IBinder对象然后借助于它的transact方法可以方便地与Binder驱动进行通信。那么IBinder内部是如何实现的 Binder提供的功能可以统一在IBinder中表示至少要有如下接口方法 /*frameworks/base/core/java/android/os/IBinder.java*/ public interface IBinder {public IInterface queryLocalInterface(String descriptor);public boolean transact(int code, Parcel data, Parcel reply, int flags)throws Remote Exception;… }此外还应该有获取IBinder对象的一个类即BinderInternal。提供的相应方法是 /*frameworks/base/core/java/com/android/internel/os/BinderIntern al.java*/ public class BinderInternal {public static final native IBinder getContextObject();… }对应的native方法 /*frameworks/base/core/jni/android_util_Binder.cpp*/ static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz) {spIBinder b ProcessState::self()- getContextObject(NULL);return javaObjectForIBinder(env, b); }是通过ProcessState来实现的把ProcessState中创建的对象转化成Java层的IBinder对象。 IBinder只是一个接口类显然还会有具体的实现类继承于它。在Native层这就是BpBinder (BpBinder.cpp)而在Java层则是Binder.java中的BinderProxy。事实上ProcessState::self() -getContextObject(NULL)返回的就是一个BpBinder对象。 BinderProxy和BpBinder分别继承自Java和Native层的IBinder接口。其中BpBinder是由ProcessState创建的而BinderProxy是由javaObjectForIBinder()函数通过JNI的NewObject()创建的。 分析源码mRemote-transact调用BinderProxy的transact方法真正的实现还是在android_util_Binder.cpp中最后就是通过BpBinder.transact来处理用户的Binder请求 /*frameworks/native/libs/binder/BpBinder.cpp*/ status_t BpBinder::transact(uint32_t code, const Parcel data, Parcel* reply, uint32_t flags) {// Once a binder has died, it will never come back to life.if (mAlive) {status_t statusIPCThreadState::self()- transact(mHandle,code,data,reply, flags);if (status DEAD_OBJECT) mAlive 0;return status;}return DEAD_OBJECT; }最终还是通过IPCThreadState及ProcessState来实现的。 1.6 ProcessState和IPCThreadState 实现ProcessState的关键点在于。 保证同一个进程中只有一个ProcessState实例存在而且只有在ProcessState对象创建时才打开Binder设备以及做内存映射。向上层提供IPC服务。与IPCThreadState分工合作各司其职。 大多数程序都有IPC的需要而进程间通信本身又是非常烦琐的因而Android系统特别为程序进程使用Binder机制封装了两个实现类即ProcessState和IPCThreadState。从名称上可以看出前者是进程 相关的而后者是线程相关的。ProcessState负责打开Binder驱动设备进行mmap()等准备工作而如何与Binder驱动进行具体的命令通信则由IPCThreadState来完成。 Binder客户端——Binder Client 1Binder是什么 它是众多进程间通信的一种。进程间通信是每个操作系统都需要提供的。 2应用程序与Binder Binder的最大“消费者”是Java层的应用程序。图6-21虽然简单却概括了Binder的实质即它是进程间的通信桥梁。接下来我们 还需要沿着这条线索继续挖掘。图中所示的进程1是一种泛指那么一个进程需要满足什么条件或者说要做哪些准备工作才有资格使用Binder呢从应用开发人员的角度来看这似乎并不在他们的考虑范围因为一般情况下他们可以在程序代码的任何位置通过bindServicestartActivity以及sendBroadcast等一系列接口方法来实现与其他进程的交互如图6-22所示。 有了Binder DriverService Manager的努力以及Android系统专门面向应用开发提供的Binder强有力的封装才能使应用程序之间顺利地进行无缝通信。我们从四大组件中就可以看出一些端倪。 Activity 通过startActivity可以启动目标进程。 Service 任何应用程序都可以通过startService或bindService来启动特定的服务而不论后者是不是跨进程的。 Broadcast 任何应用程序都可以通过sendBroadcast来发送一个广播且无论广播处理者是不是在同一个进程中。intent 四大组件的上述操作中多数情况下并不会特别指明需要由哪个目标应用程序来响应请求——它们会先通过一个被称为“intent”的 对象表达出“意愿”然后由系统找出最匹配的应用进程来完成相关工作。这样的设计极大地增强了系统的灵活性。 下面将会选取bindService为例向大家充分揭示隐藏在这些接口背后的Binder内部原理。 由图可知整个框架被一分为二分别代表了Binder机制中对应用程序可见和隐藏的两部分。 为了让大家可以更清楚地看到整个“隐藏部分”的内部实现接下来选取一个范例进行剖析。如图6-25所示Application1中的某个 Activity通过bindService(intent)来试图启动符合intent描述的Service服务——最终Application2中的Service将被运行。 应用程序如何能依托bindService来启动系统中其他进程提供的Service呢必定需要以下几个步骤才能完成这一操作目标。 Step1. 应用程序填写Intent调用bindService发出请求。Step2. 收到请求的bindService此时还在应用程序的运行空间中将与Activity ManagerService(AMS)取得联系。为了获得AMS的Binder句柄值还要事先调用ServiceManager.getService这里就已经涉及进程间通信了。在得到AMS的句柄值后程序才能真正地向它发起请求Step3. AMS基于特定的“最优匹配策略”从其内部存储的系统所有服务组件的资料中找到与Intent最匹配的一个然后向它发送 Service绑定请求这一步也是进程间通信——注意如果目标进程还不存在的话AMS还要负责把它启动起来。Step4. “被绑定”的服务进程需要响应绑定执行具体操作并在成功完成后通知AMS然后由后者再回调发起请求的应用程序回调接口是ServiceConnection 由此可见一个看似简单的bindService原来内部“大有乾坤”。但是为什么Application1在Activity中只需要调用bindService即可 而丝毫不见上述的烦琐过程呢 基于Activity应用程序的继承关系如图6-26所示 Activity继承关系的“根”是Context。bindService自然也是包含在Context里面的。具体而言Context只是提供了抽象的接口功能则是在ContextWrapper中实现的 /*frameworks/base/core/java/android/content/ContextWrapper.java*/public boolean bindService(Intent service, ServiceConnection conn,int flags) {return mBase.bindService(service, conn, flags); //mBase是什么}上述变量mBase也是一个Context对象最新版本中是由ContextImpl来实现的bindService直接调用bindServiceAsUser /*frameworks/base/core/java/android/app/ContextImpl.java*/public boolean bindServiceAsUser(Intent service,ServiceConnection conn,int flags,UserHandle user) {…int res ActivityManagerNative.getDefault().bindService(mMainThread.getApplicationThread(), getActivityToken(),service, service.resolveTypeIfNeeded(getContentResolver()),sd, flags, userId); /*ActivityManager出现了证明了我们猜测的第2步*/…}那么应用程序又是如何找到AMS并与之建立联系的呢和ServiceManager一样AMS也同样提供了ActivityManagerNative和ActivityManagerProxy具体如下 /*frameworks/base/core/java/android/app/ActivityManagerNative.ja va*/static public IActivityManager getDefault() {return gDefault.get(); /*得到默认的IActivityManager对象*/}这个gDefault.get()得到的是什么 /*frameworks/base/core/java/android/app/ActivityManagerNative.ja va*/private static final SingletonIActivityManager gDefault new Singleton IActivityManager() {/*Singleton即“单实例”是一种常见的设计模式它保证某个对象只会被创建 一次。当调用gDefault.get()时会先进行内部判断:如果该对象已经存在就直接 返回它的现有值;否则才需要通过内部create()新建一个对象实例*/protected IActivityManager create() {IBinder b ServiceManager.getService(activity);/*通过 ServiceManager Service取得ActivityManagerService的 IBinder对象*/…IActivityManager am asInterface(b); /*创建一个可用的 ActivityManagerProxy*/…return am;}};ActivityManagerNative的作用之一就是帮助调用者方便快速地取得一个ActivityManagerProxy。 在gDefault这个单实例中获取一个有效的IActivityManager对象需要两个步骤即 得到IBinderBpBinder将IBinder转化为Iinterface在这个场景中是IactivityManager。 顺便说一下ActivityManagerNative的另一个作用是为ActivityManagerService的实现提供便利。如果仔细观察就会发现 ActivityManagerNative里有如下方法 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws Remote Exception {switch (code) {case START_ACTIVITY_TRANSACTION:{…int result startActivity(app, intent, resolvedType,grantedUriPermissions, grantedMode, resultTo, resultWho,requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopPro filer);…}这样在AMS里只要继承自ActivityManagerNative就已经将用户的业务请求码与自己的内部实现函数连接了起来是不是很方便源代码如下 /*frameworks/base/services/java/com/android/server/am/ActivityMa nagerService.java*/ public final class ActivityManagerService extends ActivityManagerNative/*果然继承了ActivityManagerNative*/implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {…因而可以这么说ActivityManagerNative其他服务的Native也是一样的既是面向调用者的也是面向服务实现本身的只不过这个Native的名称取得容易产生歧义。 经过上面代码的分析Application1和Application2的进程间通信还应该再加上ServiceManager和ActivityManagerService的支持 如图6-27所示 当应用程序需要通过ServiceManager来查询某个Binder Server时调用的是getService方法。直接面向应用程序的是Service Manager.java它提供了多个static接口供调用者获取ServiceManager提供的服务如Service Manager.getService。这些 静态函数内部通过getIServiceManager来得到ServiceManagerProxy对象——后者作为SM的本地代理将利用IBinder来“穿越”JNI层调用到对应的BpBinder进而使用ProcessState和IPCThreadState的相关接口最终经由Binder驱动完成与ServiceManager的通信。 bindService 调用流程 Android接口描述语言——AIDL AIDL是Android Interface Description Language的简写。从名称上看它是一种语言而且是专门用于描述接口的语言。准确地说它是用于定义客户端/服务端通信接口的一种描述语言。 通过一个范例来分析采用AIDL究竟可以为Binder Server带来哪些便利以及它的内部实现原理。以WindowManagerService为例 1WMS是在SystemServer中启动的 2 看看AIDL是如何保证接口的一致性的。使用AIDL首先要书写一个*.aidl文件来描述这个Server。比如 /*IWindowManager.aidl*/ interface IWindowManager {… IWindowSession openSession(in IInputMethodClient client,in IInputContext inputContext); … }上述代码段只保留了openSession一个接口方法。这个IWindowManager.aidl文件经过工具转化后成为以下内容 /*IWindowManager.java*/ public interface IWindowManager extends android.os.IInterface { public static abstract class Stub extends android.os.Binder //Stub表示一个“桩” implements android.view.IWindowManager { public static android.view.IWindowManager asInterface(android.os.IBinder obj) { … } Override public android.os.IBinder asBinder() { return this; } Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case TRANSACTION_openSession: { … }… } return super.onTransact(code, data, reply, flags); } private static class Proxy implements android.view.IwindowManager //Proxy就是“代理”我们已经多次讲解 { private android.os.IBinder mRemote; … Override public android.os.IBinder asBinder() { return mRemote; } Override public android.view.IWindowSessionopenSession(com.android.internal.view.IInputMethodClient client,com.android.internal.view.IInputContext inputContext) throws android.os.RemoteException { … } } //Proxy结束 }//Stub结束 … static final int TRANSACTION_openSession (android.os.IBinder.FIRST_CALL_TRANSACTION 3); /*自动分配业务码大家可以和ServiceManager中的手工分配做下对比*/ }//IWindowManager结束IWindowManager 一般以大写字母I开头的表示一个Interface。在AIDL中所有的服务接口都继承于Iinterface然后在此基础上声明与此Server服务相关的方法。比如IWindowManager中除了两个嵌套类外其末尾还包含了它提供的服务openSession、getRealDisplaySize、hasSystemNavBar等接口的原型。IWindowManager.Stub 还记得ServiceManagerNative吗Stub的作用和它类似。它包含了一个嵌套类Stub.Proxy以及各种常用的接口方法如asInterfaceasBinder等其中最重要的一个就是onTransact。我们知道ServiceManagerNative是同时面向服务器和客户端的Stub也同样如此。在实际使用中一个Binder Server的实现类通常继承自Stub。而Stub又继承自Binder并实现了该Server的IXX接口如IWindowManager的实现类WindowManagerService public class WindowManagerService extends IWindowManager.StubIWindowManager.Stub.Proxy Proxy即代理功能和ServiceManager Proxy类似。因而这个类是面向Binder Client的它可以让调用者轻松地构造出Binder Server的本地代理对象 具体如图所示基于AIDL的Binder Server 通过分析aidl文件以及由它转化生成的java接口文件我们知道一个AIDL接口包括了IwindowManagerIWindowManager.Stub和IWindowManager.Stub.Proxy三个重要类。后两者分别面向于WMS的服务端和Binder Client本地代理的实现且都继承于IWindowManager因而就保证了Client和Server是在完全一致的服务接口上进行通信的。 3如何与Binder驱动交互的 通过解析我们发现原来系统服务进程在一开始就调用了 ProcessState::self()-startThreadPool(); IPCThreadState::self()-joinThreadPool();它们将导致程序最终进入一个类似于binder_loop的主循环。因而在这个进程中的Binder对象都可以不用单独与驱动进行交互 4Client如何准确地访问到目标进程中的Binder Server 有两种方式可以让Binder Server可知其一是通过在SM中注册也就是WMS所属的情况。其二就是匿名Server实现。对于实名的Server当它利用addService来把自身注册到SM中时会“路过”Binder驱动而后者就会按计划把这一Binder对象链接到proc- nodes以及target_proc- refs_by_desc和target_proc- refs_by_node中。在这一场景中proc是系统服务进程而target_proc则是SM。也就是说SM中拥有了一个描述WMS的binder_node的引用。这样当一个Binder Client通过getService向SM发起查询时后者就可以准确地告知这个调用者它想访问的WMS节点所在的位置。 匿名Binder Server 通过addService把自己注册到了Service Manager中因而任何BinderClient都可以通过SM的getService接口来获取它的一个引用。称这种类型的Binder Server为“实名”Server。不在Service Manager中注册称之为“匿名”的Binder Server。匿名性带来的一个直接好处是安全系数的提高如某个应用程序提供了某种Server服务但并不希望面向公众开放。
http://www.hkea.cn/news/14477978/

相关文章:

  • led灯 东莞网站建设广东网站设计推荐
  • 网站链接推广怎么赚钱深圳设计公司名字
  • 这么做网站教程wordpress主题模板源码
  • 水果网站系统的建设与实现怎样查企业注册信息查询
  • 网站建设与网页设计案例教程pdf下载代理服务器在哪里找
  • 西安手机网站建站宣传推广策略有哪些
  • 建网站主机南京江北新区规划
  • 如何做网站效果图外贸企业网站对外贸的重要性
  • 网站开发的基本条件做网站框架需要什么软件
  • 手机网站左右滑动珠海做网站公司
  • 天津中小企业网站建设做一个中英文双语网站建设多少钱
  • discuz 手机网站模板html5做网站链接范例
  • js判断是手机还是电脑访问网站帮做论文网站吗
  • 网站建设朋友圈素材自己做卖东西的网站
  • 徐州网站建设薇at57666y邯郸做网站推广费用
  • 做直播网站需要哪些技术网线制作的标准
  • 做网站ps能用美图秀秀么网站建设与管理用什么软件
  • 邢台手机网站建设惠州外贸网站建设
  • 体育局网站建设秦皇岛房管局备案查询网
  • 贸易网站建设什么行业最需要网站建设
  • 网站关键词布局图很有质感的网站
  • 安阳住房与城乡建设局官方网站wordpress手机底部导航栏设置
  • 移动微网站建设邢台信息港123招聘
  • 上海网站建设公司官网网站权重多少比较好
  • 北京微信网站开发wordpress注册链接
  • 甘肃自助建站系统怎么用用手机搭建wordpress
  • 广州旅游网站建设字体设计在线生成免费
  • wordpress 本地建站教程wordpress绑定wap域名
  • 电子商务网站建设与管理的背景电商平台运营是做什么的
  • 世界网站制作wordpress网站页脚