当前位置: 首页 > 编程日记 > 正文

Android Binder概述

背景知识

为了更好的理解binder,我们要先澄清一下概念,因为Android 基于Linux内核,我们有必要了解相关知识。

进程隔离

进程隔离是为了保护操作系统进程之间互不干扰而设计的,这个技术是为了避免进程A写入进程B准备的,进程隔离的实现,使用了虚拟地址空间,进程A的虚拟地址和进程B的虚拟地址不同,这样就防止进程A的数据写入进程B,操作系统之间不同进程之间,数据不共享,对于每一个进程来说,都天真的以为自己独享了整个系统,不知道其他进程的存在,因此一个进程与其他进程进行通信,需要一种特殊的方式才可以

用户空间/内核空间

Linux Kernel 是操作系统的核心,独立于普通应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。

对于Kernel这么一个高安全级别的东西,显然是不允许其他应用程序随便调用或访问的,所以需要对Kernel提供一定的保护机制,这个保护机制用来告诉那些应用程序,你只可以访问某些许可的资源,不可以访问不许可的资源,于是就把Kernel和上层应用程序抽象的隔离开,分别称之为 Kernel Space 和 User Space


系统调用/内核态/用户态

虽然在逻辑上抽离了用户空间、内核空间,但不可避免的是,仍有用户空间需要访问内核空间的资源,比如程序访问文件。该怎么办?

用户空间访问内核空间的唯一方式就是系统调用 ,通过这一统一入口,所有资源访问都是在内核控制下进行,以免导致用户程序对系统资源的越权访问,从而保障了系统的安全,稳定

当一个任务进程进行系统代码调用而陷入内核代码执行时,我们就称进程处于内核运行状态,简称内核态,此时处理器处于特权级别最高的(0)状态,内核代码中执行,当进程在执行用户自己的代码时,称其处于用户状态,简称用户态,此时处理器在特权最低(3)状态,处理器正在特权高的时候才能执行那些特权cpu指令

内核模块/驱动

通过系统调用可使用户空间访问内核空间,如果一个用户空间访问另一个用户空间该怎么办?我们首先想到的是通过内核添加支持,因为内核是共享的,传统的Linux机制比如Socket ,管道,都可以支持的,但是Binder并不是Linux内核的一部分,他是怎么做到访问内核空间的?Linux的动态可加载内核模块机制解决了这个问题,模块是具有独立功能的程序,他可以被单独编译,但不能独立运行,它在运行时被链接到内核作为内核的一部分在内核空间运行,这样,Android 系统通过添加一个内核模块运行在内核空间,用户进程之间通过这个模块作为桥梁,就可以完成通信。

在Android 系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动

驱动程序一般指的是设备驱动程序,是一种可以使计算机和设备通信的特殊程序,相当于硬件的接口,操作系统只有通过这个接口才能操作硬件设备

驱动就是操作硬件的接口,为了支持Binder通信过程,binder使用了一种硬件,因此这个模块称之为驱动。

为什么使用Binder

Android 使用的Linux内核有很多种的跨进程通信的方式,那么为什么还要单独搞出一个Binder出来呢?主要有俩点,性能和安全,在移动设备上,广泛的使用跨进程通信肯定对通信体制本身提出了严格的要求;Binder相对于传统的Socket方式,更加高效,另外,传统 的进程通信方式对通信双方身份并没有做出严格的验证,只要在上层协议上进行架设,,比如Socket通信的ip地址是客户端手动输入的,可以进行伪造,而binder机制从协议本身就支持对通信双方的身份验证,从而大大提高了安全性,这个也是Android权限模型的基础。

Binder通信模型

Android 系统 Binder机制中的四个组件,Client,Server,ServerManager,和Binder驱动关系如下


1 Client ,Server,ServerManager,实现用户空间,Binder驱动程序实现在内核空间中

2 Binder驱动和ServerManager 在Android 平台已经实现,开发者只需要在用户空间实现自己的Client和Server

3 Binder驱动程序提供设备文件/dev/binder与用户空间交互,Client ,Server,ServerManager 通过open和ioctl文件操作函数与binder驱动程序进行通信。

4 Client和Server 进程通信通过binder驱动程序间接实现

5 ServerManager 是一个守护进程,用来管理Server,并像Client提供查询Server能力

6 ServerManager 建立,首先要有一个进程向驱动提出成为SM。驱动同意后,SM负责管理Server,不过现在是空的

7 各个Server向SM注册,每个Server端进程启动后,向SM报告,比如我是zhangsan,要找我请返回0X1234,其他Server亦是如此,这样SM就建立一张表,对应着Server的名字和地址

8 Client 要与Server通信,首先询问SM ,请告诉我如何联系zhangsan,SM收到后返回一地址0X1234,Client收到后就可以和Server通信

那么Binder驱动在干什么呢,Client和SM通信,Client和Server通信,Server和SM通信都是通过Binder驱动,驱动默默无闻,但做着最重要的工作,驱动是整个通信的核心,因此完成跨进程通信的秘密全部隐藏在驱动里面,这个稍后讨论

Binder机制跨进程原理

上文给出了Binder的通信模型,指出了通信过程的四个角色: Client, Server, SM, driver; 但是我们仍然不清楚Client到底是如何与Server完成通信的

两个运行在用户空间的进程A和进程B如何完成通信呢?内核可以访问A和B的所有数据;所以,最简单的方式是通过内核做中转;假设进程A要给进程B发送数据,那么就先把A的数据copy到内核空间,然后把内核空间对应的数据copy到B就完成了;用户空间要操作内核空间,需要通过系统调用;刚好,这里就有两个系统调用:copy_from_user, copy_to_user

但是,Binder机制并不是这么干的。讲这么一段,是说明进程间通信并不是什么神秘的东西。那么,Binder机制是如何实现跨进程通信的呢?

Binder驱动为我们做了一切。

假设Client进程想要调用Server进程的object对象的一个方法add;对于这个跨进程通信过程,我们来看看Binder机制是如何做的。 (通信是一个广泛的概念,只要一个进程能调用另外一个进程里面某对象的方法,那么具体要完成什么通信内容就很容易了。)

首先,Server进程要向SM注册;告诉自己是谁,自己有什么能力;在这个场景就是Server告诉SM,它叫zhangsan,它有一个object对象,可以执行add 操作;于是SM建立了一张表:zhangsan这个名字对应进程Server;

然后Client向SM查询:我需要联系一个名字叫做zhangsan的进程里面的object对象;这时候关键来了:进程之间通信的数据都会经过运行在内核空间里面的驱动,驱动在数据流过的时候做了一点手脚,它并不会给Client进程返回一个真正的object对象,而是返回一个看起来跟object一模一样的代理对象objectProxy,这个objectProxy也有一个add方法,但是这个add方法没有Server进程里面object对象的add方法那个能力;objectProxyadd只是一个傀儡,它唯一做的事情就是把参数包装然后交给驱动。(这里我们简化了SM的流程,见下文)

但是Client进程并不知道驱动返回给它的对象动过手脚,毕竟伪装的太像了,如假包换。Client开开心心地拿着objectProxy对象然后调用add方法;我们说过,这个add什么也不做,直接把参数做一些包装然后直接转发给Binder驱动。

驱动收到这个消息,发现是这个objectProxy;一查表就明白了:我之前用objectProxy替换了object发送给Client了,它真正应该要访问的是object对象的add方法;于是Binder驱动通知Server进程,调用你的object对象的add方法,然后把结果发给我,Sever进程收到这个消息,照做之后将结果返回驱动,驱动然后把结果返回给Client进程;于是整个过程就完成了。

由于驱动返回的objectProxy与Server进程里面原始的object是如此相似,给人感觉好像是直接把Server进程里面的对象object传递到了Client进程;因此,我们可以说Binder对象是可以进行跨进程传递的对象

但事实上我们知道,Binder跨进程传输并不是真的把一个对象传输到了另外一个进程;传输过程好像是Binder跨进程穿越的时候,它在一个进程留下了一个真身,在另外一个进程幻化出一个影子(这个影子可以很多个);Client进程的操作其实是对于影子的操作,影子利用Binder驱动最终让真身完成操作。

理解这一点非常重要;务必仔细体会。另外,Android系统实现这种机制使用的是代理模式, 对于Binder的访问,如果是在同一个进程(不需要跨进程),那么直接返回原始的Binder实体;如果在不同进程,那么就给他一个代理对象(影子);我们在系统源码以及AIDL的生成代码里面可以看到很多这种实现。

另外我们为了简化整个流程,隐藏了SM这一部分驱动进行的操作;实际上,由于SM与Server通常不在一个进程,Server进程向SM注册的过程也是跨进程通信,驱动也会对这个过程进行暗箱操作:SM中存在的Server端的对象实际上也是代理对象,后面Client向SM查询的时候,驱动会给Client返回另外一个代理对象。Sever进程的本地对象仅有一个,其他进程所拥有的全部都是它的代理。

一句话总结就是:Client进程只不过是持有了Server端的代理;代理对象协助驱动完成了跨进程通信。

Binder到底是什么?

Binder对于使用者来言,Server端的Binder和Client端的Binder 没有任何区别,一个Binder对象就代表了所有,不用关心实现的细节,甚至不用关心Binder驱动和SM的存在,这就是抽象

1 通常意义上讲,Binder是指的一种通信机制,我们说的aidl使用Binder进行通信,指的就是这种Binder的IPC机制

2 对于Server进程来说,Binder指的是Binder本地对象

3 对于Client进程来说,Binder指的是Binder代理对象,他只是Binder本地对象的一个远程代理,对这个Binder代理对象进行操作,会通过驱动最终发送到Binder本地对象去完成,对一个拥有Binder对象使用者来说,它无需关心Binder是代理还是本地,对于代理对象和本地对象操作并没有区别。

4 对于传输过程来言,Binder是可以进行跨进程传递的对象,Binder驱动会对具有跨进程传递的能力的对象进行处理,自动完成代理对象和本地对象的转换。

驱动里的Binder

我们现在知道,Server里的Binder指的是Binder本地对象,Client里的Binder指的是Binder代理对象,Binder在进行跨进程传递的时候,Binder驱动自动完成这俩种类型的转换,因此,Binder驱动必然保存了每一个,跨进程Binder对象的信息,在驱动中,Binder本地对象的代表是

一个叫做Binder_node的数据结构,Binder代理对象是一个binder_ref的数据结构,有的地方吧binder本地对象成为Binder实体,把Binder代理对象称作Binder引用(句柄),其实指的是Binder对象在驱动中的表现形式,理解就好

深入理解Java层的Binder

IBinder/IInterface/Binder/BinderProxy/Stub

我们使用AIDL接口的时候,经常会接触到这些类,那么这每个类代表的是什么呢?

1 IBinder是一个接口 ,他代表的是一种跨进程通信的能力,只要实现了这个接口,就能让这个对象进行跨进程传递,这是驱动底层支持的,跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而完成本地Binder和代理Binder的转换

2 IBinder负责数据传输,那么Client和Server的调用契约是什么呢?这里的IInterface代表的就是远程Server有什么能力,具体来说就是aidl里面的接口。

3 java层的Binder其实就是代表的本地Binder,BinderProxy是Binder的内部类,它代表远程进程的Binder的本地代理,俩个都继承IBinder,所以都具有跨进程通信的能力。在跨越进程的时候,Binder驱动会自动完成俩个对象的装换。在Client进程

onServiceConnected 这个方法返回的Binder指的是BinderProxy。复制代码

4 在使用aidl的时候,编译工具会给我们生成一个Stub的静态内部类,这个类继承了Binder,说明他是一个Binder本地对象,他实现了IInterface接口,说明他远程Server的能力,具体的IInterface相关需要我们自己去实现。

aidl的过程分析

现在我们通过一个aidl的使用,来分析整个通信过程中,各个角色到底做了什么,aidl到底是如何完成通信的。

首先定一个一个简单的aidl接口:

1
2
3
4
5
复制代码
// ICompute.aidl
package com.example.test.app;
interface ICompute {int add(int a, int b);
}
复制代码

然后用编译工具编译之后,可以得到对应的ICompute.java类,看看系统给我们生成的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
复制代码
package com.example.test.app;public interface ICompute extends android.os.IInterface {/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binder implements com.example.test.app.ICompute {private static final java.lang.String DESCRIPTOR = "com.example.test.app.ICompute";/*** Construct the stub at attach it to the interface.*/public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.example.test.app.ICompute interface,* generating a proxy if needed.*/public static com.example.test.app.ICompute asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.example.test.app.ICompute))) {return ((com.example.test.app.ICompute) iin);}return new com.example.test.app.ICompute.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_add: {data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.example.test.app.ICompute {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/@Overridepublic int add(int a, int b) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(a);_data.writeInt(b);mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();_result = _reply.readInt();} finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/public int add(int a, int b) throws android.os.RemoteException;
}
复制代码

系统帮我们生成了这个文件之后,我们只需要继承ICompute.Stub这个抽象类,实现它的方法,然后在Service 的onBind方法里面返回就实现了AIDL。这个Stub类非常重要,具体看看它做了什么。

Stub继承自Binder,这就表明这个Stub是Binder本地对象,然后实现了ICompute接口,IComputer实现了IInterface,因此他携带Client需要的功能(这里是add),此类每部有一个proxy,也就是BinderProxy。

然后看看asInterface方法,看一下这个方法做了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
复制代码
/*** Cast an IBinder object into an com.example.test.app.ICompute interface,* generating a proxy if needed.*/
public static com.example.test.app.ICompute asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.example.test.app.ICompute))) {return ((com.example.test.app.ICompute) iin);}return new com.example.test.app.ICompute.Stub.Proxy(obj);
}复制代码

先看函数的参数 IBinder obj 这个是驱动给我们的,就是我们bind 一个service后onServiceConnected方法给我们的Binder,

如果是Binder代理对象,那就是
BinderProxy
类型;然后,正如上面自动生成的文档所说,它会试着查找Binder本地对象,如果找到,说明Client和Server都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回,如果找不到,说明是远程对象(处于另外一个进程)那么就需要创建一个Binde代理对象,让这个Binder代理实现对于远程对象的访问。一般来说,如果是与一个远程Service对象进行通信,那么这里返回的一定是一个Binder代理对象,这个IBinder参数的实际上是BinderProxy;

我们在来看看add方法的实现,在stub里面add是一个抽象方法,我么那些需要继承这个类去实现他,如果Client和Server是同一个进程,就直接调用这个方法,如果不是一个进程,这中间发生了什么 ,Client是如何调用Server方法的。

我们知道,远程调用需要用BinderProxy去完成的,这里面的Proxy就是,Proxy里的add如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
复制代码
Override
public int add(int a, int b) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(a);_data.writeInt(b);mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();_result = _reply.readInt();} finally {_reply.recycle();_data.recycle();}return _result;
}复制代码

它首先用Parcel把数据序列化了,然后调用了transact方法;这个transact到底做了什么呢?这个Proxy类在asInterface方法里面被创建,前面提到过,如果是Binder代理那么说明驱动返回的IBinder实际是BinderProxy, 因此我们的Proxy类里面的mRemote实际类型应该是BinderProxy;通过BinderProxy的transcact方法,把需要调用的方法,方法需要参数,方法的返回值都一同传给驱动,这时通信就交给驱动来完成了,这时Client就陷入内核态,Client进程add方法挂起等待返回,驱动经过一系列操作后唤醒Server进程,调用Server进程本地对象的onTranscact方法(由Server端线程池完成),我们再看Binder本地对象的onTranscact方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
复制代码
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_add: {data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);
}
复制代码

在Server进程里面,onTransact根据调用号(每个aidl都有一个编号,在跨进程的时候,不会传递函数,而是传递函数编号代表调用的是哪个函数)调用相关函数,在这个例子里面,调用了Binder本地对象的add方法,这个方法把结果返回给驱动,驱动唤起Client进程里面的线程,并把结果返回,于是一次进程调用就完成了。

至此,你应该对aidl里面,各个类和各个角色有一定的了解,他总是一种固定的模式,一个需要跨进程传递的对象,一定继承自Binder,如果是IBinder本地对象,一定实现了IInterface接口,如果是代理对象,一定是实现了IInterface接口,并持有IBinder对象引用.

Proxy和Stub不同,虽然都即是IInterface有是IBinder,不同的是stub采用继承(is IBinder),Proxy采用是组合(has IBinder),他们均实现了IInterface所有函数,不同的是,stub使用了策略模式虚函数,让子类实现函数,而Proxy使用了组合模式,为什么stub采用继承,而Proxy采用组合?事实上stub本身就是一个Binder,它本身就是一个能跨越进程边界传输的对象,所以它得继承IBinder实现transact这个函数从而得到跨越进程的能力(这个能力由驱动赋予)。Proxy类使用组合,是因为他不关心自己是什么,它也不需要跨越进程传输,它只需要拥有这个能力即可,要拥有这个能力,只需要保留一个对IBinder的引用。如果把这个过程做一个类比,在封建社会,Stub好比皇帝,可以号令天下,他生而具有这个权利(不要说宣扬封建迷信。。)如果一个人也想号令天下,可以,“挟天子以令诸侯”。为什么不自己去当皇帝,其一,一般情况没必要,当了皇帝其实限制也蛮多的是不是?我现在既能掌管天下,又能不受约束(Java单继承);其二,名不正言不顺啊,我本来特么就不是(Binder),你非要我是说不过去,搞不好还会造反。最后呢,如果想当皇帝也可以,那就是asBinder了。在Stub类里面,asBinder返回this,在Proxy里面返回的是持有的组合类IBinder的引用。

再去翻阅系统的ActivityManagerServer的源码,就知道哪一个类是什么角色了:IActivityManager是一个IInterface,它代表远程Service具有什么能力,ActivityManagerNative指的是Binder本地对象(类似AIDL工具生成的Stub类),这个类是抽象类,它的实现是ActivityManagerService;因此对于AMS的最终操作都会进入ActivityManagerService这个真正实现;同时如果仔细观察,ActivityManagerNative.java里面有一个非公开类ActivityManagerProxy, 它代表的就是Binder代理对象;是不是跟AIDL模型一模一样呢?那么ActivityManager是什么?他不过是一个管理类而已,可以看到真正的操作都是转发给ActivityManagerNative进而交给他的实现ActivityManagerService 完成的。,


参考 :Binder学习指南

Android进程间通信(IPC)机制Binder简要介绍和学习计划


相关文章:

如何用Asp判断你的网站的虚拟物理路径

appath方法 < p align"center" > < font size"4" face"Arial" > < b > The Physical path to this virtual website is: < /b > < /font > < font color"#FF0000" size"6" face"Aria…

总结:SQL的优缺点及与NoSQL对比

SQL在这里指的是关系型数据库&#xff0c;NoSQL指元组存储&#xff1f;

关于成功的因素-----谨记

引导语&#xff1a;一个人生活的环境&#xff0c;对他树立理想和取得成就有着重要的影响。坚忍&#xff0c;是成大事者的共同特征。没有足够的知识储备&#xff0c;一个人难以在工作和事业中取得突破性进展&#xff0c;难以向更高地位发展。 【意志力】 意志力是一种…

最常用的Linux命令

基础命令&#xff1a; &#xff08;一&#xff09;cd&#xff1a;进入文件目录 cd / #进入Linux根目录 cd ~ #进入当前用户Home目录&#xff0c;例如root用户 cd .. #进入上一层目录(根目录下没有上一层)&#xff0c;…

Delphi数据库开发之TTable篇1

既然是说Delphi中的SQL使用&#xff0c;肯定离不开TDataSet派生出来的DataSet控件&#xff08;应该说类比较确切些&#xff09;&#xff0c;这里我说的DataSet只是一个大类的称呼&#xff0c;并不是特指实际的TDataSet。在众多控件中其实可以根据如何操作数据分为两类。 其中一…

大型数据库入门

本文介绍大型数据库的概念及其性能决定因素&#xff0c;以及如何优化性能。 什么是大型数据库 -没有一个标准定义 -包含非常多元组(数据库行)的数据库&#xff0c;或者占用非常大的物理文件系统存储空间的数据库。 -占据TB量级的磁盘存储&#xff0c;包含数十亿表行。为什么需…

php安装pear和phpunit

php安装pear和phpunit http://hi.baidu.com/zjutxujie/item/7b08761f922df7476926bb2c 安装pear 下载go-pear.phar&#xff0c;放到phproot/pear目录下&#xff0c;地址http://pear.php.net/go-pear.phar。 管理员身份运行cmd&#xff0c;打开phproot&#xff0c;运行php -d p…

ResNets和Inception的理解

ResNets和Inception的理解 ResNet解析

软件体系结构风格(第5-8课时)

摘要&#xff1a;本文给出软件体系结构风格的定义&#xff0c;并介绍几种常用风格&#xff1a;管道过滤器、数据抽象和面向对象组织&#xff0c;基于事件的隐式调用&#xff0c;分层系统&#xff0c;仓库系统&#xff0c;过程控制环路 软件体系结构风格是什么 描述特定系统组织…

C#中调用Windows API时的数据类型对应关系

C#中调用Windows API时的数据类型对应关系 原文 C#中调用Windows API时的数据类型对应关系 BOOLSystem.Int32 BOOLEANSystem.Int32 BYTESystem.UInt16 CHARSystem.Int16 COLORREFSystem.UInt32 DWORDSystem.UInt32 DWORD32System.UInt32 DWORD64System.UInt64 …

linux下安装db2

最近研究了一下在 ubuntu下安装db2的过程&#xff0c;很快就完成安装&#xff0c;特贴出来供大家讨论&#xff0c;如有错误请多多指教。 注意&#xff1a;安装过程请使用root用户&#xff0c;否则会出现安装失败的情况&#xff1b; 安装过程&#xff1a; 准备工作&#xff1a; …

使用 SAX 解析器简化文档处理程序的编写

http://www-900.ibm.com/developerWorks/cn/xml/x-dochan.shtml有时候 SAX 文档处理程序的代码可能变得非常麻烦、结构性差而且难以维护&#xff0c;尤其是对于那些包含多种不同元素的复杂 XML 结构。本文介绍了应对这种问题的设计策略&#xff0c;从而有助于改进代码的质量和可…

软件体系结构风格之C/S,三层C/S,与BS

C/S的物理结构&#xff0c;其发展历程为(1)->(3)->(2)&#xff0c;本文接下来要介绍的C/S为(3),即胖客户端瘦服务器,服务器只管数据库&#xff0c;接下来要介绍的三层C/S为(2)&#xff0c;即客户端不胖不瘦。 C/S软件体系结构 背景&#xff1a;基于资源不对等&#xff0…

HDU-2020

绝对值排序 Problem Description输入n(n<100)个整数&#xff0c;按照绝对值从大到小排序后输出。题目保证对于每一个测试实例&#xff0c;所有的数的绝对值都不相等。 Input输入数据有多组&#xff0c;每组占一行&#xff0c;每行的第一个数字为n,接着是n个整数&#xff0c;…

Java归去来第2集:利用Eclipse创建Maven Web项目

一、前言 如果还不了解剧情&#xff0c;请返回第一集的剧情 Java归去来第1集&#xff1a;手动给Eclipse配置Maven环境 二、利用Eclipse创建Maven Web项目 选择File-New-Project 然后选择Maven-Maven Project 选择项目所在的工作空间目录&#xff0c;然后下一步 选择模…

vs2005什么时候能出正式版

2005试用版本出了很久了&#xff0c;不知道什么时候能出正式版&#xff0c;不会真得要推迟到2005年吧。企盼中…… 转载于:https://www.cnblogs.com/playboy2005/archive/2004/09/22/45563.html

人工神经网络是如何实现存算一体的

摘要 本文内容如题。 下图是一个神经元模型 可以将其抽象为一个数学函数yf(w1x1w2x2…wn*xnb)。也就是这个模型同时涵盖输入输出和进行计算。

编码能力的提升?_20131228

我在编程过程中发现&#xff0c;编码也是一很重要的能力&#xff0c;故花一点时间对其进行思考。 现将思考的成果与各位同行分享&#xff1b;我希望大家在看到以后&#xff0c;能提供宝贵的意见&#xff0c;先谢之&#xff01; 转载于:https://www.cnblogs.com/uestc999/p/3495…

Microsoft PowerToys for Windows XP

它们又卷土重来了&#xff01;PowerToys是由开发人员在软件产品正式发布并投放生产后相继编制的附加程序。它们的出现不仅使Windows体验更加兴味盎然&#xff0c;而且&#xff0c;还为Windows系统平添了许多功能特性。 说明&#xff1a;我们已为确保PowerToys的正常运行而付出了…

mysql主从配置读写分离笔记

第二次回头看了&#xff0c;第一次学完后感觉都会了&#xff0c;回头再看才发现什么都想不起来了。还得查资料再学习&#xff0c;虽然很简单。还是做个笔记吧&#xff01;笔记有点糙 就是自己看的因为主要是测试主从和读写分离 所以直接 yum install -y mariadb mariadb-server…

通用软件测试的6个角度

之后的课程都会围绕此展开 以QQ邮箱为例 外观界面测试 页面外观背景颜色、字体、字体格式、页面图案、动画、窗体布局功能测试 输入正确的用户名和密码可以登录&#xff0c;其他都不可性能测试 输入完邮箱的用户名和密码并单击登录按钮后&#xff0c;用户等待多长时间可以登录…

easyui源码翻译1.32--Messager(消息窗口)

前言 使用$.messager.defaults重写默认值对象。下载该插件翻译源码 消息窗口提供了不同的消息框风格&#xff0c;包含alert(警告框), confirm(确认框), prompt(提示框), progress(进度框)等。所有的消息框都是异步的。用户可以在交互消息之后使用回调函数去处理结果或做一些自己…

新闻网站个人中心(查询用户的关注信息)流程分析

1.获取当前用户登录状态 2.获取参数 用户的id user_id 3.获取新闻作者的id 4.如果新闻作者id和用户id 5.返回内容给前端转载于:https://www.cnblogs.com/zxt-cn/p/9158860.html

物联网技术与应用(第1-2课时)(cont.)

物联网的定义&#xff1a; 通过射频识别&#xff08;RFID&#xff09;、红外感应器、全球定位系统、激光扫描器等信息传感设备&#xff0c;按约定的协议&#xff0c;将任何物品通过有线与无线方式与互联网连接&#xff0c;进行通信和信息交换&#xff0c;以实现智能化识别、定位…

db2 基础语法

一、db2 基础 基本语法 注释&#xff1a;“--”&#xff08;两个减号&#xff09; 字符串连接&#xff1a;“||” 如set msg’aaaa’||’bbbb’&#xff0c;则msg为’aaaabbbb’ 字符串的引用&#xff1a;‘’&#xff08;一定用单引号&#xff09;&#xff0c;如果需要输入单引…

Repeater分页代码

//ASP.NET中的DataList和Repeater提供了简单快速的方法來显示,其间&#xff0c;我们可以使用<ItemTemplate>更是使我们能随心所欲的控制数据的排放样式&#xff01; //.可惜的是它们不像Datagrid那样&#xff0c;有内置的分页功能。 // //如何解决呢&#xff1f; // //其…

消失,只为一个人存在……

好像很久很久没有24小时挂在网上了…… 每天收到的短消息来源越来越单一…… 在朋友中消失&#xff0c;所有的来电和消息&#xff0c;都不愿意理会…… 朋友虽然很重要&#xff0c;可是现在只想为一个人存在。 请各位好朋友原谅&#xff0c; 我就是那种重色轻友的人嘛…… 转载…

统一建模语言——UML(第9-20课时)(cont.)

文章目录UML概述UML是什么UML能做什么为什么要学UMLUML构造需求模型用例建模技术绘制用例图识别执行者识别用例检查形式关联关系编写用例文档用例文档的组成部分详细说明检查用例模型状态图定义组成元素复杂一点活动图定义作用组成元素绘制技巧例顺序图定义作用组成元素UML构造…

xunsearch 迅搜初探

2014年1月2日 19:34:12 1 [rootlocalhost bin]# ./php /usr/local/lamp/xunsearch/sdk/php/util/Quest.php demo "俗话说 OR pid:1"2 在 3 条数据中&#xff0c;大约有 2 条包含 俗话说 OR pid:1 &#xff0c;第 1-2 条&#xff0c;用时&#xff1a;0.0036 秒。3 4 1…

Aria2打造属于自己的下载神器

请关注微信公众号( ?sharingplus) 我使用Aria2差不多已经2年了。在这段时间使用很多下载工具&#xff0c;最开始使用的是迅雷极速版 、后面各种原因不好使了。Free Download Manager、uTorrent、qBittorrent、BitComet、IDM等等全部折腾了一遍。各有千秋&#xff0c;在上面中使…