Bn Bp Binder native层关系
Servicemanager
源码在/frameworks/base/cmds/servicemanager/service_manager.c
编译成 systemmanager 可执行文件
systemmanager是以binder为主要通信手段,为系统各种各样服务的进行登记查询管理的服务。
系统服务的框架里有3个主要角色,服务提供者(server),服务使用者(client),和服务管理者(system manager),通信方式是Binder,在系统启动流程中的先后顺序是:服务管理者,服务提供者,服务使用者。
成为system manager
该进程会常驻在系统中,先通过binder_become_context_manager()注册为系统中服务管理器(本质上是通过ioctl对打开的/dev/binder的fd发送BINDER_SET_CONTEXT_MGR),然后通过binder_loop() 进入死循环,来持续为系统系统各种服务的查询/添加功能。各个服务本身运行在自己的进程中,但是通过systemmanager这样一个枢纽,提供真实服务的进程,将自己的服务向systemmanager注册,然后其他需要使用服务的进程通过先从/dev/binder中获取到systemmanager,然后再通过systemmanager查询并获取到真正想要的服务的stub,然后包装成binderPoxy(bpbinder),然后通过这个bpbinder与真正的服务进行IPC通信。
用在服务提供者和服务使用者角色中,与system manager 进行通信的模块:IPCThreadState 和 ProcessState
其他服务使用者通过在自己的进程中调用ProcessState::self()获得一个ProcessState对象(进程级单例,单例对象定义在binder/Static.cppg Process;)(一般是使用IPCThreadState对象时间接初始化,IPCThreadState的使用非常广泛,如Looper的native层,BPBinder中,bootanimation服务,app_process中当虚拟机状态发生改变后用来关闭binder的fd等,只要是native层,几乎都会使用到这个类来进行binder的IPC通信),在构造函数中ProcessState.cpp 中open_driver()打开/dev/binder 的fd,然后通过mmap映射这个fd的BINDER_VM_SIZE 大小的到自己进程中。
之后,往往通过调用joinThreadPool(isMain = 默认true)方法和binder的驱动进行交互并初始化,让当前进程在serviciemanager处注册服务,方法内部通过mOut和mIn 这2个Parcel数据对象(提供了将数据按规定格式(如Int32,float等)进行读写的方法,各种通信状态的记录等)来向binder收发数据,首先构造mOut写入Int32 BC_ENTER_LOOPER,然后进入一个循环中,先对mIn的数据处理,然后调用talkWithDriver()(本质上是通过ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)与binder通信,而bwr结构是又一次对mIn和mOut的数据转换,是直接和binder驱动进行IO的数据结构,一次这样的ioctl既可以用作向binder写数据,也可以从binder读数据,是否存在读的数据需要看上下文以及外部环境是否有信息发送过来) 来和binder驱动通信),获得状态对象,然后从mIn读取cmd,然后执行executeCommand(cmd);根据cmd的类型执行回馈处理命令,将mIn的数据出,将处理好的数据放入mOut打包,等下次talkWithDriver()的时候和binder回复以及接受新命令(mIn头部的int32大小的数据存放cmd)。joinThreadPool中的这个处理binder数据的循环会运行到executeCommand() 返回 TIMED_OUT 或者ECONNREFUSED等失败退出的状态为止,退出循环,最后通过向mOut写入 (BC_EXIT_LOOPER) 并调用talkWithDriver(false) 来结束通信。
SystemServer的分析,主要流程
Main()
加载 android_servers 动态库
1. 进入 init1(args) native方法(主要启动本机服务SurfaceFlinger(屏幕),AudioFlinger,传感器等,设置通过binder监听随着servicemanager死亡的而自杀)
2. 之后调用init2()启动android服务
a) 设置进程,线程优先级
b) 检查 sys.shutdown.requested, =1重启,或关机
c) 启动一堆核心服务,并注册(如PowerManagerService,ActivityManager等)
若设置了 vold.decrypt 为 ENCRYPTING 或 ENCRYPTED(加密状态) 则设置只运行 coreApp,以此启动packageManager
。。。服务。。。。
d) 启动一些需要UI的非核心服务,并注册,大多数服务类似am,驻留在当前system_service 进程里,不像c层服务,大多数是自己独立一个进程
e) 根据当前是否为安全模式,进行不同的初始化
f) 启动其他app 进程,如devicePolicy
g) 注册am中的一个回调systemReady,在回调中 调用一堆服务的 systemReady方法
3. 循环处理binder命令(注册了自己的服务?)
Init1 对应源码在
/frameworks/base/services/jni/com_android_server_SystemServer.cpp
省略源码+注释若干
下面是java层的system_server的总结
java层的system_server启动了并注册和启动了很多java层服务(各个服务自己有一条线程来维持与binder通信和执行业务),进入了一个Looper循环(作用?)
System_server 中的很多服务都是在java层初始化,并驻留在system_server进程中(通过一条单独处理binder通信的线程来维持运作,如activityManager),然后注册到serviceManager
而init中也有服务的概念,这些服务中有一部分不会注册到servcieManager如ueventd,healthd ,但也有一部分会启动后以c层代码的方式注册到服务,如bootanimation
所以说关于服务有3类
1. Init中启动的native层服务,不注册到serviceManager
2. Init中启动的native层服务,会注册到serviceManager
3. System_server中启动的java层服务,会注册到serviceManager
Binder
IPCThreadState中joinThreadPool 与binder通信某些地方有点类似与TCP通信,一方发送一个Command(字符串BC_*的命令),另一方接收到并处理完后,返回一个return(字符串BR_*)状态,类似TCP的发送某SEQ帧后,接收方收到后回馈一个对应的ACK帧
一次向servicemanager添加注册服务需要经过多层
Binder驱动层,binder驱动通信层,servicemanager业务层,具体的执行注册服务的函数。4层
关键通信函数及一些常量
Binder驱动,ProcessState, ioctl() |
IPCThreadState::executeCommand() |
svcmgr_handler() |
do_add_service() |
BINDER_VERSION BINDER_WRITE_READ----> BINDER_THREAD_EXIT BINDER_SET_CONTEXT_MGR …… |
BR_TRANSACTION-----------> …… |
SVC_MGR_GET_SERVICE SVC_MGR_CHECK_SERVICE SVC_MGR_ADD_SERVICE----> …… |
svc_can_register()呼叫者uid权限校验 …… 加入服务到svclist链表中 …… |
小结:
起点:
I##自定义INTERFACE服务类接口 : IInterface
。。。
上面画的线图,格式毁了,看截图把
浓缩:
IBinder有2个主要的子类BBinder 和 BpBinder
1.服务端继承BBinder实现transact用于在onTransact里根据cmd调用实际的接口实现的方法然后通过IPCThreadState的talkWithDriver写回结果给binder
2.客户端继承BpBinder实现transact用于根据调用的业务方法产生对应cmd和参数调用IPCThreadState::self()->transact(),然后通过waitForResponse() -> talkWithDriver() 和binder通信
3.Binder驱动和servicemanager做的工作主要是使用内存共享完成IPC,另外一个是在客户端获取服务的IBinder时,用BpBinder*的实现,而不是服务器端添加服务时给出的BBinder*。
4.当前是否服务端还是客户端?
从servicemanager通过binder通信获取到某个服务的IBinder指针后,是在interface_cast()调用的asInterface()中根据这个IBinder的queryLocalInterface() 返回IInterface* 是否为0指针来判断的
queryLocalInterface()返回是否为0指针
queryLocalInterface 是声明在IBinder中的一个虚方法,有一个默认实现(客户端中的情况)在Binder.cpp 中实现 IBinder::queryLocalInterface() (总是返回NULL)
queryLocalInterface 在服务端在BnInterface中覆盖默认的实现
服务端是调用方法IBinder->BBinder->BnInterface::queryLocalInterface(),查询用的 (用MediaPlayService举例)IMediaPlayService::descriptor == MediaPlayService::descriptor (MediaPlayService::descriptor是MediaPlayService通过继承IMediaPlayService得来,是同一个类中的成员),所以返回 this对象的指针地址,不为0
这一部分和java层的binder机制是类似的(为了方便程序员使用Binder,android提供的AIDL的主要功能就是在此层,根据自定义方法的数量生成等量的常量,用在BINDER_WRITE_READ -> BR_TRANSACTION 与binder通信时确定远端让本方调用某个本地方法的 COMMAND 常量,然后根据command常量调用对应的方法。类似svcmgr_handler()中做的工作。)
对象转换过程:
IserviceManager <- IBinder <- BpBinder
服务使用:
IPCThreadState,ProcessState,android::Thread, android::Thread::threadLoop(), binder. 这一切是有关联的
从binder – ProcessState - android::Thread – IPCThreadState
简要流程
Binder
打开/dev/binder ,用ioctl 循环的BINDER_WRITE_READ 进行数据读写并执行对应的命令
ProcessState
主要持有/dev/binder的fd,设置与binder通信的基本参数
分析一个 开机动画的例子 bootanimation
在void SurfaceFlinger::startBootAnim()中
调用property_set("ctl.start", "bootanim"); 通过属性服务 发送控制命令 启动bootanim 服务,播放启动动画,之后当SurfaceFlinger::bootFinished()启动完成后,调用property_set("service.bootanim.exit", "1"); 设置动画退出属性为1,bootanimation会轮询检测该标志,若读取到1,则退出播放动画。
- 打开/dev/binder,用ioctl 检查BINDER_VERSION,设置最大线程数量 BINDER_SET_MAX_THREADS
- startThreadPool() 打开线程池 -> spawnPooledThread(true)
1. new 一个PoolThread 线程对象(继承android::Thread ,class Thread : virtual public RefBase, android::Thread的线程功能实际上是用pthread实现的),设置线程名称为Binder Thread 1
2. 调用PoolThread 的run方法,实际上是调用了android::Thread::run() -> androidSetCreateThreadFunc() -> pthread_create() -> Thread::_threadLoop() -> PoolThread::readyToRun() -> PoolThread::threadLoop()
mCanCallJava = isMain = true
androidCreateThreadEtc()
默认是 gCreateThreadFn = androidCreateRawThreadEtc;
有个别地方 如 AndroidRuntime.cpp 中通过 androidSetCreateThreadFunc() 将 gCreateThreadFn 指向了 javaCreateThreadEtc() ,但其实际上做了一些对JVM的额外处理后又进入了androidCreateRawThreadEtc
而 androidCreateRawThreadEtc 的实现分为 LINUX 下的 PTHREAD 实现 和 WIN32_THREAD 实现,这里是LINUX的实现
最关键的则是int result = pthread_create(&thread, &attr, (android_pthread_entry)entryFunction, userData);
而入口函数entryFunction 则是在 Thread::run() 中指定的Thread::_threadLoop(),此时子线程才真正跑起来
之后做了一些初始化如gettid()等,进入了一个循环,第一次进入循环则调用readyToRun()虚方法(一般由继承Thread的子类自己根据业务逻辑实现,比如BootAnimation::readyToRun()中当图形绘制的初值化工作完成后返回NO_ERROR,否则返回NO_INIT),然后android Thread框架检测,如果是 NO_ERROR 并且exitPending()未返回true,那么就调用 虚方法threadLoop();(注意,该方法不是_threadLoop()),该方法内一般让子类实现关键的业务逻辑,如BootAnimation::threadLoop()中就在进入一个死循环,播放开机动画,直到exitPending()返回true(当调用了requestExit()后exitPending会返回true,本质上是将mExitPending 变量赋值为true)后退出循环停止播放动画。
因为这里的特定子类是PoolThread所以Thread::_threadLoop()这个框架方法调用了PoolThread::readyToRun(),但PoolThread没去重写它,所以默认返回NO_ERROR,继续,调用PoolThread::threadLoop(), 调用IPCThreadState::self()->joinThreadPool(mIsMain = true); ,self()方法 主要是通过pthread_getspecific 获取保存的线程私有数据中IPCThreadState 对象的指针,如果没有获取到则新建一个IPCThreradState 对象并用pthread_key_create() 和pthread_setspecific() 对象指针存入线程私有数据集合,然后进入joinThreadPool()
IPCThreadState的executeCommand() 主要实现了和通过binder通信的一些常用命令,如获取/释放 自身服务的BBinder对象指针,增/减引用计数器,这里主要是对BR_*的反馈命令进行处理。BC_*发给binder,binder或远端服务返回一个BR_*。
Bootnimation 被 new 出来后,被第一次引用时,会触发onFirstRef()的回调,这里是bootanimation对象业务逻辑的的入口,在里面调用了run() 启动Bootnimation这个继承了Thread类的子线程,并进入threadLoop()根据当前启动阶段展示开机动画
总结:
ProcessState – PoolThread – IPCThreadState 这几个类常用在底层做binder通信使用
常见用法:
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
- ProcessState::self(),创建ProcessState对象,打开binder驱动,并设置基本参数
- ProcessState::startThreadPool(), 启动一个PoolThread子线程,调用run()让子线程初始化并进入ProcessState::threadLoop() 该方法中调用IPCThreadState::self()->joinThreadPool(mIsMain) ,让子线程在一个循环内通过talkWithDriver() 和binder、servicemanager、服务进程进行通信,并用executeCommand() 根据具体的反馈cmd执行处理命令,让这个子线程成为与binder通信的专用线程。
ProcessState::startThreadPool() =
-> new PoolThread子线程 并让它
-> Thread::run()
-> PoolThread::threadLoop()
-> IPCThreadState::self()->joinThreadPool(mIsMain) 循环的与binder通信
- 主线程也调用了一次 IPCThreadState::self()->joinThreadPool(); 那么主线程也进入与binder通信并执行命令的循环中
.doc文件的格式直接复制过来,有些对不上,所以截图
小结:
起点:
I##自定义INTERFACE服务类接口 : IInterface
|
V
在.h文件中DECLARE_META_INTERF asBinder()
ACE(INTERFACE) 声明asInterface()
以及声明自定义的业务方法。
在.c 文件中使用
IMPLEMENT_META_INTERFACE 实现
AsInterface()方法
此处调用发起是,IPCThreadState
调用了joinThread后(PoolThread)
,在一个循环中处理binder发来
的消息,并进入executeCommand()
,其中cmd为BR_TRANSACTION的
case里,会调用the_context_object
法(在服务端的该方法实现是调用
onTransact,客户端的BpBinder
是直接与binder用ioctl通信)
^
|
实现BBinder::onTransact 传递给addService
根据传入参数解cmd(code)
真正实现接口(I##自定义 调用当前对象中自定义INTE
INTERFACE服务类接口)中 RFACE服务类对应的方法,拿
的方法的业务逻辑 到返回值后写入reply对象
^ß---------------------------/ ^
| |
自定义INTERFACE服务类 : Bn自定义INTERFACE服务 : BnInterface : I##自定义INTERFACE服务类接口:(IInterface) : BBinder : IBinder : RefBase
Bp自定义INTERFACE服务 : BpInterface : I##自定义INTERFACE服务类接口:(IInterface) : BpRefBase -- BpBinder : IBinder
| |
V V
实现INTERFACE方法,但 客户端拿到servicemanager通过binder返回过来的 保存binder返回的IBin
是通过调用内部保存的 IBinder后,调用asInterface转换为I##自定义 der(BpBinder)指针
IBinder(BpBinder) INTERFACE服务类接口对象,直接调用想用的业务方法
的transact方法通过 《----------------------- Bp自定义INTERFACE服务 中的实现
Binder通信(ioctl)
完成数据转发给服务
端BBinder的
onTransact处理并返回
浓缩:
IBinder有2个主要的子类BBinder 和 BpBinder
1.服务端继承BBinder实现transact用于在onTransact里根据cmd调用实际的接口实现的方法然后通过IPCThreadState的talkWithDriver写回结果给binder
2.客户端继承BpBinder实现transact用于根据调用的业务方法产生对应cmd和参数调用IPCThreadState::self()->transact(),然后通过waitForResponse() -> talkWithDriver() 和binder通信
3.Binder驱动和servicemanager做的工作主要是使用内存共享完成IPC,另外一个是在客户端获取服务的IBinder时,用BpBinder*的实现,而不是服务器端添加服务时给出的BBinder*。
4.当前是否服务端还是客户端?
从servicemanager通过binder通信获取到某个服务的IBinder指针后,是在interface_cast()调用的asInterface()中根据这个IBinder的queryLocalInterface() 返回IInterface* 是否为0指针来判断的
queryLocalInterface()返回是否为0指针
queryLocalInterface 是声明在IBinder中的一个虚方法,有一个默认实现(客户端中的情况)在Binder.cpp 中实现 IBinder::queryLocalInterface() (总是返回NULL)
queryLocalInterface 在服务端在BnInterface中覆盖默认的实现
服务端是调用方法IBinder->BBinder->BnInterface::queryLocalInterface(),查询用的 (用MediaPlayService举例)IMediaPlayService::descriptor == MediaPlayService::descriptor (MediaPlayService::descriptor是MediaPlayService通过继承IMediaPlayService得来,是同一个类中的成员),所以返回 this对象的指针地址,不为0
这一部分和java层的binder机制是类似的(为了方便程序员使用Binder,android提供的AIDL的主要功能就是在此层,根据自定义方法的数量生成等量的常量,用在BINDER_WRITE_READ -> BR_TRANSACTION 与binder通信时确定远端让本方调用某个本地方法的 COMMAND 常量,然后根据command常量调用对应的方法。类似svcmgr_handler()中做的工作。)
对象转换过程:
IserviceManager <- IBinder <- BpBinder
服务注册:
服务使用:
IPCThreadState,ProcessState,android::Thread, android::Thread::threadLoop(), binder. 这一切是有关联的
从binder – ProcessState - android::Thread – IPCThreadState
简要流程
Binder
打开/dev/binder ,用ioctl 循环的BINDER_WRITE_READ 进行数据读写并执行对应的命令
ProcessState
主要持有/dev/binder的fd,设置与binder通信的基本参数
分析一个 开机动画的例子 bootanimation
在void SurfaceFlinger::startBootAnim()中
调用property_set("ctl.start", "bootanim"); 通过属性服务 发送控制命令 启动bootanim 服务,播放启动动画,之后当SurfaceFlinger::bootFinished()启动完成后,调用property_set("service.bootanim.exit", "1"); 设置动画退出属性为1,bootanimation会轮询检测该标志,若读取到1,则退出播放动画。
1. 打开/dev/binder,用ioctl 检查BINDER_VERSION,设置最大线程数量 BINDER_SET_MAX_THREADS
2. startThreadPool() 打开线程池 -> spawnPooledThread(true)
1. new 一个PoolThread 线程对象(继承android::Thread ,class Thread : virtual public RefBase, android::Thread的线程功能实际上是用pthread实现的),设置线程名称为Binder Thread 1
2. 调用PoolThread 的run方法,实际上是调用了android::Thread::run() -> androidSetCreateThreadFunc() -> pthread_create() -> Thread::_threadLoop() -> PoolThread::readyToRun() -> PoolThread::threadLoop()
mCanCallJava = isMain = true
androidCreateThreadEtc()
默认是 gCreateThreadFn = androidCreateRawThreadEtc;
有个别地方 如 AndroidRuntime.cpp 中通过 androidSetCreateThreadFunc() 将 gCreateThreadFn 指向了 javaCreateThreadEtc() ,但其实际上做了一些对JVM的额外处理后又进入了androidCreateRawThreadEtc
而 androidCreateRawThreadEtc 的实现分为 LINUX 下的 PTHREAD 实现 和 WIN32_THREAD 实现,这里是LINUX的实现
最关键的则是int result = pthread_create(&thread, &attr, (android_pthread_entry)entryFunction, userData);
而入口函数entryFunction 则是在 Thread::run() 中指定的Thread::_threadLoop(),此时子线程才真正跑起来
之后做了一些初始化如gettid()等,进入了一个循环,第一次进入循环则调用readyToRun()虚方法(一般由继承Thread的子类自己根据业务逻辑实现,比如BootAnimation::readyToRun()中当图形绘制的初值化工作完成后返回NO_ERROR,否则返回NO_INIT),然后android Thread框架检测,如果是 NO_ERROR 并且exitPending()未返回true,那么就调用 虚方法threadLoop();(注意,该方法不是_threadLoop()),该方法内一般让子类实现关键的业务逻辑,如BootAnimation::threadLoop()中就在进入一个死循环,播放开机动画,直到exitPending()返回true(当调用了requestExit()后exitPending会返回true,本质上是将mExitPending 变量赋值为true)后退出循环停止播放动画。
因为这里的特定子类是PoolThread所以Thread::_threadLoop()这个框架方法调用了PoolThread::readyToRun(),但PoolThread没去重写它,所以默认返回NO_ERROR,继续,调用PoolThread::threadLoop(), 调用IPCThreadState::self()->joinThreadPool(mIsMain = true); ,self()方法 主要是通过pthread_getspecific 获取保存的线程私有数据中IPCThreadState 对象的指针,如果没有获取到则新建一个IPCThreradState 对象并用pthread_key_create() 和pthread_setspecific() 对象指针存入线程私有数据集合,然后进入joinThreadPool()
IPCThreadState的executeCommand() 主要实现了和通过binder通信的一些常用命令,如获取/释放 自身服务的BBinder对象指针,增/减引用计数器,这里主要是对BR_*的反馈命令进行处理。BC_*发给binder,binder或远端服务返回一个BR_*。
Bootnimation 被 new 出来后,被第一次引用时,会触发onFirstRef()的回调,这里是bootanimation对象业务逻辑的的入口,在里面调用了run() 启动Bootnimation这个继承了Thread类的子线程,并进入threadLoop()根据当前启动阶段展示开机动画
总结:
ProcessState – PoolThread – IPCThreadState 这几个类常用在底层做binder通信使用
常见用法:
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
1. ProcessState::self(),创建ProcessState对象,打开binder驱动,并设置基本参数
2. ProcessState::startThreadPool(), 启动一个PoolThread子线程,调用run()让子线程初始化并进入ProcessState::threadLoop() 该方法中调用IPCThreadState::self()->joinThreadPool(mIsMain) ,让子线程在一个循环内通过talkWithDriver() 和binder、servicemanager、服务进程进行通信,并用executeCommand() 根据具体的反馈cmd执行处理命令,让这个子线程成为与binder通信的专用线程。
ProcessState::startThreadPool() =
-> new PoolThread子线程 并让它
-> Thread::run()
-> PoolThread::threadLoop()
-> IPCThreadState::self()->joinThreadPool(mIsMain) 循环的与binder通信
3. 主线程也调用了一次 IPCThreadState::self()->joinThreadPool(); 那么主线程也进入与binder通信并执行命令的循环中
Bn Bp Binder native层关系的更多相关文章
- Binder Native 层(二)
Binder 框架及 Native 层 Binder机制使本地对象可以像操作当前对象一样调用远程对象,可以使不同的进程间互相通信.Binder 使用 Client/Server 架构,客户端通过服务端 ...
- Android Framework 分析---2消息机制Native层
在Android的消息机制中.不仅提供了供Application 开发使用的java的消息循环.事实上java的机制终于还是靠native来实现的.在native不仅提供一套消息传递和处理的机制,还提 ...
- Native层和so接口和Java层
一.Java层加载so文件 Android在Java层加载so的接口是System.loadLibrary()逐级调用的过程: System.loadLibrary()系统源码: 987 pub ...
- Android Java层,Native层,Lib层打印Log简介【转】
本文转载自:https://blog.csdn.net/AndroidMage/article/details/52225068 说明: 这里我根据个人工作情况说明在各个层打印log.如有问题欢迎拍砖 ...
- 在Android Native层中创建Java虚拟机实例
前言 Android应用中JNI代码,是作为本地方法运行的.而大部分情况下,这些JNI方法均需要传递Dalvik虚拟机实例作为第一个参数.例如,你需要用虚拟机实例来创建jstring和其他的Java对 ...
- Android Native层异步消息处理框架
*本文系作者工作学习总结,尚有不完善及理解不恰当之处,欢迎批评指正* 一.前言 在NuPlayer中,可以发现许多类似于下面的代码: //============================== ...
- 在Visual Studio中使用层关系图描述系统架构、技术栈
当需要描述项目的架构或技术栈的时候,可以考虑使用层关系图. 在解决方案下添加一个名称为"TailspinToys.DesignModel"的建模项目. 在新建的建模项目下添加一个名 ...
- Android逆向之旅---Native层的Hook神器Cydia Substrate使用详解
一.前言 在之前已经介绍过了Android中一款hook神器Xposed,那个框架使用非常简单,方法也就那几个,其实最主要的是我们如何找到一个想要hook的应用的那个突破点.需要逆向分析app即可.不 ...
- SQLite数据库学习小结——native层实现
1. SQlite概述 SQLite是一款轻量.快速.跨平台的嵌入式数据库,是遵守ACID(注:ACID指数据库事务正确执行的四个基本要素的缩写.包含:原子性(Atomicity).一致性(Consi ...
随机推荐
- Linux系统学习 四、网络基础—互联网概述,互联网接入方式
互联网概述 WWW:万维网 FTP:文件传输协议 E-MAIL:电子邮件 WWW 典型的C/S架构 URL:统一资源定位 协议+域名或IP:端口+网页路径+网页名 http://www.xxx.com ...
- 201871010110 - 李华 《面向对象程序设计(java)》第一周学习总结
项目 内容 <面向对象程序设计(java)> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://i.cnblogs.com/Ed ...
- python的imread、newaxis
一:imread 用来读取图片,返回一个numpy.ndarray类型的多维数组,具有两个参数: 参数1 filename, 读取的图片文件名,可以使用相对路径或者绝对路径,但必须带完整的文件扩展名( ...
- 【2019.10.7 CCF-CSP-2019模拟赛 T2】绝对值(abs)(线段树细节题)
找规律 设\(p_i=a_{i+1}-a_i\),则答案就是\(\sum_{i=1}^{n-1}p_i\). 考虑若将\(a_i\)加上\(x\)(边界情况特殊考虑),就相当于是将\(p_{i-1}\ ...
- linux学习之Ubuntu
查看自己的ubuntu版本,输入以下命令(我的都是在root用户下的,在普通用户要使用sudo)第一行的lsb是因为没有安装LSB,安装之后就不会出现这个东西.LSB(Linux Standards ...
- 多模块springboot项目启动访问不了jsp页面
被springboot项目maven打包.启动.访问折腾的头都大了,一步一个坑,在解决了所有的问题之后,那种欣喜若狂的心情只有自己能体会,决定要好好记录一下了,废话不多说了,直接进入正题. 问题和排查 ...
- 【转载】修改Windows下键盘按键对应功能的一些方案
原文见:https://sites.google.com/site/xiangyangsite/home/technical-tips/windows-tips/multi_media_key_cus ...
- mysql 优化之 is null ,is not null 索引使用测试
关于mysql优化部分,有很多网友说尽量避免使用is null, is not null,select * 等,会导致索引失效,性能降低?那是否一定收到影响呢?真的就不会使用索引了吗? 本文的测试数据 ...
- ElasticSearch安装中文分词器IK
1.安装IK分词器,下载对应版本的插件,elasticsearch-analysis-ik中文分词器的开发者一直进行维护的,对应着elasticsearch的版本,所以选择好自己的版本即可.IKAna ...
- WPF后台代码实现TextBlock滚动条
方法一: 常规的WPF操作: <ScrollViewer Width=" VerticalScrollBarVisibility="Auto" Horizontal ...