精华内容
下载资源
问答
  • 2.接口有哪些类型? 3.接口的本质是什么? 4.什么是接口测试? 5.问什么要做接口测试? 6.怎样做接口测试? 7.接口测测试点是什么? 8.接口测试都要掌握哪些知识? 9.其他相关知识? 一.什么是接口? 新...

    转载地址http://www.sohu.com/a/254976978_100194747
    Http-API设计指南https://github.com/ZhangBohan/http-api-design-ZH_CN
    扫盲内容:

    1.什么是接口?

    2.接口都有哪些类型?

    3.接口的本质是什么?

    4.什么是接口测试?

    5.问什么要做接口测试?

    6.怎样做接口测试?

    7.接口测测试点是什么?

    8.接口测试都要掌握哪些知识?

    9.其他相关知识?

    一.什么是接口?

    新互联教育(http://nie.jiadezhihe.com/) 提醒您。接口测试主要用于外部系统与系统之间以及内部各个子系统之间的交互点,定义特定的交互点,然后通过这些交互点来,通过一些特殊的规则也就是协议,来进行数据之间的交互。

    二.接口都有哪些类型?

    接口一般分为两种:

    1.程序内部的接口

    2.系统对外的接口

    系统对外的接口:比如你要从别的网站或服务器上获取资源或信息,别人肯定不会把数据库共享给你,他只能给你提供一个他们写好的方法来获取数据,你引用他提供的接口就能使用他写好的方法,从而达到数据共享的目的。

    程序内部的接口:方法与方法之间,模块与模块之间的交互,程序内部抛出的接口,比如bbs系统,有登录模块、发帖模块等等,那你要发帖就必须先登录,那么这两个模块就得有交互,它就会抛出一个接口,供内部系统进行调用。

    接口的分类:

    1.webservice接口

    2.http api接口

    webService接口是走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都用通过工具才能进行调用,测试。

    http api接口是走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串,有get和post等方法,这也是最常用的两种请求方式。

    json是一种通用的数据类型,所有的语言都认识它。(json的本质是字符串,他与其他语言无关,只是可以经过稍稍加工可以转换成其他语言的数据类型,比如可以转换成Python中的字典,key-value的形式,可以转换成Java中的原生对象,可以转换成java中的类对象等。)

    三.接口的本质及其工作原理是什么?

    接口你可以简单的理解他就是URL,工作原理就会说URL通过get或者post请求像服务器发送一些东西,然后得到一些相应的返回值,本质就是数据的传输与接收。

    四.什么是接口测试?

    接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。

    –百度百科

    简答的说就是通过URL像服务器或者其他模块等,传输我们想传输的数据,然后看看他们返回的是不是我们预期想要的。

    五.问什么要做接口测试?

    ①.越底层发现bug,它的修复成本是越低的。

    ②.前端随便变,接口测好了,后端不用变,前后端是两拨人开发的。

    ③.检查系统的安全性、稳定性,前端传参不可信,比如京东购物,前端价格不可能传入-1元,但是通过接口可以传入-1元。

    ④.如今的系统复杂度不断上升,传统的测试方法成本急剧增加且测试效率大幅下降,接口测试可以提供这种情况下的解决方案。

    ⑤. 接口测试相对容易实现自动化持续集成,且相对UI自动化也比较稳定,可以减少人工回归测试人力成本与时间,缩短测试周期,支持后端快速发版需求。接口持续集成是为什么能低成本高收益的根源。

    ⑥. 现在很多系统前后端架构是分离的,从安全层面来说:

    (1)、只依赖前端进行限制已经完全不能满足系统的安全要求(绕过前面实在太容易), 需要后端同样进行控制,在这种情况下就需要从接口层面进行验证。

    (2)、前后端传输、日志打印等信息是否加密传输也是需要验证的,特别是涉及到用户的隐私信息,如身份证,银行卡等。

    六.怎样做接口测试?

    –由于我们项目前后端调用主要是基于http协议的接口,所以测试接口时主要是通过工具或代码模拟http请求的发送与接收。工具有很多如:postman、jmeter、soupUI、java+httpclient、robotframework+httplibrary等。

    –也可以用 接口自动化来实现,就是用代码实现,框架和UI自动化差不多,发送请求用断言来判断。

    七.接口测测试点是什么?

    目的:测试接口的正确性和稳定性;

    原理:模拟客户端向服务器发送请求报文,服务器接收请求报文后对相应的报文做处理并向客户端返回应答,客户端接收应答的过程;

    重点:检查数据的交换,传递和控制管理过程,还包括处理的次数;

    核心:持续集成是接口测试的核心;

    优点:为高复杂性的平台带来高效的缺陷监测和质量监督能力,平台越复杂,系统越庞大,接口测试的效果越明显(提高测试效率,提升用户体验,降低研发成本);

    用例设计重点:通常情况下主要测试最外层的两类接口:数据进入系统接口(调用外部系统的参数为本系统使用)和数据流出系统接口(验证系统处理后的数据是否正常);

    PS:设计用例时还需要注意外部接口提供给使用这些接口的外部用户什么功能,外部用户真正需要什么功能;

    问题1.1、后端接口都测试什么?

    –回答这个问题,我们可以从接口测试活动内容的角度下手,看一下面这张图,基本反应了当前我们项目后端接口测试的主要内容:

    问题2、后端接口测试一遍 ,前端也测试一遍,是不是重复测试了?

    –回答这个问题,我们可以直接对比接口测试和app端测试活动的内容,如下图为app测试时需要覆盖或考虑内容:

    从上面这两张图对比可以看出,两个测试活动中相同的部分有功能测试、边界分析测试和性能测试,其它部分由于各自特性或关注点不同需要进行特殊的测试,在此不做讨论。接下来我们针对以上三部分相同的内容再进行分析:

    1、基本功能测试:

    由于是针对基本业务功能进行测试,所以这部分是两种测试重合度最高的一块,开发同学通常所指的也主要是这部分的内容。

    2、边界分析测试:

    在基本功能测试的基础上考虑输入输出的边界条件,这部分内容也会有重复的部分(比如业务规则的边界)。但是,前端的输入输出很多时候都是提供固守的值让用户选择(如下拉框),在这种情况下测试的边界范围就非常有限,但接口测试就不存在这方面的限制,相对来说接口可以覆盖的范围更广,同样的,接口出现问题的概率也更高。

    3、性能测试:

    这个比较容易区分,虽然都需要做性能测试,但关注点确大不相同。App端性能主要关注与手机相关的特性,如手机cpu、内存、流量、fps等。而接口性能主要关注接口响应时间、并发、服务端资源的使用情况等。两种测试时的策略和方法都有很大区别,所以这部分内容是需要分开单独进行测试的,理论上来说这也是不同的部分。

    综论:

    1、接口测试和app测试的活动有部分重复的内容,主要集中在业务功能测试方面。除此之外,针对各自特性的测试都不一样,需要分别进行有针对性的测试,才能确保整个产品的质量。

    2、接口测试可以关注于服务器逻辑验证,而UI测试可以关注于页面展示逻辑及界面前端与服务器集成验证

    3、接口测试持续集成:

    对接口测试而言,持续集成自动化是核心内容,通过持自动化的手段我们才能做到低成本高收益。目前我们已经实现了接口自动化,主要应用于回归阶段,后续还需要加强自动化的程度,包括但不限于下面的内容:

    a) 流程方面:在回归阶段加强接口异常场景的覆盖度,并逐步向系统测试,冒烟测试阶段延伸,最终达到全流程自动化。

    b) 结果展示:更加丰富的结果展示、趋势分析,质量统计和分析等

    c) 问题定位:报错信息、日志更精准,方便问题复现与定位。

    d) 结果校验:加强自动化校验能力,如数据库信息校验。

    e) 代码覆盖率:不断尝试由目前的黑盒向白盒下探,提高代码覆盖率。

    f) 性能需求:完善性能测试体系,通过自动化的手段监控接口性能指标是否正常。

    4、接口测试质量评估标准:

    a) 业务功能覆盖是否完整

    b) 业务规则覆盖是否完整

    c) 参数验证是否达到要求(边界、业务规则)

    d) 接口异常场景覆盖是否完整

    e) 接口覆盖率是否达到要求

    f) 代码覆盖率是否达到要求

    g) 性能指标是否满足要求

    h) 安全指标是否满足要求

    八.接口测试都要掌握哪些知识?

    ①了解系统及内部各个组件之间的业务逻辑交互;

    ②了解接口的I/O(input/output:输入输出);

    ③了解协议的基本内容,包括:通信原理、三次握手、常用的协议类型、报文构成、数据传输方式、常见的状态码、URL构成等;

    ④常用的接口测试工具,比如:jmeter、loadrunner、postman、soapUI等;

    ⑤数据库基础操作命令(检查数据入库、提取测试数据等);

    ⑥常见的字符类型,比如:char、varchar、text、int、float、datatime、string等;

    如何学这些技能?

    ①系统间业务交互逻辑:通过需求文档、流程图、思维导图、沟通等很多渠道和方式;

    ②协议:推荐《图解http》这本书,内容生动,相对算是入门级的书籍,其他的还有《图解tcp、IP》等;

    ③接口测试工具:百度这些工具,然后你会发现,好多的教学博客、相关问题解决方案、以及一些基于工具的书籍,当然,选择合适的书很重要;

    ④数据库操作命令:学习网站(W3C、菜鸟教程)、教学博客,以及一些数据库相关书籍,入门级推荐:《mysql必知必会》、《oracle PL/SQL必知必会》等

    ⑤字符类型:还是百度,有句话这么说:内事不决问百度,外事不决问Google。。。

    如何获取接口相关信息?

    一般的企业,都会由开发或者对应的技术负责人员编写接口文档,里面会注明接口相关的地址、参数类型、方法、输入、输出等信息,如果没有,想办法获取。。。

    接口文档八要素:

    封面:封面最好是本公司规定的封面,有logo,内容标题,版本号,公司名称,文档产生日期;

    修订历史:表格形式较好些,包括:版本、修订说明、修订日期、修订人、审核时间审核人等;

    接口信息:接口调用方式,常用的GET/POST方式,接口地址;

    功能描述:简洁清晰的描述接口功能,比如:接口获取的信息不包括哪些;

    接口参数说明:每个参数都要和实际中调用的一样,包括大小写;参数的含义言简意赅的说明,格式,是string 还是int 还是long等格式;

    说明部分,说明参数值是需要哪里提供,并详细说明参数怎么生成的,例如时间戳,是哪个时间段的,参数是否必填,一些参数是必须要有的,有些是可选参数等;

    返回值说明:

    ①最好有一个模板返回值,并说明每个返回参数的意义;

    ②提供一个真实的调用接口,真实的返回值;

    调用限制,安全方面:

    加密方式,或者自己公司一个特殊的加密过程,只要双方采用一致的加密算法就可以调用接口,保证了接口调用的安全性,比如常见的md5;

    文档维护:文档在维护的时候,如有修改一定要写上修改日期,修改人,对大的修改要有版本号变更;

    九.其他相关知识?

    get请求,post请求的区别:

    1、GET使用URL或Cookie传参。而POST将数据放在BODY中。

    2、GET的URL会有长度上的限制,则POST的数据则可以非常大。

    3、POST比GET安全,因为数据在地址栏上不可见。

    4、一般get请求用来获取数据,post请求用来发送数据。

    其实上面这几点,只有最后一点说的是比较靠谱的,第一点post请求也可以把数据放到url里面,get请求其实也没长度限制,post请求看起来参数是隐式的,稍微安全那么一些些,但是那只是对于小白用户来说的,就算post请求,你通过抓包也是可以抓到参数的。(唯一区别就是这一点,上面3点区别都是不准确的)

    http状态码:

    1、200 2开头的都表示这个请求发送成功,最常见的就是200,就代表这个请求是ok的,服务器也返回了。

    2、300 3开头的代表重定向,最常见的是302,把这个请求重定向到别的地方了。

    3、400 400代表客户端发送的请求有语法错误,401代表访问的页面没有授权,403表示没有权限访问这个页面,404代表没有这个页面。

    4、500 5开头的代表服务器有异常,500代表服务器内部异常,504代表服务器端超时,没返回结果。

    webservice接口怎么测试:

    它不需要你在拼报文了,会给一个webservice的地址,或者wsdl文件,直接在soapui导入,就可以看到这个webservice里面的所有接口,也有报文,直接填入参数调用,看返回结果就可以了。

    天气预报wsdl地址:http://www.webservicex.net/globalweather.asmx?wsdl

    cookie与session的区别:

    1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

    2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗考虑到安全应当使用session。

    3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。

    4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

    5、所以个人建议:

    将登陆信息等重要信息存放为session

    其他信息如果需要保留,可以放在cookie中

    展开全文
  • LCD的接口类型详解

    万次阅读 2017-04-27 16:18:14
    LCD的接口类型详解  LCD的接口多种,分类很细。主要看LCD的驱动方式和控制方式,目前手机上的彩色LCD的连接方式一般这么几种:MCU模式,RGB模式,SPI模式,VSYNC模式,MDDI模式,DSI模式。MCU模式(也写成MPU...

    LCD的接口类型详解

              LCD的接口有多种,分类很细。主要看LCD的驱动方式和控制方式,目前手机上的彩色LCD的连接方式一般有这么几种:MCU模式,RGB模式,SPI模式,VSYNC模式,MDDI模式,DSI模式。MCU模式(也写成MPU模式的)。只有TFT模块才有RGB接口。

     

    但应用比较多的就是MUC模式和RGB模式,区别有以下几点:

    1.MCU接口:会解码命令,由timing generator产生时序信号,驱动COM和SEG驱器。

       RGB接口:在写LCD register setting时,和MCU接口没有区别。区别只在于图像的写入方式。 

    2.用MCU模式时由于数据可以先存到IC内部GRAM后再往屏上写,所以这种模式LCD可以直接接在MEMORY的总线上。

       用RGB模式时就不同了,它没有内部RAM,HSYNC,VSYNC,ENABLE,CS,RESET,RS可以直接接在MEMORY的GPIO口上,用GPIO口来模拟波形. 

    3.MPU接口方式:显示数据写入DDRAM,常用于静止图片显示。

        RGB接口方式:显示数据不写入DDRAM,直接写屏,速度快,常用于显示视频或动画用。

    MCU接口和RGB接口主要的区别是:

                              MCU接口方式:显示数据写入DDRAM,常用于静止图片显示。

                              RGB接口方式:显示数据不写入DDRAM,直接写屏,速度快,常用于显示视频或动画用。

     

     

              MCU模式

                因为主要针对单片机的领域在使用,因此得名.后在中低端手机大量使用,其主要特点是价格便宜的。MCU-LCD接口的标准术语是Intel提出的8080总线标准,因此在很多文档中用I80 来指MCU-LCD屏。主要又可以分为8080模式和6800模式,这两者之间主要是时序的区别。数据位传输有8位,9位,16位,18位,24位。连线分为:CS/,RS(寄存器选择),RD/,WR/,再就是数据线了。优点是:控制简单方便,无需时钟和同步信号。缺点是:要耗费GRAM,所以难以做到大屏(3.8以上)。对于MCU接口的LCM,其内部的芯片就叫LCD驱动器。主要功能是对主机发过的数据/命令,进行变换,变成每个象素的RGB数据,使之在屏上显示出来。这个过程不需要点、行、帧时钟。

             MCU接口的LCDDriver IC都带GRAMDriver IC作为MCU的一片协处理器,接受MCU发过来的Command/Data,可以相对独立的工作。对于MCU接口的LCM(LCD Module),其内部的芯片就叫LCD驱动器。主要功能是对主机发过的数据/命令,进行变换,变成每个象素的RGB数据,使之在屏上显示出来。这个过程不需要点、行、帧时钟。

     

                                       M6800模式

                                       M6800模式支持可选择的总线宽度8/9/16/18-bit(默认为8位),其实际设计思想是与I80的思想是一样的,主要区别就是该模式的总线控制读写信号组合在一个引脚上(/WR),而增加了一个锁存信号(E)数据位传输有8位,9位,16位和18位

     

                                      I8080模式

                                      I80模式连线分为:CS/,RS(寄存器选择),RD/,WR/,再就是数据线了。优点是:控制简单方便,无需时钟和同步信号。缺点是:要耗费GRAM,所以难以做到大屏(QVGA以上)。

     

    MCU接口标准名称是I80,管脚的控制脚有5个:

    • CS 片选信号
    • RS (置1为写数据,置0为写命令)
    • /WR (为0表示写数据) 数据命令区分信号
    • /RD (为0表示读数据)
    • RESET 复位LCD( 用固定命令系列 0 1 0来复位)

            VSYNC模式

                  该模式其实就是就是在MCU模式上加了一个VSYNC信号,应用于运动画面更新,这样就与上述两个接口有很大的区别。该模式支持直接进行动画显示的功能,它提供了一个对MCU接口最小的改动,实现动画显示的解决方案。在这种模式下,内部的显示操作与外部VSYNC信号同步。可以实现比内部操作更高的速率的动画显示。但由于其操作方式的不同,该模式对速率有一个限制,那就是对内部SRAM的写速率一定要大于显示读内部SRAM的速率。

     

              RGB模式

              大屏采用较多的模式,数据位传输也有6位,16位和18位,24位之分。连线一般有:VSYNC,HSYNC,DOTCLK,CS,RESET,有的也需要RS,剩下就是数据线。它的优缺点正好和MCU模式相反。

              MCU-LCD屏它与RGB-LCD屏主要区别在于显存的位置。RGB-LCD的显存是由系统内存充当的,因此其大小只受限于系统内存的大小,这样RGB-LCD可以做出较大尺寸,象现在4.3"只能算入门级,而MID中7",10"的屏都开始大量使用。而MCU-LCD的设计之初只要考虑单片机的内存较小,因此都是把显存内置在LCD模块内部.然后软件通过专门显示命令来更新显存,因此MCU屏往往不能做得很大。同时显示更新速度也比RGB-LCD慢。显示数据传输模式也有差别。RGB屏只需显存组织好数据。启动显示后,LCD-DMA会自动把显存中的数据通过RGB接口送到LCM。而MCU屏则需要发送画点的命令来修改MCU内部的RAM(即不能直接写MCU屏的RAM)。所以RGB显示速度明显比MCU快,而且播放视频方面,MCU-LCD也比较慢。

             对于RGB接口的LCM,主机输出的直接是每个象素的RGB数据,不需要进行变换(GAMMA校正等除外),对于这种接口,需要在主机部分有个LCD控制器,以产生RGB数据和点、行、帧同步信号。

     
     

                       彩色TFT液晶屏主要有2种接口:TTL接口(RGB颜色接口), LVDS接口(将RGB颜色打包成差分信号传输)。TTL接口主要用于12.1寸一下的小尺寸TFT屏,LVDS接口主要用于8寸以上的大尺寸TFT屏。TTL接口线多,传输距离短;LVDS接口传输距离长,线的数量少。大屏采用较多的模式,控制脚是VSYNC,HSYNC,VDEN,VCLK, S3C2440最高支持24个数据脚,数据脚是VD[23-0]。

                     CPU或显卡发出的图像数据是TTL信号(0-5V、0-3.3V、0-2.5V、或0-1.8V),LCD本身接收的也是TTL信号,由于TTL信号在高速率的长距离传输时性能不佳,抗干扰能力比较差,后来又提出了多种传输模式,比如LVDS、TDMS、GVIF、P&D、DVI和DFP等。他们实际上只是将CPU或显卡发出的TTL信号编码成各种信号以传输,在LCD那边将接收到的信号进行解码得到TTL信号。

                      但是不管采用何种传输模式,本质的TTL信号是一样的。

    注意:TTL/LVDS分别是两种信号的传输模式,TTL是高电平表示1,低电平表示0的模式,LVDS是正负两个对应波形,用两个波形的差值来表示当前是1还是0

              SPI模式

                 采用较少,有3线和4线的,连线为CS/,SLK,SDI,SDO四根线,连线少但是软件控制比较复杂。

             

              MDDI模式(MobileDisplayDigitalInterface)

                 高通公司于2004年提出的接口MDDI,通过减少连线可提高移动电话的可靠性并降低功耗,这将取代SPI模式而成为移动领域的高速串行接口。 连线主要是host_data,host_strobe,client_data,client_strobe,power,GND几根线。

              DSI模式

                 该模式串行的双向高速命令传输模式,连线有D0P,D0N,D1P,D1N,CLKP,CLKN。    

     


               

     


    原文链接: http://blog.csdn.net/ce123/article/details/6896819
    展开全文
  • ATA(Advanced Technology Attachment, 高级技术附加装置)起源于 IBM,是一个单纯的磁盘驱动器接口,不支持其他的接口设备,适配的是 IDE(Integrated Drive Electronics,电子集成驱动器)磁盘驱动器。IDE 接口,...

    目录

    ATA(IDE)

    ATA(Advanced Technology Attachment, 高级技术附加装置)起源于 IBM,是一个单纯的磁盘驱动器接口,不支持其他的接口设备,适配的是 IDE(Integrated Drive Electronics,电子集成驱动器)磁盘驱动器。IDE 接口,也称为 PATA(Parallel ATA,并行 ATA)接口,意在把磁盘控制器和磁盘驱动器集成到了一起,这种做法减少了磁盘接口的电缆数目与长度,数据传输的可靠性得到了增强,磁盘制造起来也变得更容易,因为厂商不需要担心自己的磁盘驱动器是否与其它厂商的磁盘控制器兼容。对用户而言,磁盘安装起来也更为方便。

    IDE 具有价格低廉,兼容性强,性价比高,数据传输慢,不支持热插拔等特点,多用于家用产品中,也有部分应用于服务器。ATA、Ultra ATA、DMA、Ultra DMA 等接口都属于 IDE 硬盘。

    在这里插入图片描述
    在这里插入图片描述

    SATA 与 eSATA

    SATA(Serial ATA,串行 ATA),又叫串口磁盘,是一个串行点对点连接系统,支持热插拔,支持主机与每个磁盘驱动器之间拥有单独的连接路径,每个磁盘驱动器都可以独享全部带宽。SATA 在机柜内部连接外部设备,而 eSATA 是外置式的 SATA 接口。

    SATA 是一种完全不同于 Parallel ATA 的新型磁盘接口类型,由于采用串行方式传输数据而知名。相对于 Parallel ATA 而言,就具有非常多的优势。首先,SATA 以连续串行的方式传送数据,一次只会传送 1 位数据。这样能减少 SATA 接口的针脚数目,使连接电缆数目变少,效率也会更高。实际上,SATA 仅用四支针脚就能完成所有的工作,分别用于连接电缆、连接地线、发送数据和接收数据,同时这样的架构还能降低系统能耗和减小系统复杂性。其次,SATA 的起点更高、发展潜力更大,SATA 1.0 定义的数据传输率可达 150MB/s,这比目前最新的 Parallel ATA(即 ATA/133)所能达到 133MB/s 的最高数据传输率还高,而在 SATA 2.0 的数据传输率将达到 300MB/s,最终 SATA 将实现 600MB/s 的最高数据传输率。SATA 总线使用嵌入式时钟信号,具有更强的纠错能力,与 ATA 相比其最大的区别在于能对传输指令(不仅仅是数据)进行检查,如果发现错误会自动矫正,这在很大程度上提高了数据传输的可靠性。串行接口还具有结构简单、支持热插拔的优点。

    在这里插入图片描述
    在这里插入图片描述

    SCSI

    SCSI(Small Computer System Interface,小型计算机系统接口)是当前流行的用于服务器和微机的外部设备接口标准,目前的计算机基本都设置了 SCSI 接口,如果没有的话就需要一块专门的 SCSI 适配器卡,完成主机总线到 SCSI 总线的跨接,不同的主机总线对应不同的适配器。SCSI 设备与主机之间以 DMA 的方式传输数据库,SCSI 适配器中整合了主机通信的指令,降低了系统 I/O 处理对主机 CPU 的占用率。

    与 IDE 不同,IDE 接口是普通 PC 的标准接口,而 SCSI 并不是专门为硬盘设计的接口,是一种广泛应用于小型机上的高速数据传输技术。不仅可以控制磁盘驱动器,还可以控制磁带机、光盘、打印机和扫描仪等外设。由于设备中包括了控制器,设备的功能更复杂,因而称为智能外设。SCSI 接口具有应用范围广、多任务、带宽大、CPU 占用率低,以及热插拔等优点,但也拥有较高的价格。

    在这里插入图片描述
    在这里插入图片描述

    SAS

    SAS(Serial attached SCSI,串行 SCSI),向下兼容并行 SATA,两种运行相同的指令集,在物理连接接口上提供了对 SATA 的支持,即两者可以使用相类似的电缆。从接口标准上而言,SATA 是 SAS 的一个子标准,SAS 吸收了 FC(光纤通道)的特点,因此 SAS 控制器可以直接操控 SATA 硬盘,但是 SAS 却不能直接使用在 SATA 的环境中,因为 SATA 控制器并不能对 SAS 硬盘进行控制。

    在协议层,SAS 由 3 种类型协议组成,根据连接的不同设备使用相应的协议进行数据传输。其中 SSP(串行 SCSI 协议)用于传输 SCSI 命令,SMP(SCSI 管理协议)用于对连接的设备进行维护和管理,STP(SATA 通道协议)用于 SAS 和 SATA 之间数据的传输。在这 3 种协议的配合下,SAS 可以和 SATA 以及部分 SCSI 设备无缝结合。此外,与并行方式相比,串行方式能提供更快速的通信传输速度以及更简易的配置,SAS 的传输速率要比 SATA 快得多。SAS 为服务器和网络存储等应用提供了更大的选择空间。

    在这里插入图片描述
    Serial attached SCSI

    iSCSI

    iSCSI(Internet SCSI)是运行在 TCP/IP 网络上的 SCSI,iSCSI 使得标准的 SCSI 指令能够在 TCP/IP 网络上的主机与网络存储设备之间传送,也可以在系统之间传送。而 SCSI 协议只能访问本地的 SCSI 设备。
    在这里插入图片描述
    iSCSI 的工作原理:当 iSCSI Initiator(终端用户)发送一个请求后,操作系统会产生一个适当的 SCSI 指令和数据请求,SCSI 指令通过封装被加上了 TCP/IP 协议的包头,并支持在需要加密的时候执行加密处理。封装后的 SCSI 指令数据包得以在 Internet 上传送,以此突破了物理空间的限制。iSCSI Target(接收端)在收到这个数据包之后按照相反的步骤进行解析得出 SCSI 指令和数据请求,再交由 SCSI 存储设备驱动程序处理。因为 iSCSI 是双向的协议,所以 iSCSI Target 可以将数据(执行结果)返回给 iSCSI Initiator。

    FC

    FC(Fibre Channel,光纤通道),与 SCSI 接口一样,光纤通道最初也不是专为磁盘驱动器所设计的接口技术,而是专门为网络系统设计的。但随着存储系统对速度的需求日益增长,FC 才逐渐应用被到磁盘存储系统中。光纤通道磁盘是为提高多磁盘存储系统的速度和灵活性才开发的,它的出现大大提高了多磁盘系统的通信速度。FC 具有热插拔性、高速带宽、远程连接、连接设备数量大等特点,是为了服务器此类多磁盘系统环境而设计的,能够满足高端工作站、服务器、海量存储子网络、外设间通过集线器、交换机和点对点连接进行双向、串行数据通讯等系统对高数据传输率的要求。

    在这里插入图片描述

    RDMA

    RDMA(Remote DMA,远程直接内存访问)是一种新的内存访问技术,RDMA 让计算机可以直接存取其他计算机的内存,而不需要经过处理器耗时的处理。RDMA 将数据从一个系统快速移动到远程系统存储器中,而不对操作系统造成任何影响。RDMA 技术的原理及其与 TCP/IP 架构的对比如下图所示。

    在这里插入图片描述

    • InfiniBand(IB):从一开始就支持 RDMA 的新一代网络协议。由于这是一种新的网络技术,因此需要支持该技术的网卡和交换机。
    • RDMA 融合以太网(RoCE):一种允许通过以太网进行 RDMA 的网络协议。其较低的网络头是以太网头,其上网络头(包括数据)是 InfiniBand 头。这允许在标准以太网基础架构(交换机)上使用 RDMA。只有 NIC 应该是特殊的,并支持 RoCE。
    • 互联网广域 RDMA 协议(iWARP):允许通过 TCP 执行 RDMA 的网络协议。在 IB 和 RoCE 中存在功能,iWARP 不支持这些功能。这允许在标准以太网基础架构(交换机)上使用 RDMA。只有 NIC 应该是特殊的,并支持 iWARP(如果使用 CPU 卸载),否则所有 iWARP 堆栈都可以在 SW 中实现,并且丢失了大部分的 RDMA 性能优势。

    因此,RDMA 可以简单理解为利用相关的硬件和网络技术,服务器 1 的网卡可以直接读写服务器 2 的内存,最终达到高带宽、低延迟和低资源利用率的效果。如下图所示,应用程序不需要参与数据传输过程,只需要指定内存读写地址,开启传输并等待传输完成即可。

    在这里插入图片描述

    RDMA 的主要优势总结如下:

    • Zero-Copy:数据不需要在网络协议栈的各个层之间来回拷贝,这缩短了数据流路径。
    • Kernel-Bypass:应用直接操作设备接口,不再经过系统调用切换到内核态,没有内核切换开销。
    • None-CPU:数据传输无须 CPU 参与,完全由网卡搞定,无需再做发包收包中断处理,不耗费 CPU 资源。

    在这里插入图片描述
    这么多优势总结起来就是提高处理效率,减低时延。

    实际上 RDMA 并非最近几年才提出,事实上最早实现 RDMA 的网络协议 InfiniBand 早已应用到了高性能计算中。但是 InfinBand 和传统 TCP/IP 网络相比区别非常大,需要专用的硬件设备,承担昂贵的价格,并且会大大增加运维人力成本。

    目前支持以太网的 RDMA 协议主要是 RoCE(RDMA over Converged Ethernet)和 iWARP(Internet Wide Area RDMA Protocol),系统部同学通过性能、可用性等多方面的调研后,最终引入了 RoCE 网络。RoCE 和 InfiniBand 的性能基本相近,而且比 iWARP 产业生态更加健全,主流网卡厂商都已支持。除此之外,RoCE 网络在数据链路层支持标准以太网协议,在网络层上支持 IP 协议,因此可以无缝融合到现有的 IDC 环境中,部署方便;其次由于 RoCE 网络支持标准以太网和IP协议,更加方便运维,而且设备成本更低。

    NVMe

    近几年存储行业发生了翻天覆地的变化,半导体存储登上了历史的舞台。和传统磁盘存储介质相比,半导体存储介质具有天然的优势。无论在可靠性、性能、功耗等方面都远远超越传统磁盘。SSD 存储介质和接口技术一直处于不断向前发展和演进的过程。SSD 分为几个阶段,第一个阶段是 SATA SSD 或者 SATA/SAS SSD 为主导,这个阶段介质以 SLC 和 eMLC 为主。第二个阶段是 PCIe SSD,PCIe SSD 最大的问题是不标准,很多私有化协议各自为政,基于 FTL 位置不同主要分为 Host based SSD 和 Device base SSD。 直到 NVMe 时代才统一了接口和协议标准,NVMe 主要的产品形态有三大类。第一类是和 SATA/SAS SS D兼容的 U.2,第二类是和 PCIe 兼容的 SSD 卡,第三类消费级产品常用的 M.2。
    在这里插入图片描述
    目前常用的半导体存储介质是 NVMe SSD,采用 PCIe 接口方式与主机进行交互,大大提升了性能,释放了存储介质本身的性能。NVMe SSD 推进了数据中心闪存化进程。
    在这里插入图片描述
    NVM Express(NVMe,Non-Volatile Memory express,非易失性内存主机控制器接口规范),是一个逻辑设备的接口规范。他是与 AHCI 类似的、基于设备逻辑接口的总线传输协议规范(相当于通讯协议中的应用层),用于访问通过 PCI-Express(PCIe)总线附加的非易失性内存介质,虽然理论上不一定要求 PCIe 总线协议。

    此规范目的在于充分利用 PCI-E 通道的低延时以及并行性,还有当代处理器、平台与应用的并行性,在可控制的存储成本下,极大的提升固态硬盘的读写性能,降低由于 AHCI 接口带来的高延时,彻底解放 SATA 时代固态硬盘的极致性能。

    历史上,大多数 SSD 使用如 SATA、SAS 或光纤通道等接口与计算机接口的总线连接。随着固态硬盘在大众市场上的流行,SATA 已成为个人电脑中连接 SSD 的最典型方式;但是,SATA 的设计主要是作为机械硬盘驱动器(HDD)的接口,并随着时间的推移越来越难满足速度日益提高的 SSD。随着在大众市场的流行,许多固态硬盘的数据速率提升已经放缓。不同于机械硬盘,部分 SSD 已受到 SATA 最大吞吐量的限制。

    在 NVMe 出现之前,高端 SSD 只得以采用 PCI Express 总线制造,但需使用非标准规范的接口。若使用标准化的 SSD 接口,操作系统只需要一个驱动程序就能使用匹配规范的所有 SSD。这也意味着每个 SSD 制造商不必用额外的资源来设计特定接口的驱动程序。2015 年 11 月,发布了 “NVM Express 管理接口规范” (NVMe),为非用户组件和系统提供了带外管理。NVMe 提供了一个通用的基线管理功能,它可以跨越所有的 NVMe 设备和系统,以及实现可选特性的一致方法。命令包括查询和设置配置、获取子系统的健康、固件管理、命名空间管理、安全管理等。

    NVMe 的优势

    • 性能有数倍的提升;
    • 可大幅降低延迟;
    • NVMe 可以把最大队列深度从 32 提升到 64000,SSD 的 IOPS 能力也会得到大幅提升;
    • 自动功耗状态切换和动态能耗管理功能大大降低功耗;
    • NVMe 标准的出现解决了不同 PCIe SSD 之间的驱动适用性问题。

    目前 NVMe SSD 主流采用的存储介质是 NAND Flash。最近几年 NAND Flash 技术快速发展,主要发展的思路有两条:

    1. 通过 3D 堆叠的方式增加 NAND Flash 的存储密度;
    2. 通过增加单 Cell 比特数来提升 NAND Flash 的存储密度。

    纵观 SSD 发展,NVMe 的出现虽然对接口标准和数据传输效率上得到了跨越式的提升,但是存储介质目前主流还是基于 NAND Flash 实现。那再往前发展,存储介质应该怎么发展呢?Intel Optane(傲腾)系列硬盘通过实践证明 NVMe 和 SCM(Storage Class Memory)配对时才会显现更大的存储优势,在很多时候也把 SCM 称为 VNM,那时数据存储将会迎来重大飞跃,NVMe 的未来属于 SCM。它将建立在行业协议标准而不是专有技术之上。

    扩展阅读:《联通沃云虚拟机扩展内存技术的方法验证研究

    NVMe Over Fabrics

    NVMe 传输是一种抽象协议层,旨在提供可靠的 NVMe 命令和数据传输。为了支持数据中心的网络存储,通过 NVMe over Fabric 实现 NVMe 标准在 PCIe 总线上的扩展,以此来挑战 SCSI 在 SAN 中的统治地位。NVMe over Fabric 支持把 NVMe 映射到多个 Fabrics 传输选项,主要包括 FC、InfiniBand、RoCE v2、iWARP 和 TCP。

    NVMe Over Fabrics 使用 RDMA 或光纤通道(FC)架构等 Fabric 技术取代 PCIe 传输。如图所示,除了基于 RDMA 架构的传输包括以太网(ROCE),InfiniBand 和 iWARP,当然,采用基于原生 TCP(非 RDMA)传输也是可能的(截至 2018 年 7 月,TCP 技术仍在研发阶段)。
    在这里插入图片描述
    图中所示的 NVM 子系统是一个或多个物理结构接口(端口)的集合,每个单独的控制器通常连接到单个端口。多个控制器可以共享一个端口。尽管允许 NVM 子系统的端口支持不同的 NVMe 传输,但实际上,单个端口可能仅支持单个传输类型。NVM 子系统包括一个或多个控制器,一个或多个命名空间,一个或多个 PCI Express 端口,非易失性存储器存储介质,以及控制器和非易失性存储器存储介质之间的接口。

    详细资料请参考:

    • https://mp.weixin.qq.com/s/gl-RbUUtdtxK3o17nFNcxA
    • https://mp.weixin.qq.com/s/2LTuDRDGeSPefCJdWnLH7w

    InfiniBand 架构

    InfiniBand 是一种基于交换结构和点到点通信的 I/O 接口,是一种长缆线的连接方式,具有高速、低延迟的传输特性。InfiniBand 以极高的传输速度将服务器、存储设备和网络设备连接在一起。基于 InfiniBand 技术的网卡的单端口带宽可达 20Gbps,最初主要用在高性能计算系统中,近年来随着设备成本的下降,InfiniBand 也逐渐被用到企业数据中心。

    InfiniBand 由 4 中基本设备组成,包括主机通道适配器 HCA、目标通道适配器 TCA、交换机及路由器。其中 HCA 功能较强,具有微处理器,参与管理。TCA 功能比较简单。交换机和路由器是用于连接的设备。
    在这里插入图片描述
    为了发挥 InfiniBand 设备的性能,需要一整套的软件栈来驱动和使用,这其中最著名的就是 OFED(OpenFabrics Enterprise Distribution),基于 InfiniBand 的设备实现了 RDMA(Remote Direct Memory Access)。RDMA 的最主要的特点就是零拷贝和旁路操作系统,数据直接在外部设备和应用程序内存之间传递,这种传递不需要 CPU 的干预和上下文切换。OFED 还实现了一系列的其它软件栈:IPoIB(IP over InfiniBand)、SRP(SCSI RDMA Protocol)等,这就为 InfiniBand 聚合存储网络和互联网络提供了基础。OFED 现已经被主流的 Linux 发行版本支持,并被整合到微软的 Windows Server 中。
    在这里插入图片描述

    以太网光纤通道 FCoE

    就在大家认为 InfiniBand 是数据中心连接技术的未来时,10Gb 以太网的出现让人看到了其它选择,以太网的发展好像从来没有上限,目前它的性能已经接近 InfiniBand,而从现有网络逐渐升级到 10Gb 以太网也更易为用户所接受。

    FCoE(FiberChannel Over Ethernet)的出现为数据中心互联网络和存储网络的聚合提供了另一种可能。FCoE 可将传统的 FC 存储网络功能融合到以太网中,思路是将光纤信道直接映射到以太网线上,这样光纤信道就成了以太网线上除了互联网网络协议之外的另一种网络协议。FCoE 能够很容易的和传统光纤网络上运行的软件和管理工具相整合,因而能够代替光纤连接存储网络。虽然出现的晚,但 FCoE 发展极其迅猛。与 InfiniBand 技术需要采用全新的链路相比,企业 IT 更愿意升级已有的以太网。在两者性能接近的情况下,采用 FCoE 方案似乎性价比更高。

    大型数据中心利用 FCoE 等新技术将存储传输和 IP 传输融合到以太网连接上,而标准的 STP 将不再适合融合网络或超大型数据中心的扩展。随着 FCoE 采用率的提高,企业存储开始考虑加入 IP 网络上的其他协议,从存储的角度来看,TRILL 可以代替 STP。

    展开全文
  • Android 匿名共享内存C++接口分析

    千次阅读 2013-09-17 22:52:58
    l在上一篇Android 匿名共享内存C接口分析中介绍了Android系统的匿名共享内存C语言访问接口,本文在前文的基础上继续介绍Android系统的匿名共享内存提供的C++访问接口。在C++层通过引入Binder进程间通信机制可以实现...

    在上一篇Android 匿名共享内存C接口分析中介绍了Android系统的匿名共享内存C语言访问接口,本文在前文的基础上继续介绍Android系统的匿名共享内存提供的C++访问接口。在C++层通过引入Binder进程间通信机制可以实现跨进程访问匿名共享内存。我们知道Android匿名共享内存的设计本身就是为了实现进程间共享大量数据,当源进程开辟一块匿名共享内存并把这块匿名共享内存映射到当前进程的虚拟地址空间从而使当前进程可以直接访问这块匿名共享内存后,如何让目标进程共享访问这块匿名共享内存呢?这就需要利用Binder进程间通信方式将匿名共享内存的文件描述符传输给目标进程,目标进程才能将得到的匿名共享内存文件描述符映射到本进程地址空间中,只有这样目标进程才能访问源进程创建的这块匿名共享内存。将Binder进程间通信方式与匿名共享内存相结合实现小数据量传输换来大数据量共享。


    由于这里用到了Binder进程间通信机制,这里再次贴上Android系统的Binder通信设计框架,关于Binder通信的具体分析,请查看Binder通信模块中的一系列文章。


    MemoryHeapBase

    Android使用MemoryHeapBase接口来实现进程间共享一个完整的匿名共享内存块,通过MemoryBase接口来实现进程间共享一个匿名共享内存块中的其中一部分,MemoryBase接口是建立在MemoryHeapBase接口的基础上面的,都可以作为一个Binder对象来在进程间传输。首先介绍MemoryHeapBase如何实现一个完整匿名共享内存块的共享,下面是MemoryHeapBase在Binder通信框架中的类关系图:


    既然Android的匿名共享内存涉及到了Binder通信机制,那就必定包括客户端和服务端两种实现方式。图中黄色标识的类及MemoryHeapBase是匿名共享内存在服务进程中的实现类,而粉红色标识的类则是匿名共享内存在Client进程中的引用类。IMemoryHeapding接口定义了匿名共享内存访问接口;BpMemoryHeap定义了匿名共享内存在客户进程中的业务逻辑,BpInterface,BpRefBase,BpBinder则是为业务逻辑层的BpMemoryHeap提供进程间通信支持,ProcessState和IPCThreadState是Binder进程和线程的抽象操作类,为BpBinder和Binder驱动交互提供辅助接口。黄色标识类BBinder,BnInterface是服务进程中用于支持Binder通信实现类,BnMemoryHeap用于分发关于匿名共享内存请求命令,而MemoryHeapBase实现了匿名共享内存在服务进程的所有业务逻辑。了解了整个匿名共享内存的框架设计后,接下来根据代码具体分析Android是如何在进程间共享匿名共享内存的。在IMemoryBase类中定义了匿名共享内存的访问接口:

    class IMemoryHeap : public IInterface
    {
    public:
        DECLARE_META_INTERFACE(MemoryHeap);
    	...
    	//用来获得匿名共享内存块的打开文件描述符
        virtual int         getHeapID() const = 0;
    	//用来获得匿名共享内存块的基地址
        virtual void*       getBase() const = 0;
    	//用来获得匿名共享内存块的大小
        virtual size_t      getSize() const = 0;
    	//用来获得匿名共享内存块的保护标识位
        virtual uint32_t    getFlags() const = 0;
    	//用来获得匿名共享内存块中的偏移量
        virtual uint32_t    getOffset() const = 0;
    	...
    };

    Server端

    MemoryHeapBase类实现了IMemoryBase提供的接口函数,位于frameworks\native\libs\binder\MemoryBase.cpp,MemoryHeapBase提供了四种构造函数:

    MemoryHeapBase::MemoryHeapBase()
        : mFD(-1), mSize(0), mBase(MAP_FAILED),
          mDevice(NULL), mNeedUnmap(false), mOffset(0)
    {
    }

    size表示要创建的匿名共享内存的大小,flags是用来设置这块匿名共享内存的属性的,name是用来标识这个匿名共享内存的名字,mFD表示匿名共享内存的文件描述符,mBase标识匿名共享内存的基地址,mDevice表示匿名共享内存的设备文件。这个构造函数只是简单初始化了各个成员变量。

    MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
        : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
          mDevice(0), mNeedUnmap(false), mOffset(0)
    {
    	//获得系统中一页大小的内存
        const size_t pagesize = getpagesize();
    	//内存页对齐
        size = ((size + pagesize-1) & ~(pagesize-1));
    	//创建一块匿名共享内存
        int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
        ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
        if (fd >= 0) {
    		//将创建的匿名共享内存映射到当前进程地址空间中
            if (mapfd(fd, size) == NO_ERROR) {
                if (flags & READ_ONLY) {//如果地址映射成功,修改匿名共享内存的访问属性
                    ashmem_set_prot_region(fd, PROT_READ);
                }
            }
        }
    }
    在以上构造函数中根据参数,利用匿名共享内存提供的C语言接口创建一块匿名共享内存,并映射到当前进程的虚拟地址空间,参数size是指定匿名共享内存的大小,flags指定匿名共享内存的访问属性,name指定匿名共享内存的名称,如果没有指定名称,默认命名为MemoryHeapBase。

    MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
        : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
          mDevice(0), mNeedUnmap(false), mOffset(0)
    {
        int open_flags = O_RDWR;
        if (flags & NO_CACHING)
            open_flags |= O_SYNC;
    	//打开匿名共享内存设备文件
        int fd = open(device, open_flags);
        ALOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno));
        if (fd >= 0) {
    		//将指定的匿名共享内存大小按页对齐
            const size_t pagesize = getpagesize();
            size = ((size + pagesize-1) & ~(pagesize-1));
    		//将匿名共享内存映射到当前进程地址空间
            if (mapfd(fd, size) == NO_ERROR) {
                mDevice = device;
            }
        }
    }
    该构造函数通过指定已创建的匿名共享内存的设备文件来打开该共享内存,并映射到当前进程地址空间。device指定已经创建的匿名共享内存的设备文件路径,size指定匿名共享内存的大小。

    MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
        : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
          mDevice(0), mNeedUnmap(false), mOffset(0)
    {
    	//指定匿名共享内存大小按页对齐
        const size_t pagesize = getpagesize();
        size = ((size + pagesize-1) & ~(pagesize-1));
        mapfd(dup(fd), size, offset);
    }

    以上四种构造函数都通过mapfd函数来将匿名共享内存映射到当前进程的虚拟地址空间,以便进程可以直接访问匿名共享内存中的内容。

    status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
    {
        if (size == 0) {
            // try to figure out the size automatically
    #ifdef HAVE_ANDROID_OS
            // first try the PMEM ioctl
            pmem_region reg;
            int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®);
            if (err == 0)
                size = reg.len;
    #endif
            if (size == 0) { // try fstat
                struct stat sb;
                if (fstat(fd, &sb) == 0)
                    size = sb.st_size;
            }
            // if it didn't work, let mmap() fail.
        }
    
        if ((mFlags & DONT_MAP_LOCALLY) == 0) {
    		//通过mmap系统调用进入内核空间的匿名共享内存驱动,并调用ashmem_map函数将匿名共享内存映射到当前进程
            void* base = (uint8_t*)mmap(0, size,PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
            if (base == MAP_FAILED) {
                ALOGE("mmap(fd=%d, size=%u) failed (%s)",fd, uint32_t(size), strerror(errno));
                close(fd);
                return -errno;
            }
            mBase = base;
            mNeedUnmap = true;
        } else  {
            mBase = 0; // not MAP_FAILED
            mNeedUnmap = false;
        }
        mFD = fd;
        mSize = size;
        mOffset = offset;
        return NO_ERROR;
    }
    mmap函数的第一个参数0表示由内核来决定这个匿名共享内存文件在进程地址空间的起始位置,第二个参数size表示要映射的匿名共享内文件的大小,第三个参数PROT_READ|PROT_WRITE表示这个匿名共享内存是可读写的,第四个参数fd指定要映射的匿名共享内存的文件描述符,第五个参数offset表示要从这个文件的哪个偏移位置开始映射。调用了这个函数之后,最后会进入到内核空间的ashmem驱动程序模块中去执行ashmem_map函数,调用mmap函数返回之后,就得这块匿名共享内存在本进程地址空间中的起始访问地址了,将这个地址保存在成员变量mBase中,最后,还将这个匿名共享内存的文件描述符和以及大小分别保存在成员变量mFD和mSize,并提供了相应接口函数来访问这些变量值。通过构造MemoryHeapBase对象就可以创建一块匿名共享内存,或者映射一块已经创建的匿名共享内存到当前进程的地址空间。

    int MemoryHeapBase::getHeapID() const {
        return mFD;
    }
    
    void* MemoryHeapBase::getBase() const {
        return mBase;
    }
    
    size_t MemoryHeapBase::getSize() const {
        return mSize;
    }
    
    uint32_t MemoryHeapBase::getFlags() const {
        return mFlags;
    }
    
    const char* MemoryHeapBase::getDevice() const {
        return mDevice;
    }
    
    uint32_t MemoryHeapBase::getOffset() const {
        return mOffset;
    }

    Client端

    前面介绍了匿名共享内存服务端的实现,那客户端如果通过Binder通信中的RPC方式访问匿名共享内存的服务端,从而得到相应的服务呢?Android提供了BpMemoryHeap类来请求服务,位于frameworks\native\libs\binder\IMemory.cpp
    BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)
        : BpInterface<IMemoryHeap>(impl),
            mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mOffset(0), mRealHeap(false)
    {
    }
    在BpMemoryHeap的构造函数里只是初始化一些成员变量,BpMemoryHeap提供了和服务端MemoryHeapBase相对应的服务函数:
    int BpMemoryHeap::getHeapID() const {
        assertMapped();
        return mHeapId;
    }
    
    void* BpMemoryHeap::getBase() const {
        assertMapped();
        return mBase;
    }
    
    size_t BpMemoryHeap::getSize() const {
        assertMapped();
        return mSize;
    }
    
    uint32_t BpMemoryHeap::getFlags() const {
        assertMapped();
        return mFlags;
    }
    
    uint32_t BpMemoryHeap::getOffset() const {
        assertMapped();
        return mOffset;
    }
    通过BpMemoryHeap代理对象访问匿名共享内存前都会调用函数assertMapped()来判断是否已经向服务获取到匿名共享内存的信息,如果没有就向服务端发起请求:
    void BpMemoryHeap::assertMapped() const
    {
        if (mHeapId == -1) {//如果还没有请求服务创建匿名共享内存
    		//将当前BpMemoryHeap对象转换为IBinder对象
            sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());
    		//从成员变量gHeapCache中查找对应的BpMemoryHeap对象
            sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
    		//向服务端请求获取匿名共享内存信息
            heap->assertReallyMapped();
    		//判断该匿名共享内存是否映射成功
            if (heap->mBase != MAP_FAILED) {
                Mutex::Autolock _l(mLock);
    			//保存服务端返回回来的匿名共享内存信息
                if (mHeapId == -1) {
                    mBase   = heap->mBase;
                    mSize   = heap->mSize;
                    mOffset = heap->mOffset;
                    android_atomic_write( dup( heap->mHeapId ), &mHeapId );
                }
            } else {
                // something went wrong
                free_heap(binder);
            }
        }
    }
    mHeapId等于-1,表示匿名共享内存还为准备就绪,因此请求服务端MemoryHeapBase创建匿名共享内存,否则该函数不作任何处理。只有第一次使用匿名共享时才会请求服务端创建匿名共享内存。由于在客户端进程中使用同一个BpBinder代理对象可以创建多个与匿名共享内存业务相关的BpMemoryHeap对象,因此定义了类型为HeapCache的全局变量gHeapCache用来保存创建的所有BpMemoryHeap对象,assertMapped函数首先将当前BpMemoryHeap对象强制转换为IBinder类型对象,然后调用find_heap()函数从全局变量gHeapCache中查找出对应的BpMemoryHeap对象,并调用assertReallyMapped()函数向服务进程的BnemoryHeap请求创建匿名共享内存。
    void BpMemoryHeap::assertReallyMapped() const
    {
        if (mHeapId == -1) {//再次判断是否已经请求创建过匿名共享内存
            Parcel data, reply;
            data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
    		//向服务端BnMemoryHeap发起请求
            status_t err = remote()->transact(HEAP_ID, data, &reply);
            int parcel_fd = reply.readFileDescriptor();
            ssize_t size = reply.readInt32();
            uint32_t flags = reply.readInt32();
            uint32_t offset = reply.readInt32();
    		//保存服务进程创建的匿名共享内存的文件描述符
            int fd = dup( parcel_fd );
            int access = PROT_READ;
            if (!(flags & READ_ONLY)) {
                access |= PROT_WRITE;
            }
            Mutex::Autolock _l(mLock);
            if (mHeapId == -1) {
    			//将服务进程创建的匿名共享内存映射到当前客户进程的地址空间中
                mRealHeap = true;
                mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
                if (mBase == MAP_FAILED) {
                    ALOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)",asBinder().get(), size, fd, strerror(errno));
                    close(fd);
                } else {//映射成功后,将匿名共享内存信息保存到BpMemoryHeap的成员变量中,供其他接口函数访问
                    mSize = size;
                    mFlags = flags;
                    mOffset = offset;
                    android_atomic_write(fd, &mHeapId);
                }
            }
        }
    }
    该函数首先通过Binder通信方式向服务进程请求创建匿名共享内存,当服务端BnMemoryHeap对象创建完匿名共享内存后,并将共享内存信息返回到客户进程后,客户进程通过系统调用mmap函数将匿名共享内存映射到当前进程的地址空间,这样客户进程就可以访问服务进程创建的匿名共享内存了。当了解Binder通信机制,就知道BpMemoryHeap对象通过transact函数向服务端发起请求后,服务端的BnMemoryHeap的onTransact函数会被调用。
    status_t BnMemoryHeap::onTransact(
            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        switch(code) {
           case HEAP_ID: {
                CHECK_INTERFACE(IMemoryHeap, data, reply);
                reply->writeFileDescriptor(getHeapID());
                reply->writeInt32(getSize());
                reply->writeInt32(getFlags());
                reply->writeInt32(getOffset());
                return NO_ERROR;
            } break;
            default:
                return BBinder::onTransact(code, data, reply, flags);
        }
    }
    服务端的BnMemoryHeap对象将调用服务端的匿名共享内存访问接口得到创建的匿名共享内存信息,并返回给客户端进程,MemoryHeapBase是BnMemoryHeap的子类,实现了服务端的匿名共享内存访问接口。全局变量gHeapCache用来保存客户端创建的所有BpMemoryHeap对象,它的类型为HeapCache

    BpMemoryHeap通过find_heap()函数从全局变量gHeapCache中查找当前的BpMemoryHeap对象,查找过程其实是调用HeapCache类的find_heap()函数从向量mHeapCache中查找,该向量以键值对的形式保存了客户端创建的所有BpMemoryHeap对象。为什么要保存所有创建的BpMemoryHeap对象呢?虽然客户端可以创建多个不同的BpMemoryHeap对象,但他们请求的匿名共享内存在服务端确实同一块。当第一个BpMemoryHeap对象从服务端获取匿名共享内存的信息并在本进程中映射好这块匿名共享内存之后,其他的BpMemoryHeap对象就可以直接使用了,不需要再映射了。下图显示了匿名共享内存在客户端和服务端分别提供的访问接口:


    通过对Android匿名共享内存C++层的数据结构分析及Binder通信的服务端和客户端分析,总结一下匿名共享内存的C++访问方式:
    1)服务端构造MemoryHeapBase对象时,创建匿名共享内存,并映射到服务进程的地址空间中,同时提供获取该匿名共享内存的接口函数;
    2)客户端通过BpMemoryHeap对象请求服务端返回创建的匿名共享内存信息,并且将服务端创建的匿名共享内存映射到客户进程的地址空间中,在客户端也提供对应的接口函数来获取匿名共享内存的信息;

    MemoryBase

    MemoryBase接口是建立在MemoryHeapBase接口的基础上的,它们都可以作为一个Binder对象来在进程间进行数据共享

    和MemoryHeapBase类型,首先定义了IMemory,同时定义了客户端的BpMemory代理类,服务端的BnMemory及其子类MemoryBase,熟悉Binder进程间通信框架就应该很请求各个类之间的关系,这里不在介绍Binder通信层的相关类,而是直接介绍在通信层上面的业务逻辑层的相关类。IMemory类定义了MemoryBase类所需要实现的接口,这个类定义在frameworks/base/include/binder/IMemory.h
    class IMemory : public IInterface
    {
    public:
        DECLARE_META_INTERFACE(Memory);
        virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
        // helpers
        void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
        void* pointer() const;
        size_t size() const;
        ssize_t offset() const;
    };
    在IMemory类中实现了大部分接口函数,只有getMemory函数是在IMemory的子类MemoryBase中实现的。成员函数getMemory用来获取内部的MemoryHeapBase对象的IMemoryHeap接口。
    void* IMemory::pointer() const {
        ssize_t offset;
        sp<IMemoryHeap> heap = getMemory(&offset);
        void* const base = heap!=0 ? heap->base() : MAP_FAILED;
        if (base == MAP_FAILED)
            return 0;
        return static_cast<char*>(base) + offset;
    }
    函数pointer()用来获取内部所维护的匿名共享内存的基地址
    size_t IMemory::size() const {
        size_t size;
        getMemory(NULL, &size);
        return size;
    }
    函数size()用来获取内部所维护的匿名共享内存的大小
    ssize_t IMemory::offset() const {
        ssize_t offset;
        getMemory(&offset);
        return offset;
    }
    函数offset()用来获取内部所维护的这部分匿名共享内存在整个匿名共享内存中的偏移量
    由于这三个接口函数都在IMemory类中实现了,因此无论是在Binder进程间通信的客户端的BpMemory还是服务端的MemoryBase类中都不在实现这三个接口函数,从以上三个接口函数的实现可以看出,它们都是调用getMemory函数来获取匿名共享内存的基地址,大小及偏移量,那getMemory函数在客户端和服务端的实现有何区别呢,下面分别介绍客户端BpMemory和服务端MemoryBase中getMemory函数的实现过程。

    客户端

    在客户端的BpMemory类中,通过Binder进程间通信机制向服务端MemoryBase发起请求:
    frameworks\native\libs\binder\IMemory.cpp
    sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const
    {
        if (mHeap == 0) {//指向的匿名共享内存MemoryHeapBase为空
            Parcel data, reply;
            data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
    		//向服务端MemoryBase发起RPC请求
            if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
    			//读取服务端返回来的结果
                sp<IBinder> heap = reply.readStrongBinder();//读取匿名共享内存MemoryHeapBase的IBinder对象
                ssize_t o = reply.readInt32();//读取匿名共享内存中的偏移量
                size_t s = reply.readInt32();//读取匿名共享内存的大小
    			//如果服务端返回来的用于描述整块匿名共享内存的MemoryHeapBase不为空
                if (heap != 0) {
                    mHeap = interface_cast<IMemoryHeap>(heap);
                    if (mHeap != 0) {//将匿名共享内存的偏移和大小保存到成员变量中
                        mOffset = o;
                        mSize = s;
                    }
                }
            }
        }
    	//将成员变量赋值给传进来的参数,从而修改参数值
        if (offset) *offset = mOffset;
        if (size) *size = mSize;
        return mHeap;
    }

    服务端

    当客户端的BpMemory向服务端MemoryBase发起RPC请求后,服务端的BnMemory对象的onTransact函数被调用
    status_t BnMemory::onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        switch(code) {
            case GET_MEMORY: {
    	    //根据客户端发送过来的接口描述进行检查确认
                CHECK_INTERFACE(IMemory, data, reply);
                ssize_t offset;
                size_t size;
    	    //调用服务端的getMemory函数获取匿名共享内存对象MemoryHeapBase及匿名共享内存大小,偏移,并返回给客户端
                reply->writeStrongBinder(getMemory(&offset, &size)->asBinder() );
    	    //将偏移量返回给客户端
                reply->writeInt32(offset);
    	    //将匿名共享内存大小返回给客户端
                reply->writeInt32(size);
                return NO_ERROR;
            } break;
            default:
                return BBinder::onTransact(code, data, reply, flags);
        }
    }
    服务端的getMemory函数由BnMemory的子类MemoryBase实现
    sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
    {
        if (offset) *offset = mOffset;
        if (size)   *size = mSize;
        return mHeap;
    }
    这里只是将成员变量的值赋给传进来的参数,从而修改参数值,由于参数类型为指针类型,因此形参的改变必然改变实参。那服务端MemoryBase的成员变量mSize,mOffset,mHeap是在什么时候赋值的呢?MemoryBase的构造函数如下:
    MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap,
            ssize_t offset, size_t size)
        : mSize(size), mOffset(offset), mHeap(heap)
    {
    }
    从MemoryBase的构造函数可以看出,它的成员变量值都是在构造MemoryBase对象是被初始化的。参数heap指向的是一个MemoryHeapBase对象,真正的匿名共享内存就是由它来维护的,参数offset表示这个MemoryBase对象所要维护的这部分匿名共享内存在整个匿名共享内存块中的起始位置,参数size表示这个MemoryBase对象所要维护的这部分匿名共享内存的大小。MemoryBase用于共享匿名共享内存中的部分内存,在构造MemoryBase对象是指定共享的整块匿名共享内存为mHeap,大小为mSize,被共享的内存在整块匿名共享内存中的偏移量为mOffset。客户端通过Binder进程间通信方式获取用于描述部分共享内存的MemoryBase对象信息,MemoryBase只是用来维护部分匿名共享内存,而匿名共享内存的创建依然是通过MemoryHeapBase来完成的。

    展开全文
  • Java数组、类、接口多态的内存逻辑模型以及接口和抽象类的区别 Copyright©stonee新博客 在我之前的博客java概述和编译过程中已经大概涉及到了java代码的内存逻辑模型,在博客java多态中也涉及到了类的多态,这次...
  • java中引用数据类型有哪些

    万次阅读 多人点赞 2016-07-18 09:45:37
    Java中俩种数据类型,其中主要8中基本数据类型和引用数据类型,除了8中基本数据类型以外都是引用数据类型,8中基本数据类型分别是byte,short,int,long,char,boolean,float,double,具体如下:1、boolean:数据值...
  • C# “值类型“和“引用类型“在内存的分配

    千次阅读 多人点赞 2021-08-01 01:37:43
    在代码中每创建一个变量,...值类型有哪些? int"整数" double"高精度小数" char(单字符) decimal(小数) bool(布尔) enum(枚举) struct(结构) 等等… 引用类型有哪些? string 数组 自定义类 集合 object 接口 等等…
  • 背景:FPGA的片上BRAM内存较少,难以实现大量的存储。DDR的内存较多,若可直接用FPGA调用则可以实现很多东西。 目的:运用MIG调用DDR相关文档学习。 参阅的文档:DS176:Zynq-7000 AP SoC and 7 Series FPGAs ...
  • 在Android系统中,针对移动...为了方便使用匿名共享内存机制,系统还提供了Java调用接口(MemoryFile)和C++调用接口(MemoryHeapBase、MemoryBase),Java接口在前面也已经分析过了,本文中将继续分析它的C++接口
  • java中的基本数据类型和引用数据类型以及它们的存储方式堆内存和栈内存
  • 当您定义Pod的时候可以选择为每个容器指定需要的 CPU 和内存(RAM)大小。 Pod 中的每个容器都可以指定以下的一个或者多个值: spec.containers[].resources.limits.cpu spec.containers[].resources.limits....
  • 在Java中常见的数据类型有哪些

    千次阅读 2020-04-21 22:12:14
    在java中常见的数据类型有哪些 ? 看图 看图 看图 重要的事情说三遍 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 内置数据类型 类型名称 字节、位数 ...
  • halcon/c++接口基础 之内存管理

    千次阅读 2016-08-31 23:19:18
    所有的HALCON类,不仅仅HImage,HRegion,HTuple,HFramegrabber等等,还有面向过程的方法中使用的Hobject,都可以使用默认的析构器自动释放内存。 ( see also section 2.4 “Destructors and Halcon Operators”))...
  • 硬盘接口是硬盘与主机系统间的连接部件,作用是在硬盘缓存和主机内存之间传输数据。不同的硬盘接口决定着硬盘与计算机之间的连接速度,在整个系统中,硬盘接口的优劣直接影响着程序运行快慢和系统性能好坏。从整体的...
  • 深入理解Java虚拟机-Java内存区域与内存溢出异常

    万次阅读 多人点赞 2020-01-03 21:42:24
    Java与C++之间一堵由内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来。 文章目录概述运行时数据区域程序计数器(线程私有)Java虚拟机栈(线程私有)局部变量表操作数栈动态链接...
  • Flink类型系统的根及基本接口

    千次阅读 2016-07-09 21:18:58
    之前我们谈到了Flink通过自主管理内存的方式来,避免了让JVM管理内存带来的一些问题。自主管理内存之后,JVM中原生的类型也就不适合使用了。因此Flink也对Java的类型进行了扩展,这就是我们本节关注的内容。
  • byte:Java中最小的数据类型,在内存中占1个字节(8 bit),取值范围-128~127,默认值0 short:短整型,2个字节(16 bit),取值范围-32768~32717,默认值0 int:整型,用于存储整数,在内存中占4个字节,取值...
  • Linux内存描述之概述--Linux内存管理(一)

    万次阅读 多人点赞 2016-08-31 13:29:34
    日期 内核版本 架构 作者 GitHub CSDN 2016-08-31 Linux-4.7 X86 & arm ... 链接 内存管理(一)内存模型之Node Linux 内存管理 重要结构体 Bootmem机制 Linux-2.6.32 NUMA架构之内存和调度
  • IO端口和IO内存的区别及分别使用的函数接口   每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口,通常包括:控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式,可以把CPU...
  • C#中引用类型和值类型分别有哪些

    万次阅读 2018-06-15 02:12:58
    C#的引用类型包括:数组,用户定义的类、接口、委托,object,字符串。 数组的元素,不管是引用类型还是值类型,都存储在托管堆上。 引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。为了方便,本文简称...
  • 堆外内存泄漏排查

    万次阅读 2020-08-21 14:42:39
    直接内存有哪些? 元空间。 BIO中ByteBuffer分配的直接内存。 使用Java的Unsafe类做一些分配本地内存的操作。 JNI或者JNA程序,直接操纵了本地内存,比如一些加密库、压缩解压等。 JNI(Java Native Interface):...
  • 日期 内核版本 架构 作者 GitHub ... Linux内存管理在内存管理的上下文中, 初始化(initialization)可以多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到
  • 1、 值类型和引用类型的区别 1. 值类型的数据存储在内存的栈中;...3. 值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用4. 值类型继承自System.ValueType,引用类型继承自System.Object5. 
  • 它确实给初学者提供了几组常用API的简单demo,正常情况下这些代码都是能跑通的,但近来我在日常工作中,在做一些【内存泄露】相关的【代码优化】工作,所以对这个切入点比较敏感,果不其然,细读其中的部分示例...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 757,257
精华内容 302,902
关键字:

内存接口类型有哪些