前言

本来想洋洋洒洒写一篇形象生动的Binder原理的文章,再配上我这个灵魂画手的图画,但是再经过了对驱动内核的研究后,我决定了,我自首,我这太年少了 我,太懵懂了,我没成想这Binder机制这么厉害!

作为一名Android开发,常年使用java开发的人来说,C实在是太不友好了…但是Binder机制对于整个Android系统来说,你难道没有感觉到,Binder是多么地重要~~

所以我作为一名Android开发人员,对于C语言和Linux内核也仅仅停留在一些理论上的人来说,我想通过我的理解来说分析一下Binder

#概念普及
我相信能找到这篇文章的人,基本上都有一个常识就是,Binder是一种进程间用来通信的机制。由于Android系统基于Linux内核,因此有必要了解相关知识。接下来就普及一下理论概念,拥有的一定的理论基础才能通晓文章的含义

  • Linux进程
    进程定义:进程是受操作系统管理的基本单元,是一个具有独立功能的程序的一次运行活动。其实进程有很多的定义,这个只是其中一种,那么我都知道android中的app就是一个程序,一个运行中的app就会至少对应一个进程,进程是一种动态的概念,程序是一种静态的概念。

  • 进程隔离
    进程隔离是为保护操作系统中进程互不干扰而设计的一组不同硬件和软件的技术。这个技术是为了避免进程A写入进程B的情况发生。 进程的隔离实现,使用了虚拟地址空间。进程A的虚拟地址和进程B的虚拟地址不同,这样就防止进程A将数据信息写入进程B。以上来自维基百科;操作系统的不同进程之间,数据不共享;对于每个进程来说,它都天真地以为自己独享了整个系统,完全不知道其他进程的存在;(有关虚拟地址,请自行查阅)因此一个进程需要与另外一个进程通信,需要某种系统机制才能完成。

  • 内核空间(Kernel Space)/用户空间(User Space)
    详细解释可以参考Kernel Space Definition;简单理解如下:
    Linux Kernel是操作系统的核心,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。对于Kernel这么一个高安全级别的东西,显然是不容许其它的应用程序随便调用或访问的,所以需要对Kernel提供一定的保护机制,这个保护机制用来告诉那些应用程序,你只可以访问某些许可的资源,不许可的资源是拒绝被访问的,于是就把Kernel和上层的应用程序抽像的隔离开,分别称之为Kernel Space和User Space。

  • Linux进程空间分配
    现在操作系统都是采用的虚拟存储器,对于 32 位系统而言,它的寻址空间(虚拟存储空间)就是 2 的 32 次方,也就是 4GB。Linux的虚拟地址空间范围为0~4G,Linux内核在逻辑上将这4G字节的空间分为两部分,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF)供各个进程使用,称为“用户空间。因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。

  • 共享资源
    看过上面我们知道内核由系统内的进程共享,而且我们应该也知道,进程在各自独立的地址空间中运行,进程间的资源是不共享的,进程间如果想要共享资源,想像在同一进程中那样调用方法,传递参数就需要使用一些手段,进程之间共享数据需要用mmap或者进程间通信机制。

  • 系统调用/内核态/用户态
    虽然从逻辑上抽离出用户空间和内核空间;但是不可避免的的是,总有那么一些用户空间需要访问内核的资源;比如应用程序访问文件,网络是很常见的事情,怎么办呢?
    Kernel space can be accessed by user processes only through the use of system calls.
    翻译过来就是用户空间访问内核空间的唯一方式就是系统调用;通过这个统一入口接口,所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。用户软件良莠不齐,要是它们乱搞把系统玩坏了怎么办?因此对于某些特权操作必须交给安全可靠的内核来执行。
    主要的系统调用就是 copy_from_user 和 copy_to_user两个。
    当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)此时处理器处于特权级最高的(0级)内核代码中执行。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。处理器在特权等级高的时候才能执行那些特权CPU指令。

  • 内核模块/驱动
    通过系统调用,用户空间可以访问内核空间,那么如果一个用户空间想与另外一个用户空间进行通信怎么办呢?很自然想到的是让操作系统内核添加支持;传统的Linux通信机制,比如Socket,管道等都是内核支持的;但是Binder并不是Linux内核的一部分,它是怎么做到访问内核空间的呢?Linux的动态可加载内核模块(Loadable Kernel Module,LKM)机制解决了这个问题;模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。这样,Android系统可以通过添加一个内核模块运行在内核空间,用户进程之间的通过这个模块作为桥梁,就可以完成通信了。
    在Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动;

  • Linux现有手段
    Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection):
    管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)

#Binder优势,Android为什么采用Binder?
通过上面的概念普及,相信大家心里面应该,对于进程间通信有了一定的了解,但是我们这篇说的Binder并不是Linux进程间通信的传统手段,而是Android开发的一套新的进程间通信机制,既然有了传统的手段不用,非要自己弄,那么Binder一定相对于其它的进程间通信方法在Android平台上有一定优势。

  1. 复杂度
    目前linux支持的IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。当然也可以在这些底层机制上架设一套协议来实现Client-Server通信,但这样增加了系统的复杂性,在手机这种条件复杂,资源稀缺的环境下可靠性也难以保证。

  2. 效率
    socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中(copy_form_user),然后再从内核缓存区拷贝到接收方缓存区(copy_to_user),至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。
    Binder进程间通信机制在进程间传输数据时,只需要执行一次数据拷贝操作,因此,它不仅提高了效率,而且节省了时间。

  3. 安全性
    终端用户不希望从网上下载的程序在不知情的情况下偷窥隐私数据,连接无线网络,长期操作底层设备导致电池很快耗尽等等。传统IPC没有任何安全措施,完全依赖上层协议来确保。首先传统IPC的接收方无法获得对方进程可靠的UID和PID(用户ID进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。使用传统IPC只能由用户在数据包里填入UID和PID,但这样不可靠,容易被恶意程序利用。可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。比如命名管道的名称,systemV的键值,socket的ip地址或文件名都是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。

基于以上原因,Android需要建立一套新的IPC机制来满足系统对通信方式,传输性能和安全性的要求,这就是Binder。Binder基于Client-Server通信模式,传输过程只需一次拷贝,为发送发添加UID/PID身份,既支持实名Binder也支持匿名Binder,安全性高。
当然以上都是别人说的,至于为什么安全性高,为什么只需要一次拷贝等等,这些的原理我们都不清楚,那么通过我这么些天的学习,来给大家揭开其中一部分原理。

#Binder概念
面向对象思想的引入将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。最诱人的是,这个引用和java里引用一样既可以是强类型,也可以是弱类型,而且可以从一个进程传给其它进程,让大家都能访问同一Server,就象将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的Binder对象以及星罗棋布的引用仿佛粘接各个应用程序的胶水,这也是Binder在英文里的原意。

Binder使用Client-Server通信方式:一个进程作为Server提供诸如视频/音频解码,视频捕获,地址本查询,网络连接等服务;多个进程作为Client向Server发起服务请求,获得所需要的服务。要想实现Client-Server通信据必须实现以下两点:一是server必须有确定的访问接入点或者说地址来接受Client的请求,并且Client可以通过某种途径获知Server的地址;二是制定Command-Reply协议来传输数据。例如在网络通信中Server的访问接入点就是Server主机的IP地址+端口号,传输协议为TCP协议。对Binder而言,Binder可以看成Server提供的实现某个特定服务的访问接入点, Client通过这个‘地址’向Server发送请求来使用该服务;对Client而言,Binder可以看成是通向Server的管道入口,要想和某个Server通信首先必须建立这个管道并获得管道入口。

#Binder进程间通信机制构成。

Binder内核驱动、Client端进程、Server端进程、Service Manager进程,以及要实现通信,Google帮我们封装了一个Binder进程间通信库,辅助我们实现进程间通信。

接下来我们单独分析。

###Binder内核驱动程序
虽然翻译成驱动,但是它并不和硬件打交道,只是说原理类似驱动,既然提到内核,那么大家也就知道Binder内核驱动所在的空间,通过上面关于Linux进程空间的概念普及,我们知道进程可以通过系统调用访问内核,并且由系统内所有进程共享。

Binder内核驱动程序初始化时都做了些什么?
注意这里是初始化。接下来是时候展现我这个灵魂画手鬼斧神工般的技术了。
这里写图片描述

通过图片我们可以发现Binder初始化做的事情就是再设备上创建了一个目录,一个文件。

/proc/binder/proc目录
每一个使用了Binder进程间通信机制的进程在该目录下都对应有一个文件,这些文件以进程ID来命名,通过他们就可以读取到各个进程的***Binder线程池***、Binder实体对象、***Binder引用对象***以及***内核缓冲区***的信息。
好家伙,一个目录的作用就涉及到了N多个不知道的概念,放心既然是概念普及,我以后一定会讲解到,但是大家一定要记住这些概念的名称,别当我突然提起的时候一脸懵逼。

/dev/binder设备文件
这个设备文件的操作方法列表是由全局变量binder_fops指定的,我们不管是谁指定的,我们知道这个设备文件里有操作方法。

  • 打开方法binder_open

  • 内存映射binder_mmap

  • IO控制binder_ioctl

这3个操作方法,当被调用的时候Binder内核驱动将做哪些事情呢?内核嘛都是C/C++嘛,所以这些方法都选C!
我这里根据我学习的资料来简单的描述一下吧!当然这些方法就是Binder内核的奥秘了,不可能通过我的短暂学习就理解透彻,更不可能三言两语就说的清楚,但是希望能在大家的脑海中留下一些理论概念。既然都是C/C++我就不带大家看代码了,以java面向对象的思维去分析它们都干了什么!

##打开方法binder_open

  1. 一个进程在使用Binder进程间通信机制之前,首先就会调用这个函数来打开设备文件/dev/binder来获得一个文件描述,然后才能通过这个文件描述和Binder驱动程序交互,继而执行Binder进程间通信。

  2. Binder驱动程序会在内核中创建一个binder_proc结构体proc。结构体大家当对象理解就好了。并将proc加入到一个hash队列binder_procs中,Binder驱动程序将所有打开了设备文件/dev/binder的进程都加入到全局hash队列binder_procs中,通过遍历这个hash队列就知道系统当前有多少个进程在使用Binder进程间通信机制。

  3. 文件描述和结构体proc关联在一起,使用文件描述作为参数调用mmap和ioctl,内核就能找到proc结构体。

友情提示:一个进程想要进行进程间通信,一定会在某个逻辑中调用这个方法
废话不多说,继续上图
这里写图片描述

##内存映射binder_mmap
mmap前面见过吧,不是mmp啊,没学习过相关概念的人,相信是没办法理解什么是内存映射的,我就是其中之一,所以我度娘了一下,后来经过一系列的学习,对这个内存映射有了一定理解,还记得前面Linux的进程空间吗?不理解的可以自行度娘,这个就简单普及一下

Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap()
是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。

  1. 进程打开了设备文件/dev/binder之后,还必须要调用函数mmap把这个设备文件映射到进程的地址空间,映射到进程的地址空间,映射到进程的地址空间,重要的事情说三遍,然后才能使用Binder进程间通信机制。映射到进程的地址空间是为了啥?

  2. 为了为进程分配内核缓冲区,以便用来传输进程间通信的数据,知识点到了,内核缓冲区,我理解就是一块内存。当进程调用函数mmap将设备文件/dev/binder映射到自己的地址空间时,就会分配内核缓冲区,Binder驱动程序为进程分配的内核缓冲区有两个地址,其中一个是用户空间地址,由参数vma所指向的一个vm_area_struct结构体来描述;另一个地址是内核空间地址,由变量area所指向的一个vm_struct结构体来描述。进程通过用户空间地址来访问这块内存缓冲区的内容,而Binder驱动程序通过内核空间地址来访问这块内核缓冲区内容,由于它们是连续的,并且起始值相差一个固定值,因此,只要知道其中一个地址,就可以方便地计算另外一个地址。

  3. 将内核缓冲区的信息和binder_proc关联在一起

####结合内核缓冲区解读效率优势的秘密
用户空间、内核空间的概念前面我已经说过了,这里出现了吧,能明白一点吧,还记得我们谈论的Binder机制的优势之一效率吗?通过上面的描述,我们可以将内核缓冲区想象成一个共享的内存空间,Binder驱动程序和进程都可以访问,但是注意进程只可以读这块内存,Binder驱动才有管理的权利,当Binder驱动将其他进程的数据通过拷贝(copy_from_user)写入这个缓冲区,当前进程就可以直接获取到数据了,无需拷贝(因为这块内存可以简单当成进程和内核的共享内存),所以Binder机制只进行了一次的数据拷贝,这大大的节省了效率和时间。

知识点:Binder驱动程序会为进行进程间通信的进程创建相对应的内核缓冲区。
灵魂画手前来拜访
这里写图片描述

接下来是一段引用的段落,引用地址,这个描述比我详细,如果大家理解了我说的可以跳过引用段。
http://blog.csdn.net/universus/article/details/6211589

暂且撇开Binder,考虑一下传统的IPC方式中,数据是怎样从发送端到达接收端的呢?通常的做法是,发送方将准备好的数据存放在缓存区中,调用API通过系统调用进入内核中。内核服务程序在内核空间分配内存,将数据从发送方缓存区复制到内核缓存区中。接收方读数据时也要提供一块缓存区,内核将数据从内核缓存区拷贝到接收方提供的缓存区中并唤醒接收线程,完成一次数据发送。这种存储-转发机制有两个缺陷:首先是效率低下,需要做两次拷贝:用户空间->内核空间->用户空间。Linux使用copy_from_user()和copy_to_user()实现这两个跨空间拷贝,在此过程中如果使用了高端内存(high memory),这种拷贝需要临时建立/取消页面映射,造成性能损失。其次是接收数据的缓存要由接收方提供,可接收方不知道到底要多大的缓存才够用,只能开辟尽量大的空间或先调用API接收消息头获得消息体大小,再开辟适当的空间接收消息体。两种做法都有不足,不是浪费空间就是浪费时间。

Binder采用一种全新策略:由Binder驱动负责管理数据接收缓存。我们注意到Binder驱动实现了mmap()系统调用,这对字符设备是比较特殊的,因为mmap()通常用在有物理存储介质的文件系统上,而象Binder这样没有物理介质,纯粹用来通信的字符设备没必要支持mmap()。Binder驱动当然不是为了在物理介质和用户空间做映射,而是用来创建数据接收的缓存空间。先看mmap()是如何使用的:

fd = open("/dev/binder", O_RDWR);

mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);

这样Binder的接收方就有了一片大小为MAP_SIZE的接收缓存区。mmap()的返回值是内存映射在用户空间的地址,不过这段空间是由驱动管理,用户不必也不能直接访问(映射类型为PROT_READ,只读映射)。

接收缓存区映射好后就可以做为缓存池接收和存放数据了。前面说过,接收数据包的结构为binder_transaction_data,但这只是消息头,真正的有效负荷位于data.buffer所指向的内存中。这片内存不需要接收方提供,恰恰是来自mmap()映射的这片缓存池。在数据从发送方向接收方拷贝时,驱动会根据发送数据包的大小,使用最佳匹配算法从缓存池中找到一块大小合适的空间,将数据从发送缓存区复制过来。要注意的是,存放binder_transaction_data结构本身以及表4中所有消息的内存空间还是得由接收者提供,但这些数据大小固定,数量也不多,不会给接收方造成不便。映射的缓存池要足够大,因为接收方的线程池可能会同时处理多条并发的交互,每条交互都需要从缓存池中获取目的存储区,一旦缓存池耗竭将产生导致无法预期的后果。

有分配必然有释放。接收方在处理完数据包后,就要通知驱动释放data.buffer所指向的内存区。在介绍Binder协议时已经提到,这是由命令BC_FREE_BUFFER完成的。

通过上面介绍可以看到,驱动为接收方分担了最为繁琐的任务:分配/释放大小不等,难以预测的有效负荷缓存区,而接收方只需要提供缓存来存放大小固定,最大空间可以预测的消息头即可。在效率上,由于mmap()分配的内存是映射在接收方用户空间里的,所有总体效果就相当于对有效负荷数据做了一次从发送方用户空间到接收方用户空间的直接数据拷贝,省去了内核中暂存这个步骤,提升了一倍的性能。顺便再提一点,Linux内核实际上没有从一个用户空间到另一个用户空间直接拷贝的函数,需要先用copy_from_user()拷贝到内核空间,再用copy_to_user()拷贝到另一个用户空间。为了实现用户空间到用户空间的拷贝,mmap()分配的内存除了映射进了接收方进程里,还映射进了内核空间。所以调用copy_from_user()将数据拷贝进内核空间也相当于拷贝进了接收方的用户空间,这就是Binder只需一次拷贝的‘秘密’。

##系统调用IO控制binder_ioctl
说实在的这个方法就要通过具体的情景分析了,既然说到了IO那肯定和数据传输有关,读啊,写啊,来啊,快活啊!其实我对于这部分的了解及其有限,但是我们可以知道它都做了什么。

  1. 系统调用,系统调用是干什么的,上面我已经说过了,进程可以通过系统调用访问内核,那么也就是说这个方法,可以让进程访问内核,自由的飞翔~ 这里实际到Binder线程池的概念。以后会说。

  2. IO命令控制,进程与内核间的很多协议通信传输就是依靠这个方法的case来进行相应的处理。

至此Binder内核部分分析暂时告于段落,这里只是分析了Binder驱动程序初始化做了什么,当然Binder驱动程序做的事情远远不止这些!

###Binder进程间通信库
Android系统在应用程序框架层中将各种Binder驱动程序操作封装成一个Binder库,这样进程就可以方便地调用Binder库提供的接口来实现进程间通信。大家如果开发中想要使用让程序可以进行进程间通信,Android提供了很多的方法,其中基于Binder原理实现的进程间通信方式有Content Provider、Messenger、AIDL等,其中Messenger是基于AIDL的封装,Content Provider和AIDL底层都是Binder,所以说这三者都是基于Binder的。如果不知道AIDL如何使用的,可以先去了解一下!

进程间通信,既然需要跨进程通信那么肯定至少需要两个进程了,Binder机制是C/S架构的,什么是是C/S呢,就是Clinet端和Server端,一个是客户端,一个是提供服务服务端,如果想使用通信需要在客户端和服务端创建相应的Binder对象。这里说一下Client端和Server端,大家还需要知道这样一个理论概念,就是Client端和Server端是一个相对的概念,Client端请求服务,Server端提供服务,Client端同样可以提供服务(那么这个时候他就是Server端),Server端也同样可以请求服务(那么这个时候他就是Client端),只是在一次进程间通信过程中扮演的角色而已,每个进程的身份角色会根据自身的需要而发生变化。

这里继续普及概念啊,通信库涉及到的概念:
Service组件(Server端)和Client组件(Client端)分别使用模板类BnIterface和BpInterface来描述,Bn我想应该是Binder native的缩写,而Bp应该就是Binder Proxy的缩写,前者称为Binder本地对象,后者称为Binder代理对象,实际上进程间通信就是通过Binder代理对象发送进程间通信请求,通过Binder驱动等一系列操作,最后由Binder本地对象来处理请求,完成进程间通信。在使用Binder库开发Service组件和Client组件时,除了要定义Service组件接口之外,还必须要实现一个Binder本地对象和一个Binder代理对象,而如何获取Binder代理对象或者注册一个Binder本地对象就需要Service Manager的协助

  • 模板类BnIterface

模板类BnIterface继承自BBinder类,BBinder类有两个重要的成员函数transact和OnTransact,当一个Binder代理对象通过Binder驱动程序向一个Binder本地对象发出一个进程间通信请求时,Binder驱动程序就会调用Binder本地对象的成员函数transact来处理该请求。OnTransact是由BBinder的子类来实现的,它就是负责分发和业务相关的进程间通信请求,比如你要提供什么服务,服务里想做些什么,就是在这里写逻辑的。BBinder类又继承了IBinder类,而后者有继承了RefBase类。

  • 模板类BpInterface

模板类BpInterface继承自BpRefBase类,BprefBase类也继承了RefBase类,BpRefBase类有一个重要的成员变量mRemote,它指向一个BpBinder对象(Binder代理对象),可以用通过成员函数remote来获取。

  • BpBinder类

BpBinder实现了BpRefBase类的进程间通信接口,BpBinder成员变量mHandle是一个整数,表示一个Client组件的句柄值,每一个Client组件在Binder驱动程序中都对应一个Binder引用对象,而每一个Binder引用对象都有一个句柄值,这里又出现一个句柄值概念,句柄值是和内存地址息息相关的,通过句柄值就可以在列表中找到Binder引用对象。其中Client组件就是通过这个句柄值来和Binder驱动程序中的Binder引用对象建立对应关系的。BpBinder类成员函数transact用来发送进程间通信请求。

  • Binder线程池

看到这里有没有唤醒你的记忆呢,Binder本地和Binder代理都简单说过了,我相信这个概念大家不会不明白,线程池线程池,就是线程的池子咯,每一个使用了Binder进程间通信机制的进程都有一个Binder线程池们用来处理进程间通信请求,这个Binder线程池由Binder驱动程序来维护,还记的之前打开/dev/binder设备文件创建的结构体binder_proc吗,它的成员变量threads就是一个红黑树的根节点,它以线程ID作为关键字来组织一个进程的Binder线程池。进程可以调用ioctl将一个线程注册到Binder驱动中。

  • IPCThreadState类

无论是BBinder类(Binder本地对象),还是BpBinder类(Binder代理对象),它们都是通过IPCThreadState类来和Binder驱动程序交互的,对于每一个Binder线程来说,它内部都有一个IPCThreadState对象,IPCThreadState对象我们可以IPCThreadState的静态成员函数self来获取,并且调用它的成员函数transact(臭不要脸起一样的名字)来和Binder驱动程序交互,在IPCThreadState类的成员函数transact内部,与Binder驱动程序的交互操作又是通过调用成员函数talkWithDriver来实现的,OK终于知道进程是怎么和内核中Binder驱动程序交互的。

  • ProcessState类

IPCThreadState类有一个成员变量mProcess,它指向一个ProcessState对象。对于每一个使用了Binder进程间通信机制的进程来说,它内部都有一个ProcessState对象,它负责初始化设备,即打开/dev/binder设备文件,以及将设备文件/dev/binder映射到进程的地址空间。OK我们又找到了打开设备文件和进行内存映射的类。ProcessState对象在进程范围内是唯一的,不像IPCThreadState对象每个Binder线程内部都有一个,因此每个Binder线程都可以通过它来和Binder驱动程序建立连接。

还记得之前内核中的一些结构吗,如果不记得了看看之前的图片,我们之前只是用想进程间通信来代替细节,现在我们根据已知的东西我们可以描绘出进程和Binder驱动程序的交互过程。

灵魂画手前来拜访

这里写图片描述
到目前为止前文中提到的概念,还有Binder引用对象Binder实体对象还没有介绍,我们可以发现Binder本地对象、Binder代理对象都是在用户空间中的,也就是C/S端进程中,也说了实际上进程间通信就是通过Binder代理对象发送进程间通信请求,通过Binder驱动等一系列操作,最后由Binder本地对象来处理请求,完成进程间通信。那么Client端的Binder代理对象时如何通过Binder驱动一系列处理找到Server端Binder本地对象的,只要弄明白了这里,我们就可以知道Binder驱动程序都做了什么。

其实Binder引用对象、Binder实体对象都是位于Binder驱动程序中的其实就是C语言写的数据结构,或者当成对象来理解也可以,Binder代理对象对应的就是Binder引用对象,Binder本地对象对应的就是Binder实体对象,而Binder引用对象,引用的就是Binder实体对象,Binder实体对象中有Binder引用对象的列表,为什么是对应列表呢?因为Binder进程间通信是支持并发的,同时一个Server端是可以向多个Client端提供服务的,所以用一个列表来保存Client端Binder代理对象对应的Binder本地对象,Binder引用对象有又相应的句柄值。

上图,鬼斧神工~~
这里写图片描述

这张图片添加了一些Binder引用对象和Binder实体对象和binder_proc结构体以及内核缓冲区的一些联系,显示了Binder引用对象和Binder实体对象在内核中的数据结构,但是也省略了很多的内容,比如通过进程间通信,让Server的本地对象处理逻辑,我需要先有Server端的代理对象,这个对象怎么来,怎么成为Server端,让内核中有对应我Binder对象的Binder实体对象等等!那么接下来我将通过Servicer Manager慢慢给大家揭开。
###Service Manager上下文管理者
Servicer Manager是Binder进程间通信机制的核心组件之一,它扮演着Binder进程间通信机制上下文管理者的角色,同时负责管理系统中Service组件,并向Client组件提供获取Service代理对象的服务。

还记的上面的问题吗?我们可以发现上面的问题Servicer Manager都能帮我们解答,那么Servicer Manager是如何解决的呢,我们先来看Servicer Manager本身,Servicer Manager本身就是一个运行在一个进程当中,我这么说可能大家没什么反应,但是细思极恐啊,Servicer Manager在一个进程中,那么Client端和Server端都是在各自的进程中,我们无论想通过Servicer Manager获取代理对象,还是将本地对象弄到内核中,首先就要进行跨进程间的通信啊,也就是说我们要进行进程间通信,先要有进程间通信,妈卖批啊!这就成了先有蛋还是先有鸡的问题了,懵逼啊!

还记得Client端和Server端的那个相对概念吗,无论你是打算将进程作为Client端还是Server,它们跟Service Manager通信时,相对于Servicer Manager来说都是Clinet端,而Servicer Manager进程自身就是提供服务的Server端,而Servicer Manager自身成为Server端,是Servicer Manager主动注册到Binder内核驱动中的,所以也可以说Servicer Manager是一个特殊的Server端(因为它不需要和管理者交互,因为它自己就是管理者)

Servicer Manager是由init进程负责启动的,init进程是Linux的第一个进程,所以说Servicer Manager启动的早啊,它这只鸡得赶紧先把自己孵出来啊。不然其他进程通信的时候就懵逼了啊。
Servicer Manager初始化时都做了些什么?
不废话,上图
这里写图片描述

Service Manager一共就干了三件事,而且都是大事,我们来分析一下

##打开/dev/binder设备文件、映射到本进程的地址空间

  • 打开/dev/binder设备文件、映射到本进程的地址空间,干了这些事的Service Manager,Binder驱动程序会做些什么,相信大家通过前面的介绍应该知道,这里还是简单的唠叨一下,获得文件描述用来和驱动程序交互用,Binder驱动程序分配内核缓冲区,创建binder_proc结构体等。

##注册成为管理者

  • 注册成为管理者,Service Manager想注册成为管理者,就必须要通过IO控制命令将自己注册到Binder驱动程序,IO控制命令可以理解成进程和内核的交互方法,既然Service Manager是一个特殊的Server端,如果想成为Server那么它就需要有一个自己Binder本地对象,我们知道创建对象在内存中都是有一个对应的内存地址值的,但是Service Manager的Binder本地对象是一个虚拟对象,它的地址值为0,请大家记住这个0的地址值,还记得我们之前说到的一个概念句柄值吗?我当时说句柄值和内存地址值息息相关。而Binder驱动程序就是通过binder_ioctl函数来处理IO控制命令的。简单的分析下注册流程。

首先,第一步中我们为Service Manager创建的结构体binder_proc,通过以前的图片我们可以知道,binder_proc中的threads是对应这Binder线程池的(threads红黑树,以线程PID为关键字来组织),Binder线程池中的线程就是处理和Binder驱动程序交互的,而描述这个Binder线程的结构体就是binder_thread,Binder驱动程序会查看这个threads有没有没对应的binder_thread结构体,因为ServiceManager是进行初始化注册,所以没有对应的结构体,Binder驱动程序就为Service Manager创建一个这样的结构体,当前线程就是Service Manager的主线程。

其次,Binder驱动程序会根据Service
Manager的Binder本地对象(地址为0的虚拟对象),判读是否存在对应的Binder实体对象,还是因为进行初始化注册,没有对应的实体对象,就为其建一个Binder实体对象,保存在全局变量binder_context_mgr_node中。

最后,将这个新创建的实体对象加入结构体binder_proc的nodes成员变量里,然后继续返回用户空间,开始开启循环。

以上就是注册成为管理者的过程,总结一下就是,让驱动程序根据IO控制命令弄出Binder线程,Binder实体对象,然后和binder_proc这个结构体的相关成员变量关联起来。
##循环等待Client进程请求

  • 循环等待Client进程请求
    这里将要出现一个新的结构体binder_writr_read,看名字可以看出是读写相关的,既然读写会出现输入和输出,binder_writr_read就包含输入缓存区和输出缓冲区,循环依然使用的是IO控制命令通过Binder线程(binder_thread结构体)以及inder_writr_read来和Binder驱动程序交互,依然使用的是binder_ioctl函数来处理IO控制命令,注册的时候我们已经创建了binder_thread结构体,所有就使用这个结构体,使用copy_from_use将从用户空间(Service Manager)传进来的结构体binder_writr_read拷贝进来。既然是交互,那么就有接收和返回,当进程同过IO控制命令传递binder_writr_read数据的时候,如何判断是让Binder驱动程序读数据,还是让Binder驱动程序写入数据返回给用户空间呢,这里就是通过判断binder_writr_read结构体的缓冲区,如果输入缓存区长度大于0,Binder驱动程序就使用binder_thread_write来处理数据,如果输出缓存区长度大于0,Binder驱动程序就使用binder_thread_read将需要处理的工作项写入到输出缓冲区,即将相应的返回协议写入该缓冲区中,以便进程在用户空间处理。工作项在内核中使用binder_transaction结构体来描述,工作项对应的数据就是binder_transaction_data结构,这里可能涉及到很多新的东西。我接下来会做一个比较易懂的总结。
    ##总结
    总结,循环我们可以想象我们工作中使用的轮询操作,就是不停的发送数据给Binder驱动程序,然后驱动程序会根据发送来的数据状态是将数据copy下来,还是去找看看有没有你需要做的工作返回给你做,总之一句话就是循环等待Client进程请求。这只是我的理解,当然真实情况不这个复杂的多。

老样子上图,其实这个图画的我心里也是没底的,感觉有些细节还是不是很清楚,简化了一下打开和映射的关联,如果不记得了可以看之前的图片。
这里写图片描述

到此我们就分析完了Service Manager进程是怎么工作的,怎么成为管理者的,又是怎么样来等待Client端请求的。

###Service Manager代理对象的获取
Service Manager成为管理者了,也开启循环了,它已经准备好的一切,就等为Client进程服务了,相信大家对于Binder进程间通信心里已经有了一大概的框架,我们要想使用其他进程提供给我们的服务,就需要拿到Server端的Binder代理对象,我们要使用Service Manager的服务,当然也要获取它Binder代理对象,然后通过代理对象找到Binder引用对象然后找到Binder实体对象,然后最终交给Binder本地对象来处理。而中间这个找来找去的过程,离不开Binder内核驱动,但是Service Manager的代理对象比较特殊,它省去了和Binder内核驱动的交互,为什么,谁让Service Manager是管理者,谁让它是一个特殊的Server端,谁让他的Binder本地对象是一个虚拟对象,谁让它的地址值是0呢,这个时候Binder进程间通信库就发挥作用了,它就提供了直接获取Service Manager的Binder代理对象的函数defaultServiceManager。

进程通过使用Binder库的defaultServiceManager函数获取Service Manager的过程也比较简单,

1.判断进程中有没有Service Manager代理对象,有就直接用还获取个P啊,如果没有继续。

2.这个部分总共分为3小部分,获取Service Manager代理对象的过程,实际上就是进程间通信的过程,Client端和Service Manager进程间通信,凡是需要进程间通信的,都需要打开和映射/dev/binder设备文件这个过程,从上文我们可以知道干这件事的类就是ProcessState对象啊,然后在使用句柄值0创建一个Binder代理对象,接着将这个Binder代理对象调用模板函数封装成一个Service Manager代理对象。以上就是Service Manager代理对象的获取过程。

这里写图片描述

对于一般的Service组件来说,Client进程首先要通过Binder驱动程序来获得它的一个句柄值,然后根据这个句柄值创建Binder代理对象,最后将这个Binder对象封装成一个实现特定接口的代理对象。因为Service
Manager的句柄值恒为0,所以省略了和Binder驱动程序的交互过程。对于非Servicer Manager来说,获取句柄值就需要和内核进行交互,获取的办法将会在下文中提到。

###Server端,服务的提供者
服务不是你想提,想提就能提,要想成为Server端,先得将Service组件注册到Service Manager中,接着启动Binder线程池来等待和处理Client进程的通信请求。
注册服务就是Binder库提供的addService函数,我这里说一下大体的流程,首先如果想成为Server端,就得先准备Binder本地对象,数据,Service名称,偏移数组(Binder本地对象的地址值)什么的,将这一部分东西通过一个Parcel的对象进行多次封装,因为我们已经分析的Service Manager代理对象的获取过程,这里就使用Service Manager代理对象同过之前分析Binder进程间通信库的那个交互流程将Parcel封装的数据(最终转换成flat_binder_object结构体)发送给Binder内核驱动程序,内核驱动程序就会根据Service Manager代理对象的句柄值0,我们可以确定是和Service Manager进行通信,然后就找到了全局变量binder_context_mgr_node,最后定位到了binder_proc结构体和内核缓冲区,将数据拷贝到Service Manager对应的内核缓冲区处理数据,同时我也可以根据Service Manager代理对象传入的数据分析出注册Service的进程对应的binder_proc结构体,由于是首次注册该binder_proc结构体的nodes对象一定为null,Binder驱动程序就会为创建一个Binder实体对象关联到nodes,然后再看它有没有引用对象,肯定没有啊实体都没有,哪里来的引用,接着创建引用对象(binder_ref),然后将这个对象(binder_ref)插入到Service Manager进程的binder_proc对象的一个链表中。最后启动Binder线程池将当前线程加入到Binder线程池中。

接下来结合代理对象的获取过程,简单的用一张图片来演示,其实关于这个过程的很多细节描述起来需要至少一篇很长的博客,而且我的理解也不见得有多么透彻,中间关于数据传输到内核中,内存相关,工作项事物相关的东西都没有提到,这里分享一篇博客,就是专门描述addService的这个过程,里面细节部分很详细了,http://blog.csdn.net/armwind/article/details/72852560结合这个博客再来看我的图片,就会发现是多么地辣眼睛~
这里写图片描述

###Client端,服务的请求者,我要获取Server端的代理对象,让你服务我
Client端想要使用服务,当然需要获取Server端的代理对象了,只有获取到这个代理对象,才能通过代理对象来让Server端的本地对象给我服务,我们之前了解了Servicer Manager代理对象的获取过程,知道了Server端的代理对象和Servicer Manager代理对象的获取方式上还是有些不同的,关键就是句柄值的获取,Service Manager代理对象的句柄值恒为0,我们直接用0去获取就好了。还是上面的图
这里写图片描述
知道句柄值轻轻松松拿代理,而通过上文分析Server端的注册过程我们知道,Server端的Binder引用对象(desc变量就是分配的句柄值)最终通过一系列的交互插入到Service Manager的svclist列表中,所以我们Client端想要获取Server端的代理对象就需要去Service Manager中去找,而去Service Manager找的过程又是一个进程间通信过程,还是要获得Service Manager的代理对象,这里我就不分析Service Manager的代理对象获取过程了,不记得的看上图。

Binder进程间通信库提供了defaultServiceManager()函数来获取Service Manager的代理对象,提供了addServicer()函数来注册成为Server端,那么也同时提供了一个函数getService()来获取Server端的代理对象,在调用getService()来获取Server端的代理对象时,需要指定这个Service组件的名称(这是自然,你要告诉Servicer Mananger找那个提供服务的服务端啊),内核驱动最终联系到Service Manager进程,然后Service Manager进程根据名称数据,在和内核交互,从全局变量svclist中查找,找到指定的Server端句柄,内核根据句柄找到相应的引用对象,然后在根据引用对象找到实体对象,重新创建一个引用对象供客户端转换成代理对象。

通过上面的阅读我们可以发现客户端获取代理对象的大体流程如图:
这里写图片描述

至此我们就分析完了所有的流程,为什么我开篇要说我太懵懂了,其实分析到后半段已经力不从心了,Binder内核驱动程序远比我说的要复杂,这里面内核对象的生命周期管理(引用计数技术)都没有没分析,IO控制命令的基本上一带而过,很多东西其实并不像我图中箭头方法那么直接,都经过了和Binder驱动程序多次命令协议交互,根据命令协议进程也进行了相应的操作,功力有限实在只能分析这么些了。

文中如有错误,导致误导了大家,那么算你倒霉,欢迎各位大神积极留言,帮我指正。
最后文中内容很多引用自《Android系统源代码分情景分析》一书中的内容,也引用了一些其他博主的文字,如有侵权请告知!

##终于写完了,灵魂画手的精神力已经被掏空~~~

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Android之ActivityGroup实现Tab功能

    android.app包中含有一个ActivityGroup类,该类是Activity的容器,可以包含多个嵌套进来的Activitys,这篇文章就是借助ActivityGroup可以嵌套Activity的功能来实现Tab功能。tab这种UI在很多的移动应用中可以看到,包括android、iphone、window phone7等移动终端上都有这样的应…...

    2024/4/18 2:19:02
  2. 读书心得

    虽然知道自身的性格缺陷,但是一直无法改变,导致自己在工作和家庭中出现一些问题。读高效能人士的七个习惯一书,想从中收获一些有用东西,希望能够运用到生活中。首先是七个习惯的简要定义和架构图:习惯一:积极主动(BE PROACTIVE)主动积极即采取主动,为自己过去、现在及…...

    2024/4/17 4:27:45
  3. 使用ActivityGroup来切换Activity和Layout

    http://blog.csdn.net/xyz_lmn/article/details/6939160 http://www.cnblogs.com/over140/archive/2010/09/07/1820876.html http://www.cnblogs.com/android100/p/Android-Fragment-ActivityGroup.html 前言在一个主界面中做Activity切换一般都会用TabActivity,使用方便,Act…...

    2024/4/26 0:10:52
  4. [Binder机制]大管家ServiceManager启动流程

    Binder机制作为android系统IPC(Inter-Process Communication,进程间通信)的主要方式,理解Binder机制至关重要。首先简要介绍IPC的个人理解。IPC的概念是随进程产生而产生的,在操作系统相关知识中,进程代表资源管理单位,每个进程享有独立的内存空间,两个不同的进程之间不…...

    2024/5/5 5:23:18
  5. 《Java Web从入门到精通》-笔记

    2015-10-03 原文:Java是一种跨平台的面向对象语言 2015-10-03 原文:Java是一种优秀的面向对象语言,其有着健壮的安全性设计。Java的结构是中立的,可以移植到不同的系统平台。优秀的多线程设计也是Java语言的一大特色。但是Java语言的最 2015-10-03 原文:大优势还是其对多种…...

    2024/4/18 16:20:01
  6. python爬虫入门教程(一):开始爬虫前的准备工作

    2019/10/28更新使用Python3,而不再是Python2 更新了推荐书籍转载注明出处:https://blog.csdn.net/aaronjny/article/details/77885007 爬虫入门系列教程:python爬虫入门教程(一):开始爬虫前的准备工作python爬虫入门教程(二):开始一个简单的爬虫python爬虫入门教程(三):淘…...

    2024/5/5 15:30:44
  7. 聚焦爬虫与通用爬虫

    为什么要学习爬虫?学习爬虫,可以私人订制一个搜索引擎。 大数据时代,要进行数据分析,首先要有数据源。 对于很多SEO从业者来说,从而可以更好地进行搜索引擎优化。什么是网络爬虫?模拟客户端发送网络请求,接收请求对应的数据,按照一定的规则,自动抓取互联网信息的程序。…...

    2024/3/31 21:09:24
  8. button实现href

    如何让button变成<a href=链接></a>效果 其实就是一行代码,不过网上老有人拿错误代码来误导,不知道是不是以前的语法有不一样,下面放正确方法 在原窗口跳转到新网址 <button οnclick="window.location.href=http://www.baidu.com">链接</bu…...

    2024/4/17 4:28:03
  9. Android 基于GridView和ActivityGroup实现的TAB分页

    分页大家都会用Android的TabHost和TabActivity的组合,今天我这里实现的是GridView和ActivityGroup实现的分页,这里需要将Activity转换成Window,然后再换成成View添加到容器中,效果如下1.布局文件底部放一个GridView,然后一个LinearLayout容器在GridView之上,LinearLayout用…...

    2024/3/31 21:09:23
  10. Binder机制简单理解

    Binder初理解: 参考:http://blog.csdn.net/luoshengyang/article/details/6618363通过罗大牛的文章,我们知道,Binder是作用于通信的。相对于binder,我们更熟悉使用广播进行通信。他们之间有什么联系? http://www.cnblogs.com/lwbqqyumidi/p/4168017.html 里面提到: 广播…...

    2024/5/5 6:45:43
  11. SQL Server 2005从入门到精通

    SQL Server数据库中的一个数据文件会以8KB大小分页。每一页可以包括数据、索引、或者其他SQL Server需要为其维护数据文件的数据类型。然而,大多数的页是数据页或者索引页。页是SQL Server读、写数据文件的单元。每一页只包括一个数据对象的数据或索引信息。所以,在每一个数据…...

    2024/4/27 7:30:46
  12. button按钮的点击状态

    //表示当前按钮可用(表示按钮点击了会执行方法) [Button setEnabled:YES];//表示当前按钮不可用(表示按钮点击了也不会执行方法) [Button setEnabled:NO];...

    2024/4/19 0:35:01
  13. java爬虫入门实战

    爬虫百度百科:网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。爬虫的实质,就是获取静态数据,并对此进行分…...

    2024/4/17 4:27:57
  14. Spring Boot 2.0 从入门到精通-数据库连接

    数据库连接配置Spring Boot 可以自动配置多种嵌入式数据库,如H2、HSQL、Derby,集成时不需要配置连接URL,只需要引入依赖包即可,现在我们拿H2为例讲解在pom.xml 添加H2 配置<dependency><groupId>com.h2database</groupId><artifactId>h2</artif…...

    2024/5/5 16:36:55
  15. 精读高效能人士的七个习惯:思维定式

    基于原则的思维定式,可以引导我们实现高效能,改善人际关系,解决深层问题。大家好,我是阿飞。今天是我和大家分享的概念是思维定式。 在说思维定式之前,我想和大家分享一对图片:这是两个妙龄少女是吗?我第一眼看上去也是这样的。 但是我们看看下面这张图:然后,我们再来…...

    2024/4/4 22:24:14
  16. kudu分布式存储系统从入门到精通

    课程链接: http://edu.51cto.com/course/15174.html 课程目标 通过对本课程的学习能够让您对kudu从入门到精通,对已经从事相关大数据工作的学员能够更深层次的学习新知识 适用人群 零基础学员,大数据爱好者 课程简介 系统环境: CentOS7.4+JDK1.8.0 技术点: Kudu1.7.0 讲师…...

    2024/5/5 4:10:41
  17. 使用ActivityGroup时,如何保存Activity状态 如何使用子Activity的Menu

    由于很多朋友觉得,TabHost限制太多,发挥余地不大, 所以都会使用ActivityGroup来代替TabHost。但是在使用ActivityGroup时,很多朋友可能也碰上了标题中的问题。 下面简单讲一下解决办法。1. 使用ActivityGroup时,如何保存Activity状态?其实很简单, 只要像下面这样来加载Ac…...

    2024/4/17 4:28:51
  18. android当中Binder机制,Looper与MessageQueue机制

    上周四讨论内容:android 当中Binder机制,Looper与MessageQueue相关知识,相关链接: http://blog.csdn.net/innost/article/details/6124685 http://blog.csdn.net/innost/article/details/6055793转载于:https://my.oschina.net/u/1865711/blog/294466...

    2024/4/17 4:28:45
  19. 爬虫合法性讨论

    据说互联网上 50%以上的流量都是爬虫创造的,也许你看到很多热门数据都是爬虫所创造的,所以可以说无爬虫就无互联网的繁荣。然而在繁荣的表象背后,也往往暗藏杀机 —— 2019年末「 程序员因写爬虫而被刑侦 」的新闻消息甚嚣尘上:大量数据公司被抓,几十家被列入调查名单51信…...

    2024/4/17 4:28:39
  20. ActivityGroup对返回键的监听

    在做应用开发的时候,我们经常用到ActivityGroup,它对我们管理Activity来说是非常方便的 但是,带来的不幸就是对OnkeyDown()事件的监听,你永远不知道到底是ActivityGroup收到这个方法还是现在显示的Activity接受到这个方法 下面,我提供一种把OnkeyDown()事件全权交给当前显…...

    2024/4/18 21:54:34

最新文章

  1. 新修订的《中华人民共和国保守国家秘密法》新增和修改的内容不包括( )

    新修订的《中华人民共和国保守国家秘密法》新增和修改的内容不包括&#xff08; &#xff09; 点击查看答案 A.旗帜鲜明将党的领导写入法律B.加快提升保密科技创新能力 C.把保密宣传教育摆到重要位置 D.细化商业秘密保密管理 新修订的《中华人民共和国保守国家秘密法》对解密…...

    2024/5/6 21:54:34
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. 【APUE】网络socket编程温度采集智能存储与上报项目技术------多路复用

    作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…...

    2024/5/6 2:28:08
  4. linuxday05

    1、makedile原理&#xff08;增量编译生成代码&#xff09; # &#xff08;注释符&#xff09; 目标------依赖 目标不存在//目标比依赖旧才会执行命令&#xff1b; makefile的实现 1、命名要求&#xff08;Makefile/makefile&#xff09; 2、规则的集合 目标文件&#…...

    2024/5/5 8:52:20
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/6 9:21:00
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57