假定一个机器人导航,其状态用$\vec{x_k}$描述:
采用一个向量描述状态:
两个假设:
我们考虑速度和位置是有关联的:
矩阵描述问题
协方差用于衡量p和v两个变量的总体误差,
状态迁移描述:
其中的预测过程用一个矩阵描述Fk:
我们预测速度和位置的迁移公式:
预测矩阵描述:
虽然有了预测矩阵,但是我们还不知道如何更新协方差矩阵;
这样我们用下面的方法更新协方差矩阵:
注意这里是大写的P,代表概率,协方差概率;
]]>前端与权限
微服务中的身份
API gateway
如何解决前端与API消费者同时鉴权问题
RDBC
JWT1
jwt2
ominiplan_tool
线程安全是多线程程序中的编程理念,一段代码线程安全意味着在多个线程执行的时候能够运行正确。因此,它必须满足:
rel="nofollow"
属性以避免受到攻击;OAuth2 and OpenID Connect
Securing each step of the request’s journey
favicon.ico
文件, 比如. /favicon.ico
。浏览器会自动请求该文件,甚至在HTML中无需提及;如果没有这样的文件/favicon.ico
,会产生多个404错误,消耗网站带宽;example.com/pages/45-article-title
而不是example.com/index.php?page=45
; #
替换诶#!
; click here
的链接;/sitemap.xml
;<link rel="canonical" ... />
;我们主要关注ConnectionSocketFactory,用于创建和连接socket,
可以实现为非加密socket:
|
|
也可以实现为加密socket;
也可以继续扩展增加新的方法:
|
|
将基于HttpURLConnection类的HTTPClient修改为HttpClient4.3.2:
在httpClient中可以自定义连接重用的策略,定义了一个接口类ConnectionReuseStrategy:
|
|
其缺省的实现类为org.apache.http.impl.DefaultConnectionReuseStrategy:
其中该类似实现了接口类的对外方法keepAlive。当然我们可以自定义一个连接重用策略:
调用者为org.apache.http.impl.execchain.MainClientExec:
连接关闭的调用条件:
其中内部实现如下:
SSLConnectionSocketFactory类可以基于SSLSocketFactory、支持的协议、支持的密码套件进行构造:
其中supportCipherSuites作为自定义密码套件在握手过程中检查。
]]>本篇英文原文所发布的站点Adam Petersen是一个个人网站,本文翻译了其中reactor C实现章节, 水平有限,欢迎指正。
1、多客户端示例
2、单一责任原则
3、违反Open-Closed原则
4、从性能谈起
5、问题总结
6、REACTOR模式
6.1、事件探测
6.2、实现机制
6.3、Reactor注册策略
6.4、Reactor实现
6.5、触发Reactor
6.6、处理注册
6.7、事件多样性
7、REACTOR vs OBSERVER
8、结论
9、总结
本文将研究一种适用于event-driven应用的模式。Reactor Pattern将应用的不同职责解耦,允许应用由多个潜在的client端分离并分发事件。
我们可以以单位时间内的操作次数定义性能,比如每秒请求数,每秒交易数;
可扩展性: 指通过增加计算资源以提高处理负载的能力,分为水平扩展和垂直扩展;
对于垂直扩展,我们针对单一机器本身,比如用SSD替换HDD,换个更快的CPU等,往往效果有限;
而水平扩展则不然,我们通过增加更多服务器将性能提升至几十倍上千倍,但同时难度也更大,需要确保数据的一致性;
对于WEB服务,如果我们只是单纯增加服务器数量,数据库势必很快将成为瓶颈,因此我们引入cache;
注:这里的数据库一般指传统的关系型数据库,支持用于完成业务逻辑的SQL;
Cache的几个概念:
“多久更新,如何更新”
从调用者与cache的交互方式上,我们可以分为三大类:
作为用户访问云服务的重要手段,SDK往往充当了proxy的角色,其内部加入L2 cache,可有效降低对服务本身的访问压力;
从架构上划分,cache可分为本地与分布式;
当然其缺点也很明显,首先本次缓存与程序紧耦合,多个应用程序无法共享缓存,各应用和集群节点都必须维护自己的单独缓存,也是一种浪费;另外,为了保证多个进程间的数据一致性,程序相对不易维护;
其中Ehcache的缓存数据有两级:内存和磁盘,与一般的本地内存缓存相比,有了磁盘的存储空间,将可以支持更大量的数据缓存需求;
直接实现
a. 成员变量或局部变量实现
|
|
以局部变量map结构缓存部分业务数据,减少频繁的重复数据库I/O操作。缺点仅限于类的自身作用域内,类间无法共享缓存。
b. 静态变量实现
最常用的单例实现静态资源缓存,代码示例如下:
|
|
O2O业务中常用的城市基础基本信息判断,通过静态变量一次获取缓存内存中,减少频繁的I/O读取,静态变量实现类间可共享,进程内可共享,缓存的实时性稍差。为了解决本地缓存数据的实时性问题,目前大量使用的是结合ZooKeeper的自动发现机制,实时变更本地静态变量缓存:
TODO:实际项目:相对静态数据,比如元数据可以基于本次cache加速,
美团点评内部的基础配置组件MtConfig,采用的就是类似原理,使用静态变量缓存,结合ZooKeeper的统一管理,做到自动动态更新缓存,如图2所示。
美团
TODO1:根据期望到货时间排序通知,需要考虑加入本地cache, EBOOK – REDIS IN ACTION
TODO2: Pub/Sub:在更新中保持用户对数据的映射是系统中的一个普遍任务。Redis的pub/sub功能使用了SUBSCRIBE、UNSUBSCRIBE和PUBLISH命令,让这个变得更加容易。到货通知
Java Cache Benchmark
sping cache
目前的实现memcached、redis和tair;
分布式cache性能对比
redis
Understanding transaction pitfalls
网络爬虫
stakcoveflow架构
Global Cache
网络拓扑同步用到了global cache,流程:
Secure Software and Secure Development Tools
手段:
其中JAVA的静态扫描示例:
Redis基于TCL编写测试框架;
Spring Boot中的缓存支持(一)注解配置与EhCache使用
多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识), 类已经序列化,serialVersionUID是JAVA的序列化过程中用于标识和识别序列化类的;
Serializable 则是应用于Java 对象序列化/反序列化, 作用:
框架实现:
调用过程分析:
|
|
其中cache执行器CacheingExecutor, 如果配置了使用cache的标志(在mapper.xml中),直接从cache中获取;
用途:解耦调用方和被调用方
TODO:
在一些文献中关于 reactor 的描述中,定义了一个工具,即 synchronous Event Demultiplexer , 该分离器由 reactor 调用等待已经注册的 Handles 的事件。
该事件分离器一般由操作系统提供,比如 poll() ,select() 和 WaitForMutipleObjects() 。
EventHandler 和 Reactor 之间的合作关系类似OBSERVER模式中的observer和它的对象。
为了将Reactor和它的事件处理器解耦,同时Reactor仍能够通知到他们,每个具体的事件处理器必须关联一个唯一的instance。这里的C实现中,采用void 作为通用类型以描述 *EventHandler 接口。
|
|
应用REACTOR模式的主要结论如下:
对于类似订单中心的多Key类业务,在数据量较大,需要对数据库进行水平切分时,对于后台需求,采用“前台后台分离”的架构设计方法:
|
|
Reactor模式通过对不同责任的解耦和不同模块的封装简化了事件驱动型应用的设计。关于该模式更多的讨论可以参考面向对象的软件模式卷2。
]]>生产者-消费者模式即所谓的Fan-in设计模式,这里建立两个channel,一个由多个生产者将生产的数据写入;另外一个由一个消费者读出。
线程同步由主线程借助两个channel完成,其本质是利用了阻塞实现同步。
一个大loop将每个新的i值循环写入。
|
|
|
|
从写入channel rang loop的读取,塞入输出channel。
|
|
这里利用了阻塞channel的特性:如果读取不到则阻塞,可以加入超时。
现象:
|
|
将生产者的loop变成有限次执行后退出。
|
|
将会导致main线程和reader线程阻塞:
|
|
|
|
|
|
将消费者行为改为对数据不做消费:
|
|
导致多个线程都往channel中写数据,这里channel是不一致的(可以一致):
|
|
|
|
out无人消费,导致写了一个就已经满了,因此阻塞。
|
|
发现ch数据没有被消费,写不进去而阻塞。
Close行为与loop之间通过s.closing
|
|
通过一个channel发送消息给待关闭的thread,将channel类型设置为channel,这样loop可以将错误的原因发送过来:
|
|
Loop的处理:取走error并退出
|
|
对于nil channel,无论发送与接收操作都会阻塞;
select永远不会选择一个阻塞的case.
nil channel与空channle的区别:?
|
|
获取goroutine的GID:
|
|
|
|
这里用到了C++11的新特性——可调用对象模板类,可调用对象理解为函数,该特性可以用于定义函数指针,相当于是把函数本身做进一步抽象。这里利用该特性定义了三个回调函数,以第一个typedef boost::function<bool (const TcpConnectionPtr&, StringPiece,Timestamp)> RawMessageCallback
为例:
function<bool (const TcpConnectionPtr&, StringPiece,Timestamp)>
意思是定义了一个可调用对象(函数指针),参数是三个const TcpConnectionPtr&, StringPiece,Timestamp
。RawMessageCallback
,或者说是一个可调用对象。该上下文的调用如下:
|
|
可以结合map
容器和该特性定义一张函数表,如下示例:
|
|
运行结果如下:
|
|
这里面有几个坑要注意:
auto mod = [ ](int i, int j)
定义了一个lamba表达式,即没有函数名的函数,这是什么鬼?注意函数实现最后有分号;
#include <stdexcept>
;#include <typeinfo>
,然后直接typeid(var)
即可。从运行结果可以看到迭代器本身的类型和迭代器成员的类型区别。->
操作符。TODO: 容器遍历与访问的两种方法:迭代器和容器内部方法之区别。
]]>线程安全是多线程程序中的编程理念,一段代码线程安全意味着在多个线程执行的时候能够运行正确。因此,它必须满足:
rel="nofollow"
属性以避免受到攻击;favicon.ico
文件, 比如. /favicon.ico
。浏览器会自动请求该文件,甚至在HTML中无需提及;如果没有这样的文件/favicon.ico
,会产生多个404错误,消耗网站带宽;example.com/pages/45-article-title
而不是example.com/index.php?page=45
; #
替换诶#!
; click here
的链接;/sitemap.xml
;<link rel="canonical" ... />
;线程安全是多线程程序中的编程理念,一段代码线程安全意味着在多个线程执行的时候能够运行正确。因此,它必须满足:
保证的手段如下:
一个任务执行该代码的一部分,此时另外一个任务进来,最后再恢复到原先任务。这就需要保存每一个任务的本地局部变量,一般保证在自己的栈当中,而不是static或者全局变量。
如果采用某种机制使得序列化访问共享数据,保证在任意时刻只有一个线程读写数据。由此引发条件竞争、死锁、活锁、饥饿等问题。
使用静态数据或任何其他共享资源的函数(比如文件或终端)必须通过锁使得对这些资源的访问串行化,以便函数变为线程安全。例如,以下函数是线程不安全的:
|
|
要使其变为线程安全的,那么必须通过静态锁将静态变量 counter 保护起来,如以下例子所示:
|
|
在使用线程库的多线程应用程序中,应使用 mutex 来对共享资源进行串行化。独立的库可能需要在线程的上下文以外工作,因此,请使用其他种类的锁。
其实就是变量本地化,即确保每个thread拥有自己的私有拷贝。
|
|
采用原子操作保证其他thread无法中断当前操作,通常通过特殊的机器指令,比如xchag和TSL。原子操作构成了多线程锁机制的基础。
线程安全函数
线程安全函数通过锁来保护并发访问中的共享资源。线程安全只与函数的实现有关,并不会影响它的外部接口。
在C语言中,局部变量是在堆栈中动态分配的。因此,任何不使用静态数据或其他共享资源的函数一般都是线程安全的。
全局数据的使用是线程不安全的。全局数据应针对每个线程保存或被封装起来,这样可以使它的访问串行化。线程可以读取对应于由另一个线程引起的错误的错误代码。
在多线程程序中,所有被多个线程调用的函数必须是线程安全的。但是,对于在多线程程序中使用线程不安全子例程有一个变通方法。虽然非重入函数通常都是线程不安全的,但是将它们变为重入常常也使它们变为线程安全。
使函数成为重入函数
在多数情况下,必须用带有已修改的将要重入的函数来替代非重入函数。非重入函数不能由多个线程使用。此外,可能也无法使非重入函数变为线程安全。
许多非重入函数会返回一个指向静态数据的指针。可以用以下方法来避免这种情况:
|
|
该函数不是重入函数(也不是线程安全的函数)。要通过返回动态分配的数据来使该函数重入,那么该函数应类似于以下代码段:
|
|
较好的解决方案是修改接口。调用程序必须为输入和输出字符串提供存储量,如以下代码段所示:
|
|
使用调用程序提供的存储量使非重入标准 C 库子例程重入。
在连续调用中保存数据
在连续调用中将不保存任何数据,因为不同的线程可能连续地调用该函数。如果函数必须在连续调用中保存某些数据,比如工作缓存或指针,那么调用程序应提供该数据。
如下示例,函数返回了字符串中连续的小写字符。该字符串只在第一次调用时提供,就像strtok子例程。函数在到达字符串的结尾处时返回 0。该函数可通过以下代码段来实现:
|
|
该函数不是重入函数。要使其变为重入函数,那么调用程序必须保存静态数据和变量 index。该函数的重入版本可通过以下代码段来实现:
|
|
该函数的接口和用法都发生了改变。调用程序必须向每次调用提供该字符串,且在首次调用前,必须将索引初始化为 0,如以下代码所示:
|
|
TLS库的线程安全
在TLS实现中,常见的需要保证线程安全的场景主要有:
目前的TLS线程安全由pthread_mutex互斥锁保证。
编写重入和线程安全代码
在单线程进程中,只存在一个控制流。因此,这些进程所执行的代码无需重入或是线程安全的。在多线程程序中,相同的功能和资源可以通过多个控制流并发访问。
要保护资源的完整性,编写的多线程程序代码必须能重入并是线程安全的。重入和线程安全都与函数处理资源的方式相关。重入和线程安全是不同的概念:函数可以重入和/或线程安全化,或者两者都不可行。
此部分提供了有关编写重入和线程安全程序的信息。其中不涉及有关编写高效线程程序的主题。高效线程程序是高效率的并行化程序。您必须在设计程序的时候考虑到线程的效率。现有的单线程程序可以成为高效线程程序,但是这需要将这些程序完全重新设计和重新编写。
除了上述方法,可以有变通方法编写:
该变通方法很有用,尤其是在使用多线程程序中的线程不安全库进行测试时,或者同时在等待线程安全的库变成可用时。该变通方法会导致一些开销,因为它是通过对整个函数,甚至一组函数进行串行化来实现的。以下是可能的变通方法:
|
|
|
|
重入函数不在连续的调用中保存静态数据,也不返回指向静态数据的指针。所有的数据都是由函数的调用程序提供的。重入函数不得调用非重入函数。
一般情况下,非重入函数是由其外部接口和用法标识的,但也并不总是这样。例如,strtok 子例程不是一个重入函数,因为它保存了将分割为多个标记的字符串。ctime 子例程同样不是重入函数;它返回了被每个调用所覆盖的静态数据的指针。
重入库和线程安全库并不仅仅在线程中有用,而且在大范围的并行(和异步)编程环境中也很有用。一直使用和编写重入函数和线程安全函数是很好的编程实践。
使用库
标准C库 (libc.a)和Berkeley兼容性库(libbsd.a)以下库是线程安全的,有些标准 C 子例程是非重入的,比如 ctime 和 strtok 子例程。
在编写多线程程序的时候,请使用子例程的重入版本来替代原来的版本。例如,以下代码段:
|
|
在多线程程序中应使用以下代码段来代替:
|
|
在一个程序中,线程不安全的库可能只由一个线程使用。请确保使用该库的线程的唯一性;否则,程序可出现意外的行为,甚至可能停止。
转换库
在将一个现有的库转换为重入库和线程安全库的时候,请考虑以下问题。此信息只适用于 C 语言库。
初步调试发现,此时client与server已经建立SSL连接,但是在读取APP数据时select超时,但为什么单单在连接这个APP的时候出现超时呢?
首先看SSL连接的建立过程,如图1所示:
注:
而SSL本身的数据处理如图2所示:
让我们回到数据读取,这里需要说明的是程序采用的事件处理框架。作为SSL客户端,为了能够处理多个SSL连接建立后对多个socket的并发事件处理,引入了select机制,事实上最多支持32个SSL连接。Select作为一种非阻塞IO接口,在这里用于轮询建立的TCP socket (SSL连接基于TCP socket,当然例外情况是DTLS基于UDP) 事件,结果发现没有数据可读。
数据读取在非阻塞模式下遵从能读多少就读多少的原则,同理数据写入在非阻塞模式下也遵从能输出多少就输出多少的原则,其中输出缓冲区如下图所示:
进一步查看已经建立的SSL连接表,结构如图2所示。SSL连接表本身由一个数组存储,为了加快SSL连接的查找又引入了一张哈希表,该哈希表采用数组作为散列的桶,每个桶是一个单向链表以解决哈希冲突。
当前的SSL连接表显示多个SSL连接都挂在了一个桶的链表上,看起来是发生了哈希冲突,但细想这概率似乎有点高,那就看看为啥这几个SSL连接都搞出来一个key, 注意到这里的hash key是TCP socket的抽象数据结构指针,再看代码直接把当前进程中的文件描述作为查找键,增加一行代码就好了,即利用getsock拿到真正的socket数据结构指针。
为了搞清楚错误是怎么发生的,有必要来个情景再现。让我们看看在哈希表插入的时候发生什么。哈希表的插入发生在新建一个SSL连接的时候(每一个SSL连接对应一个TCP socket),当TCP连接建立之后,SSL就以socket作为key插入哈希表当中。由于APP在SSL建立时候打开的一个文件描述符和隧道建立时候打开的文件描述符一些情况下是一样的,这样新旧两个SSL连接描述符产生相同的key,都插入同一个桶,对应依次插入链表尾部,当进行数据读写时根据文件描述符查找到的是旧的SSL连接描述符,此时该连接符已无数据可读写,导致超时。
]]>服务器端证书用于标识一个Server,典型地被签发给主机名,比如机器名或者host-header(www.baidu.com)。对Server证书而言,”Issued to”域意味着选择哪个签发的hostname。
服务器端证书用于标识一个Client或者user,意味着向Server验证Client,”Issued to”域采用用户的名字。
在HTTP下有虚拟主机这一概念,其目的是为了在服务端用单个IP支持多个主机名;对应到HTTPS,客户端验证服务器为不同主机配置多个证书,但是客户端需要检查服务器证书的common name,当客户端请求服务器证书的时候,服务器需要决定提供哪个证书,SNI应用而生,其原理如下图:
SSL中间人攻击的形式主要有三种:SSL downgrading,SSL stripping和fake of SSL certs。
在SSL协议中,证书是用来进行身份验证的,这里有两个关键的检查点:
Timer是底层库实现中重要的一部分,其作用主要是实现超时处理,应用的场景有:
为了支持实时处理,将Timer的内部实现由原来的单向链表变为红黑树。
红黑树是一种平衡树,但却不是一种基于高度的平衡树,在STL map和Linux中都作为平衡树来应用,当搜索的要求高或者修改频繁时,其表现效果更是好于AVL树,因为后者是基于高度的,所以在每次修改后都要进行rebalance,开销很大。而红黑树的插入只要两次旋转,删除最多三次旋转,虽然搜索稳定性不及AVL,但是仍不失为一种折中的好办法。
为保持红黑树的平衡,需要对树进行一些调整,好比为了保证一个黑客集团内部的黑帽子和红帽子的平衡,在成员之间的帽子问题上进行调整,假定只有两种帽子,黑帽子和红帽子,而且等级森严,按照辈分排:
当一个新的成员(帽子为暂定为红色)加入后,对家族成员帽子颜色的调整有下面几种情况:
上述规则可用下图表示:
依次插入6个节点123456,建立一颗红黑树如图所示:
OpenSSL,可以用来公私密钥对产生,证书查看(查看证书整体或者是单个信息),证书格式转化以及网络抓包,很多时候可以用于验证。
利用OpenSSL验证证书
由于OpenSSL在验证证书时基于PEM格式,因此首先将非PEM格式转化为PEM格式。root证书由对方发送过来,但是server没有给,到是可以从网络抓包中export出来如图所示:
openssl x509 -inform der -in FZROOTCA.cer -out FZROOTCA.pem
openssl x509 -in rsa_server.pem -noout –text
openssl x509 -inform der -in rsa_server.der -out rsa_server.pem
openssl verify -verbose -CAfile FZROOTCA.pem rsa_server.pem
rsa_server.pem: OK
,但貌似并不对签名进行验证,继续。利用OpenSSL验证签名
为了验证签名,需要将public key从证书中单独抓出来:
openssl x509 -pubkey -noout -in rsa_server.pem > server_pubkey.pem
同时也需要将Signature从证书中抓出来:
openssl asn1parse -in rsa_server.pem -out sig -noout -strparse 614
利用抓出来的signature和public key进行该证书的签名验证:
openssl rsautl -in sig -verify -asn1parse -inkey server_pubkey.pem –pubin
结果显示:
RSA operation error
3177:error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01:rsa_pk1.c:100:
3177:error:04067072:rsa routines:RSA_EAY_PUBLIC_DECRYPT:padding check failed:rsa_eay.c:699:
对应代码如下:
|
|
从上面的结果可以得知公钥未能正确对签名解密,严重怀疑server将私钥弄错了。实际上如果解密成功,则应该是下面这个样子:
其实就是遵循PKCS#1标准对数据的封装,整个明文长度和签名长度一致,都是RSA modulus个比特,上图中签名长度为2048 Bits,即256字节。第一个字节00
意思是确保该加密块在转化为一个整数的时候其长度小于RSA的modulus。第二个字节01
代表了这是一个私钥操作,即签名过程。然后是一大堆FF
字节,用来填充结果,最后以00
字节结尾。30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14
则指明了HASH算法ID是SHA-1。最后的20个字节才是真正对整个证书进行HASH的结果,以此作为验证的比对对象。
为了在原有安全库的基础上支持PSSRSA,需要理解此类算法的封装格式,利用OpenSSL直接可以查看此类证书的ASN1编码格式:
openssl asn1parse -in pss_rsa_server.pem
当然OpenSSL的功能远不止这些,后续会陆续添加。
APP内部实现架构选择,比如是采用多进程模型还是单进程模型;
目前整体流程基于过程式交互方式,可以改为基于事情驱动的状态机实现方式;
首先评估SSL的overhead到底有多大,以及主要瓶颈,针对性的进行优化;
SSL双方采用的版本也影响overhead,目前SSL的版本号有SSL2.0 SSL3.0,TLS1.0(SSL3.1),TLS1.0(SSL3.2)和TLS1.0(SSL3.3)。版本号的协商基于如下公式:
min(max. client supported version, max. server supported version)
关于序列号
TCP Segment Len
都为0。 Window
就是滑动窗口,用来解决流控问题,即如何提高网络吐吞率,主要涉及到拥塞控制的参数调整。 Sequence Number
用来解决乱序问题,即我发送了多少加多少(对于三次握手阶段,以报文个数为单位,即在对方Acknowledgment Number
基础上递增1;对于数据发送阶段,以字节数为递增单位,即在自己现有的Sequence Number
上递增TCP Segment Len
)。Acknowledgment Number
用于确认收包,解决丢包问题,即我收到了多少加多少(对于三次握手阶段,同样以报文个数为递增单位,即在对方Sequence Number
的基础上上递增1;对于数据发送阶段,也同样以字节数为递增单位,即在自己现有的Acknowledgment Number
上递增TCP Segment Len
)。如图所示:其中客户端192.168.69.2和服务器192.168.69.1的三次握手中,服务器的seq
是自己产生的,ack
则是在收到客户端的SYN报文后递加1(客户端的seq + 1
),表示收到了客户端的连接请求;客户端的seq
也是自个产生的,发送了一个SYN,就递加1,ack
则是在收到服务器的SYN/ACK报文后在服务端的seq + 1
。
在TCP连接建立后,客户端向服务端发送一个TCP Segment Len
为445字节的分片#4,当前seq
为1(相对序列号),不管是否送达下一个分片#6发送时,其seq
会忠实地递增445字节,变为446。服务端收到分片#4后,将ack
递增445字节。
总之,Sequence Number
和Acknowledgment Number
像极了人的奋斗过程,前者好比付出,后者好比收获,但是付出不一定有收获(如果得到ACK确认则成功,否则失败)。
网络层:
主要涉及到网络接口驱动的实现优化,比如中断轮询模式选择,是否启用DMA等。
LRU即Least Recently Used,是一种在cache中广泛应用的算法,保持经常访问的数据节点在cache中,如果某块节点很少被访问,在达到某种阈值之后就把它交换出去,这就要求查找和插入删除都要快。本系统采用双向链表和哈希表实现,主要利用了双向链表的插入和删除操作和哈希表的查找操作,数据结构定义如下:
|
|
对应的操作主要就是get和put。
|
|
|
|
基本思路:如果待插入数据在cache中没有(cache miss)且没有超出cache容量,就为其分配节点,插入至链表头部,表示最近访问,同时插入哈希表当中;如果待插入数据已经存在且cache已经满了,那么首先找到最后一个元素,意味着最近没有访问,从哈希表中删除,然后从双链表中删除。
|
|
如果cache miss也就罢了,如何找到了,在返回该节点之前因此需要将其移到链表的头部,因为查找某个节点意味着对该节点有了最新的访问。
|
|
实际上两个辅助函数LRU_moveToHead
和LRU_removeLast
的实现值得注意:
其中LRU_moveToHead
的基本实现思路就是先促成当前节点前后两个节点的牵手,然后再往前坐链表头,最后修改first
和last
节点。
|
|
TLS连接的建立和关闭过程如图:
在TLS中频繁分配释放的内存主要有以下几类:
Memory Pool的主要设计思路:
对SSL的握手过程进行对比测试,结果表明在使用mem pool的性能提升为8%左右,似乎并不理想,看来在SSL握手中此类内存的分配释放并不是影响性能的主要因素。
对于计算密集型的加解密运算,首先想到的就是利用硬件进行加速,这里有两个方面的考虑:
针对上述考虑,采取的措施如下:
整体而言,采用硬件加速就是在充分利用压榨硬件计算性能的基础上尽可能减少内存拷贝和IO消耗,最后性能平均提升大约十倍左右。
]]>在顺序程序设计中,由于代码指令都是顺序执行,重复执行会得到相同的结果,即程序与计算一一对应。
并发程序意味着一个程序未执行完,而另外一个程序已经开始执行,这就是所谓的并发性。并发性从宏观上反映了一个时间段内有几个进程都处于运行态但运行尚未结束的状态。从微观上看,任一时刻仅有一个进程的一个操作在处理器上运行。反过来看,并发其实就是处理器在几个进程之间的多路复用器,是对有限物理资源的强制进行多用户共享,消除计算机部件之间的互等,提供系统资源的利用率。
由于交互的并发进程共享某些资源,一个进程的执行可能会影响其他或者进程的执行结果,即交互的进程之间相互制约。因此,必须对进程的交互过程进行控制,引入了各种同步机制。
互斥又称为竞争,并发的引入使得原本没有竞争关系的进程在访问共享资源时发生了冲突,进程之间存在间接制约关系。在这种关系下,一个进程获得资源,另一个进程不得不阻塞等待,因此可能会导致两个严重问题:死锁与饥饿。防止不公平,或者饿死某些低优先级的进程,是调度系统必须考虑的问题。
进程同步是指为完成共同任务的并发进程基于某个条件来协调其活动,因为需要在某些位置上排定执行的先后次序而等待、传递信号或者消息所产生的协作制约关系。这种有先后次序的协作是进程之间的直接制约关系。
同步是为了几个具备一些依赖性的任务一起协同工作,通信则是为了同步的手段,可以基于共享内存,也可以基于消息传递。进程之间的协作可以是比知道对方名字的协作,比如通过共享内存进行松散式协作;或者进程双方知道对方名字,通过消息机制进行紧密协作。
竞争关系从某种意义上可以看成是同步,因为存在竞争关系的进程需要互斥的访问资源,也遵循互斥的访问次序。
实现同步的方式有多种,可以基于软件,也可以基于硬件。历史上,其演变史大致由自旋锁至信号量,再到互斥锁。
自旋锁可以通过CPU轮询机制实现同步。Spinlock作为一种临界区保护机制,在单处理器和多处理器下的实现不尽相同:
XCHGB的实现:
|
|
spinlock的实现:
|
|
自旋锁的适用场合与缺点:适用于临界区代码执行时间较短的场合;由于自旋锁采取忙式等待,白白浪费了CPU的时间,将能否进入临界区的责任推给了各个竞争的进程,而且只能解决竞争问题,而不能解决进程之间的协作问题。
信号量于1965年由Edsger Dijkstra提出,主要是为了解决并发编程中的竞争问题,其实质是二元信号量,后来Scholten在此基础上提出了通用信号量,也称为计数信号量。不管是哪一种信号量,都加入了进程调度,CPU不再大量的忙等待。
从信号量的等待队列中唤醒进程的算法有如下:
Linux的信号量semaphore实质上为计数信号量。
|
|
P操作也称为down操作,即请求一个资源。
|
|
|
|
信号量适用于等待时间不确定的场景,但也有其潜在的问题:
比如没有进程P操作就进行V操作,会导致资源访问错误。
所谓死就是进程都在等一个永远不会为真的条件,进程试图获取一个已经lock的信号量,比如如下锁实现会存在此问题。
|
|
如果一个进程已经获取该锁,当该进程尝试再次获取该锁的时候,会再次将自己置于等待状态而无法释放。
如果一个拥有信号量的task死亡了或者被终止了,会有什么后果?如果不能检测这种情况,所有正在等待的的task将永远都无法获得信号量从而进入死锁。
为了一定程度上解决这个问题,普遍的做法是在获得信号量的函数调用中指定一个可选的超时时间。比如前文提到的Linux实现就指定了超时机制。
大部分RTOS使用了优先级驱动的抢占调度算法。每个task拥有一个优先级,抢占调度中,低优先级的task释放CPU,使高优先级的task得以运行,这是构建一个实时操作系统的核心理念。优先级反转是指高优先级的task被低优先级的task挂起。
同步(Synchronization)这个词经常被错误地用于表示互斥(mutual exclusion)。根据定义,同步是:
To occur at the same time; be simultaneous
一般来说,task之间的同步是指一个task在继续执行前,等待另外一个task的通知。还有一种情况是每个task都可能进入等待状态。互斥是一种保护机制,与此有很大不同。但是,这种错误的使用导致计数信号量可以被用于单向同步:初始化一个信号量,计数值为0。
需要注意的是,P和V并不是在同一个task中成对出现的。在这个例子中,假设Task1调用了P(S)它将被挂起。当Task2之后调用V(S)时,单向同步发生了,两个task都进入就绪状态(高优先级的task先运行)。不过,这种对信号量的误用是存在问题的,会导致调试起来非常困难,并可能导致accidental release类型的问题,因为单独的V(S)调用(不与P(S)配对)现在被编译器认为是合法的。
为了解决信号量存在的问题,1980年提出了一种新的概念——互斥(Mutual Exclusion的缩写)。互斥与二元信号量在原理上是相似的,但有一个很大的不同:属主,这就意味着如果一个task获得了互斥体,只有这个task可以释放这个互斥体。如果某个task试图释放另一个task的互斥体,将会触发错误导致操作失败。一个没有属主的“互斥体”不能被称为互斥体。加锁与解锁只能在一个task中成对出现。
Mutex的定义和初始化
|
|
Mutex的定义和初始化
|
|
|
|
如果是属主进程递归加锁,只需递加计数,不会导致一直等待。
目前主流的互斥体接口主要有三类:
VxWorks主要采用VxWorks mutex,支持优先级继承。
缺省的POSIX mutex不支持递归、不支持优先级继承和消亡探测机制。Linux下大多采用POSIX threads编程,支持四种Mutex类型:
window下编程接口遵循win32 API,有如下几种:
Wireshark作为一个使用频率极高的工具,在日常工作中发挥着不可替代的作用,下面主要结合工作中涉及的无线和安全相关部分展开说明。
对于基于PSK验证方法的网络报文,可以直接利用抓包工具进行解密。
由于我们的目的在于分析加密报文,因此需要获取加密数据的密钥。简单的方法可以利用Wireshark的WPA Pre-shared Key Generator,原理如下:
原始数据:
在Wireshark中针对IEEE802.11选择解密数据,添加上步计算所得密钥保存即可:
这时候我们可以看到数据已经变为明文:
为了能够对SSL/TLS中的数据报文进行解密,要保证一下几个前提:
第一:Cipher suit只能选择RSA相关,即必须强制SSL客户端使用RSA Key。如果选择了其他Cipher,比如Ephemeral Diffie-Hellman(称之为DHE) ,Elliptic Curve Diffie-Hellman(称之为TLS_ECDH)。由于在DHE中在产生DH密钥的时候,每次都用新的DH参数,如果想解密报文,必须获取每次的pre-master key。事实上,为调试方便,直接强制使用RSA Cipher即可。对于自己的客户端,直接增加对应接口即可;而对于window客户端,可以通过修改注册表进行修改:
第二:能够拿到服务器证书对应的私钥,然后将其转化为PEM格式,因为只有私钥才能解密其对应公钥加密的报文。
第三:确保客户端和服务器之间有一系列完整的通信报文。
Wireshark的设置很简单,见下图所示:
配置好,之后就可以对所抓报文进行解密。
在实际工作中,往往遇到无法直接连接服务器的情形,只能通过抓包进行分析。在其中尤以证书验证问题居多,利用Wireshark报文,既可以将server证书串直接导出,也可以以二进制文件格式导出再辅以其他解析工具导出程序进行调试。
这其实涉及到了数据重放的概念,将实际环境当中的运行数据进行本地化调试,具体步骤如下:
第一步:获取PCAP,保存证书相关字节流。
第二步:编写脚本将hex stream转为数组,并存在一个头文件当中,该python脚本如下:
|
|
第三步:修改源代码,在文件中包含该头文件,使得证书验证的数据流指向该数据即可。
网络层的访问控制手段有多种,最简单的就是基于MAC地址的访问控制方法;另一个就是802.1X,全称为基于端口的链路层访问控制协议,其控制思路可以比喻为居民家中的防盗锁链,当外来人员试图进入家中时,可以先将防盗锁链展开以便观察验证对方,通过后再开锁。
同样道理,802.1X在允许用户访问网络之前,通过EAPoL或者EAPoW报文进行验证,验证成功方可允许网络层接入,即允许IP、ARP等通过。
这里引入了EAP(扩展验证协议),该协议提供一种端对端的验证框架:
802.1X是EAP的承载层,位于OSI中的第二层,即数据链路层。数据链路层分为LLC子层和MAC子层,而MAC子层分为802.11(无线)MAC子层和802.3(大名鼎鼎的以太网)MAC子层,因此EAP相应的有EAPoL(EAP over LAN)和EAPoW(EAP over wireless)。
为了遵循网络资源的安全特性,即CIA即confidential、integrity和avability,802.1X定义了各种访问控制手段,总的过程是首先对方进行身份请求,然后进行身份验证并获得授权,最终得以访问授权范围内的资源。
802.1X提供了身份验证机制,但并不是所有的EAP方法都支持双向验证,比如EAP-MD5就只支持客户端验证。
EAP验证的一个副产品是PMK,可以看做是AS对STA的授权码。
数据的机密性需要完善的密钥管理与分发机制,协议载体即EAPoL-key,由四步消息组成,称之为4-way handshake,实际上PTK产生和安装需要两步,GTK的产生的安装需要两步:第一对消息完成PTK产生和安装,第二对消息不仅完成对PTK的确认,而且产生和安装GTK。 如图所示:
为了保证密钥在发送过程中不被篡改(完整性),需要引入验证机制,通常的做法是在消息后面附加一个校验码,这里用一个密钥做验证密钥使用;为了保证机密性,必须得有加密密钥。这几种密钥的产生过程如下:
基于802.1X无线的安全模式一般称为WPA-Enterprise或者是WPA2-Enterprise(WPA和WPA2的差别在于前者的加密算法基于TKIP,后者则基于CCMP);基于PSK(预共享密钥)的无线安全模式称为WPA-PSK或者WPA2-PSK。两种区别如下:
WPA/WPA2-Enterprise需要RADIUS服务器(需要STA和RADIUS服务器协商出PMK,然后由服务器把PMK交给AP),可以覆盖更多的范围;而WPA/WPA2-PSK则不要用,直接由PSK在AP和STA两边各自计算出同样的PMK。
WPA/WPA2-Enterprise可以验证双方,取决于EAP方法的选择,而WPA/WPA2-PSK只能验证客户端,即STA。
WPA/WPA2-Enterprise支持机器验证,而WPA/WPA2-PSK只支持用户验证。
如果要禁止某个用户访问,对于WPA/WPA2-Enterprise只需吊销该用户的访问权限即可;而对于WPA/WPA2-PSK,一旦修改密码意味着所有用户都无法访问。因此,前者相比后者在访问控制的粒度上更为精细。
从安全角度来看,WPA/WPA2-PSK的主要缺陷如下:
大多数的应用处理数据,典型的操作就是通过更新用户的数据维护当前的状态。比如CRUD数据操作模式,即创建、读、更新和删除数据,一般的做法是从数据仓库读取数据,对数据进行修改,然后用最新的值更新数据状态,上述操作往往通过锁实现。
CURD方式的不足之处:
事件源模式(event souring pattern)定义了一种对序列化事件驱动数据的操作方法,每个事件都是以累加方式(append-only)进行存储。APP代码发送一系列事件至数据存储,以命令式方式描述每一个动作。同时,数据一致性在数据存储中完成。每个事件代表了对数据的一个属性的改变。
完成一致性处理的事件存储在event store,作为数据当前状态的信任源或系统记录(或者称之为已授权数据源中的给定元素或信息片段)。典型地,Event store发布这些事件,这样能够通知到消费者并做相应的处理。作为消费者也可以对其他系统应用执行事件群的动作,或者是完成该操作所需的关联动作。我们可以看到用于产生事件的APP代码与注册了事件的系统之前是解耦的。
通过Event store发布事件的一个典型应用就是当APP改变目录行为后对物化视图的维护,并且能够与外部系统集成。比如一个显示了所有客户订单的物化视图,用于产生UI的部分组件。当APP添加或者删除订单时,描述上述行为的事件能够被处理,以更新物化视图。
The Event Sourcing pattern provides many advantages, including the following:
TODO
]]>本文记录了NS-3模拟器在Ubuntu12.04桌面版上的完整安装记录。
NS-3的安装方法有两种,一种是利用bake工具自动化安装,另一种是手动安装,安装包的下载可以通过Mercurial或则是直接下载tar包。
由于Bake工具安装陷入超时,因此直接采用基于Mercurial的手动安装。
Bake本身提供依赖包检测,这里直接将所需包安装成功。
apt-get install mercurial
apt-get install qt4-dev-tools
apt-get install gdb valgrind
apt-get install gsl-bin libgsl0-dev libgsl0ldbl
apt-get install flex bison libfl-dev
apt-get install tcpdump
apt-get install sqlite sqlite3 libsqlite3-dev
apt-get install libxml2 libxml2-dev
apt-get install libgtk2.0-0 libgtk2.0-dev
apt-get install vtun lxc
apt-get install uncrustify
apt-get install doxygen graphviz imagemagick
apt-get install python-sphinx dia
apt-get install python-pygraphviz python-kiwi python-pygoocanvas libgoocanvas-dev
apt-get install libboost-signals-dev libboost-filesystem-dev
apt-get install openmpi-bin openmpi-common openmpi-doc libopenmpi-dev
mkdir workspace
cd workspace
mkdir repos
cd repos
hg clone http://code.nsnam.org/ns-3-allinone
过程:
requesting all changes
adding changesets
adding manifests
adding file changes
最终生成ns-3-allinone目录。
./download.py -n ns-3-dev
下载过程:
#
# Get NS-3
#
Cloning ns-3 branch
=> hg clone http://code.nsnam.org/ns-3-dev ns-3-dev
requesting all changes
adding changesets
adding manifests
adding file changes
...
added 11117 changesets with 52385 changes to 7390 files
updating to branch default
2998 files updated, 0 files merged, 0 files removed, 0 files unresolved
#
# Get PyBindGen
#
Required pybindgen version: 0.17.0.886
Trying to fetch pybindgen; this will fail if no network connection is available. Hit Ctrl-C to skip.
=> bzr checkout -rrevno:886 https://launchpad.net/pybindgen pybindgen
Fetch was successful.
#
# Get NetAnim
#
Required NetAnim version: netanim-3.105
Retrieving NetAnim from http://code.nsnam.org/netanim
=> hg clone http://code.nsnam.org/netanim netanim
requesting all changes
adding changesets
adding manifests
adding file changes
added 275 changesets with 1533 changes to 228 files
updating to branch default
196 files updated, 0 files merged, 0 files removed, 0 files unresolved
#
# Get bake
#
Retrieving bake from http://code.nsnam.org/bake
=> hg clone http://code.nsnam.org/bake
destination directory: bake
requesting all changes
adding changesets
adding manifests
adding file changes
added 333 changesets with 790 changes to 63 files
updating to branch default
45 files updated, 0 files merged, 0 files removed, 0 files unresol
NS-3的编译借助build.py脚本工具。
./build.py
但是会碰到如下错误:
[1459/1770] cxx: src/lte/model/lte-ffr-sap.cc -> build/src/lte/model/lte-ffr-sap.cc.1.o
[1460/1770] cxx: src/lte/model/lte-fr-no-op-algorithm.cc -> build/src/lte/model/lte-fr-no-op-algorithm.cc.1.o
[1461/1770] cxx: src/lte/model/lte-ffr-soft-algorithm.cc -> build/src/lte/model/lte-ffr-soft-algorithm.cc.1.o
[1462/1770] cxx: src/lte/model/lte-ue-power-control.cc -> build/src/lte/model/lte-ue-power-control.cc.1.o
[1463/1770] cxx: build/src/lte/bindings/ns3module.cc -> build/src/lte/bindings/ns3module.cc.7.o
g++: internal compiler error: Killed (program cc1plus)
Please submit a full bug report,
with preprocessed source if appropriate.
See for instructions.
Waf: Leaving directory `/home/fyang/project/workspace/repos/ns-3-allinone/ns-3-dev/build'
Build failed
-> task in 'ns3module_lte' failed (exit status 4):
{task 159048492: cxx ns3module.cc -> ns3module.cc.7.o}
['/usr/bin/g++', '-O0', '-ggdb', '-g3', '-Wall', '-Werror', '-Wno-error=deprecated-declarations', '-fstrict-alid', '-fno-strict-aliasing', '-fwrapv', '-fstack-protector', '-fno-strict-aliasing', '-fvisibility=hidden', '-Wn, '-I..', '-Isrc/lte/bindings', '-I../src/lte/bindings', '-I/usr/include/python2.7', '-I/usr/include/gtk-2.0', -I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/pango-1.0', 'glib-2.0', '-I/usr/lib/i386-linux-gnu/glib-2.0/include', '-I/usr/include/pixman-1', '-I/usr/include/freetype2',bxml2', '-DNS3_ASSERT_ENABLE', '-DNS3_LOG_ENABLE', '-DHAVE_SYS_IOCTL_H=1', '-DHAVE_IF_NETS_H=1', '-DHAVE_NET_ETTE3=1', '-DHAVE_IF_TUN_H=1', '-DHAVE_GSL=1', '-DNS_DEPRECATED=', '-DNS3_DEPRECATED_H', '-DNDEBUG', 'src/lte/binings/ns3module.cc.7.o']
Traceback (most recent call last):
File "./build.py", line 170, in
sys.exit(main(sys.argv))
File "./build.py", line 161, in main
build_ns3(config, build_examples, build_tests, args, build_options)
File "./build.py", line 81, in build_ns3
run_command([sys.executable, "waf", "build"] + build_options)
File "/home/fyang/project/workspace/repos/ns-3-allinone/util.py", line 24, in run_command
raise CommandError("Command %r exited with code %i" % (argv, retval))
util.CommandError: Command ['/usr/bin/python', 'waf', 'build'] exited with code 1
似乎是g++编译器自身的问题,解决办法是利用swap:
sudo dd if=/dev/zero of=/swapfile bs=64M count=16
sudo mkswap /swapfile
sudo swapon /swapfile
编译完成后删除:
sudo swapoff /swapfile
sudo rm /swapfile
如果成功,则显示:
Waf: Leaving directory `/home/fyang/project/workspace/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (7m31.696s)
Modules built:
antenna aodv applications
bridge buildings config-store
core csma csma-layout
dsdv dsr energy
fd-net-device flow-monitor internet
lr-wpan lte mesh
mobility mpi netanim (no Python)
network nix-vector-routing olsr
point-to-point point-to-point-layout propagation
sixlowpan spectrum stats
tap-bridge test (no Python) topology-read
uan virtual-net-device visualizer
wave wifi wimax
Modules not built (see ns-3 tutorial for explanation):
brite click openflow
Leaving directory `./ns-3-dev
Waf工具用于脚本运行,并保证共享库在运行时处在正确位置。 通过修改Waf程序配置,添加实例程序:
$ ./waf clean
$ ./waf configure --enable-examples --enable-tests --enable-modules=core
$ ./waf build
NS-3自带单元测试功能,有了上述实程序之后,进行验证:
fyang@fyang-virtual-machine:~/project/workspace/repos/ns-3-allinone/ns-3-dev$ ./test.py
Waf: Entering directory `/home/fyang/project/workspace/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/fyang/project/workspace/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.210s)
Modules built:
core
Modules not built (see ns-3 tutorial for explanation):
brite click openflow
PASS: TestSuite attributes
PASS: TestSuite callback
PASS: TestSuite command-line
PASS: TestSuite config
PASS: TestSuite global-value
PASS: TestSuite int64x64
PASS: TestSuite object-name-service
PASS: TestSuite object
PASS: TestSuite ptr
PASS: TestSuite event-garbage-collector
PASS: TestSuite sample
PASS: TestSuite simulator
PASS: TestSuite time
PASS: TestSuite timer
PASS: TestSuite traced-callback
PASS: TestSuite type-traits
PASS: TestSuite watchdog
PASS: TestSuite hash
PASS: TestSuite type-id
PASS: TestSuite threaded-simulator
PASS: TestSuite random-number-generators
PASS: TestSuite random-variable-stream-generators
PASS: Example examples/tutorial/hello-simulator
PASS: Example examples/tutorial/fourth
PASS: Example src/core/examples/main-callback
PASS: Example src/core/examples/sample-simulator
PASS: Example src/core/examples/main-ptr
PASS: Example src/core/examples/sample-random-variable
PASS: Example src/core/examples/sample-simulator.py
运行模拟器:
./waf --run hello-simulator
结果:
Waf: Entering directory `/home/fyang/project/workspace/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/fyang/project/workspace/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.202s)
Hello Simulator
Client请求server端应用时,server端的验证方法有两种:
Token可以存储在三个地方:session storage/local storage/client side cookie。 如果存储在cookie中,利用了cookie的存储机制,而不是验证机制。
Token和cookie都有生命周期。
某孕妇Mrs.Lily向她的私人医生Dr.Michael申请做胎心监护,Dr.Michael了解到Mrs.Lily在医院A.Hospital做过类似的检查,因此Dr.Michael希望访问A.Hospital的在线系统获取Mrs.Lily的相关数据,最直接的方法就是向Mrs.Lily索要其在A.Hospital在线系统用户名和密码。
那么问题来了,该方法会造成哪些潜在安全威胁?
OAuth解决了上述问题,即定义了一种允许一个APP访问用户相关的其他APP的验证机制,比如用户可以利用QQ账号登陆其他APP。基本思路如下:
待续。
TCP/IP总的设计目的就是保证数据的快速有序的传输,且无丢失,基本特点如下:
TCP/IP网络协议栈包含多个层次,下图以一个TCP连接中的报文发送过程简述其基本流程:
对于报文发送过程而言,上层协议栈是生产者,驱动层为消费者。
下面以无线网卡驱动为例详述整个报文发送流程的实现细节:
理解问题需从根上抓起,TCP/IP也一样,仍有许多细节需要在工作实践中反复体会。
]]>
多态用到的技术即后期绑定,由JAVA内部一段程序实现;相反,前期绑定则通过绝对地址调用实现。C++需要用virtual显示声明,JAVA默认支持。
容器本质提供了各种数据类型的抽象,JAVA中包括List类,MAP类,以及队列、树堆栈等。迭代则提供了对容器成员的抽象化访问,比如设值、取值和遍历等。在C语言中可以利用函数指针进行迭代器的设计。
所有对象都具备相同的接口,好处是垃圾回收器可以方便的发送适当的消息给系统中的每一个对象。
有待理解。
前者即多个线程运行在多个处理器上,后者是多个线程分时复用一个CPU。程序写得不好,会造成在多处理器中出现线程总运行与某个处理器之上。
WEB客户端编程设计初衷就是期望减轻服务器端压力,将计算和逻辑控制过程置于客户端运行。具体方法包括插件、脚本语言比如JAVAScript、VBScript等、JAVA(利用applet)。相比脚本语言。JAVA applet和前者的本质区别在于其本身是以被编译过的形式存在的,并且利用数字签名保障安全性。
]]>HTTP用于获取服务器的资源,资源的种类称为MIME,资源的表达方式URI(统一资源标识符)。URI有两种形式,URL和URN:
一个事务包含一条请求加响应的响应。HTTP方法:
HTTP客户端的应用程序为了完成某项任务可以发布多个HTTP事务,而请求的资源可以位于不同的HTTP服务器端。比如JAVA小程序、图像等。
HTTP报文都是纯文本,不是二进制,包含三部分:
基于TCP/IP传输,因此需要目的IP地址和端口号。HTTP通过URL告诉TCP层IP地址和端口号。其中URL可以直接包含IP地址或者通过DNS查询得到,端口号默认为80。
TCP为HTTP提供了一条可靠的比特传输通道,从TCP一端填入的字节会从另外一端以原有顺序、正确的传送出来。
HTTP事务的性能在很大程度上决定上取决于底层TCP通道的性能。HTTP时延主要由TCP时延引起。需要考虑如下因素:
在HTTP层的优化方法:
Secruity Realm
server通过安全域告诉client授权范围限定在该域内。
HTTP Basic Authentication
HTTP的验证框架基于Challenge/Response验证,一般仅用于测试可用性。具体而言,基本的HTTP验证通过GET方法,在首部利用WWW-Authentication字段(GET Response)和Authorization字段(GET Request)完成交互。
根据client在Authorization字段的对身份凭证(比如用户名密码)的处理方式不同,可以引申出诸多验证方式。
但是其其缺点也很明显:
HTTPS应运而生。
Token-based HTTP Authentication
该种验证方式适合于桌面系统和移动设备客户端。
Session backend for authentication
OAuth-based Authentication
Web的基本原理即client向server发起HTTP Requst, server向client回应HTTP Response。client不仅仅局限于浏览器,也不限于人,更一般的情况下client向第三方发起HTTP Requst,得到第三方的HTTP Response。
nginx 1.5 + modsecutity + openssl + bootstrap 2
Phython 3.0 + celery + pyedis + libcurl + ffmpeg + libopencv + nodejs + phantomjs
postgresql + pgv8 + v8
hadoop + hive + thrift + OpenJDK
随着HTTP交互的数据量增长,不可避免会出现请求无法响应的情况,例如Twitter的在宕机时会出现fail whale图片。为了解决这个问题,引入消息队列。
目前的消息队列库的实现可以基于多种语言,包括Ruby、Python、Java、Erlang、Scala、C#、Lua、 PHP 等等。
消息队列遵循的协议有STOMP、AMQP和Memcache。
消息队列相关的软件包括RabbitMQ、Stompserver、ZeroMQ、Morbid、ApacheMQ、Starling、Beanstalk、Kestrel以及MemcacheQ。
Redis + redis-sentinel
Python 2.7 + Flask + pyredis + celery + psycop + postgresql-client
]]>本文研究了Linux下802.11n TX aggregation过程。
Linux协议栈中mac80211的整体架构如下图所示:
kernel->mac80211->iwlwifi的具体层次如下图所示:
iwlwifi->hardware的具体层次如下图所示:
LCRS, 即left-child, right-sibling representation is a way of encoding a multi-way tree using a binary tree
传统的Mutil-way Tree
A
//|\ \
/ / | \ \
B C D E F
| /|\ / \
G H I J K L
数据结构表达
struct Node {
DataType value;
std::vector children; /*用到多个孩子指针*/
};
每个节点存储一个是leftmost child,另外一个指针指向其右兄弟节点,形式变化如下:
A
/
/
/
B -> C -> D -> E -> F
/ / /
G H->I->J K->L
层次和结构不变,变的是存储方式。可以理解为double chained tree:
遍历
From a parent node to its kth child (zero-indexed):
Descend至当前节点的leftmost child,即child向右遍历该节点的兄弟节点k次返回节点,
数据结构描述相应变为:
struct Node {
DataType data;
Node* leftChild;/**/
Node* rightSibling; /*存储着*/
};
Multi-way tree VS LCSR tree
在构建某些特定的数据结构是,比如Heap data structures ,采样LCRS可以优化空间,而最常见操作集中如下:删除tree的根节点和处理一个孩子;合并两颗树,而这二者对于LCRS易于完成,对于certificate存储系统,该操作常见。
#define CTREE_CLASS_CERT. 0
#define CTREE_C_SCLASS_PUBLIC. 0x0001.
#define CTREE_C_SCLASS_PRIVATE. 0x0002.
#define CTREE_C_SCLASS_BOTH. 0x0003.
#define CTREE_CLASS_ROOT. 1
#define CTREE_CLASS_FOLDER. 2.
#define CTREE_F_SCLASS_COMMON. 0x0020.
#define CTREE_F_SCLASS_UNKNOWN. 0x0021
struct tree {
struct tree *parent;
struct tree *child; /*leftmost child, as the sibling’s list head*/
struct tree *sibling; /*link to child in single list structure*/
uchar. name[64]; /*file name, eg certificate name*/
uint . id; /* Must be > 0 */
ushort. type, subtype; /* both for reserved */
ushort .iclass, subclass; /*cert/root/folder; private key/public key*/
uchar. num_child, level, unused[2]; /*tree level*/
uint . capability; /*reserved*/
pBmp. EBmp, CBmp; /*Icon*/
void. *iprivate; /*for unkown CA folder and private info*/
};
Insert node
remove node
STATIC void
ctree_remove_node(struct tree *troot, uchar *filename)
{
struct tree *delte, *parent, *child, *lost, *lastchild;
delte = tree_find_node_by_name(troot, filename, 1);/*获取要删除的节点*/
if (delte == 0)
return;
parent = delte->parent; /*取得被删节点的父指针*/
child = parent->child; /*取得leftmost child指针*/
if (parent->child == delte) /*Case1: 被删节点就是leftmost child*/
parent->child = delte->sibling; /* 修改leftmost child指向被删节点的兄弟*/
else { /*Case1: 被删节点是sibling节点*/
child = parent->child; /*以leftmost child为单链表头遍历,获取要删除的sibling节点 */
while (child->sibling != delte)
child = child->sibling;
ASSERT(child->sibling == delte);
if (child->sibling != delte)
return;
child->sibling = delte->sibling;
}
delte->parent = 0;
delte->sibling = 0;
if (delte->child == 0) /*如果被删节点没有孩子,则直接返回*/
return;
lost = (struct tree *) troot->iprivate; /*重新连接整个子树至unkown CA*/
//re-link child
child = delte->child;
do { /*将该层所有的孩子节点的父指针修改为lost,即unkown CA*/
child->parent = lost;
lastchild = child; /*记录该层最后一个孩子节点*/
child = child->sibling;/*单链表遍历*/
} while (child);
lastchild->sibling = lost->child; /*最后一个孩子节点的next指针(sibling)指向lost的leftmost child*/
lost->child = delte->child; /*修改lost的leftmost child为被删节点的leftmost child*/
delte->child = 0;
ctree_free_node(delte);
tree_update_order(lost); /*更新以lost为root的子树*/
}
update subtree
void tree_update_order(struct tree *parent)
{
struct tree *child;
uint. previd = 0;.
PRINTF("TW: parent update [%s] (lv %d, id %x)\n", parent->name, parent->level, parent->id);
parent->num_child = 0;
while (child = tree_next_child(parent, previd)) { /*获取该层的所有孩子*/
child->level = parent->level + 1; /*孩子的层是父亲层加1*/
child->id = tree_calc_orderid(parent);
PRINTF("TW: child [%s] lv %d id %x\n", child->name, child->level, child->id);
tree_update_order(child); /*child作为子树root递归调用*/
previd = child->id; /*更新前一个id*/
child = child->sibling; /*单链表遍历*/
}
PRINTF("TW: [%s] return \n", parent->name);
}
由DAP发展而来,相比DAP,其基于TCP/IP,在功能上有所减少。LDAP本质上就是一种定义了如何访问目录数据的方法的协议,同时定义了在目录服务中数据是如何表达的。
对于数据操作,LDAP只定义了数据如何加载至目录服务和如何从目录服务导出数据,并未定义如何存储数据和操作,厂家可以有自己的back-end的具体实现,比如OpenLDAP提供了选择back-end数据库的支持。事实上很多商业DB都提供了LDAP视图。
LDAP可以看做是一种读优化的数据库。LDAP Server可以基于multi-master架构,也可以基于Multi-master架构。
LDAP是面向对象的数据库,定义了基础原语(read/delete/modify),以继承性对象的方式表示数据。
LDAP提供了一种可以查询和修改目录服务的应用协议。AD本质就是一种数据库的系统,在window环境中提供验证、目录、策略和其他服务。
LDAP是一个标准,AD则是微软的基于目录服务器的一中LDAP实现。
原文链接explain-like-im-5-kerberos
Kerberos源自古希腊神话中的三头狗,对应引入了第三方验证系统。Kerberos有如下特点:
一个ticket意味着用特定服务请求所对应的key加密后的身份证明,如果其有效,你可以在一个kerberos域内访问该服务。
Ticket由很多信息组成,以保证特定服务授权的针对性和实效性。
Keberos其目的仍是保证client和client所请求的服务或者主机之间的session key。
典型实例:
访问内网的工资系统,每次重新输入user/password的时候或者在终端运行kinit USER的时候,用户的ticket会更新。
管理员创建realm,囊括了所有可能的访问,即定义了不同服务和主机的访问策略和授权,其本质为依据谁能够访问什么的原则定义了Kerberos的管理内容。
用户主机位于该realm,同时也包括了所期望访问的服务、KDC(Key Distribution Center)。
KDC可以分为Authentication Server 和Ticket Granting Server。
当访问一个服务或者主机时,存在着三种交互:
其他要点如下:
上述定义的消息和内容并未反映他们在TCP、UDP发送中的实际顺序, 下面描述如果访问一个内部的HTTP服务,将会发生什么。
在访问HTTP服务之前,client必须向authentication server介绍自己。当登陆系统或者运行kinit USERNAME的时候,通过一个明文请求TGT(Ticket Granting Ticket)。其具体内容包括:
Authentication server仅仅检查是否存在于KDC数据库,无需密码。
Authentication server回复两条消息:
消息一,即用TGS的密钥(TGS和Authenticator都知道各自的密钥?)加密的ticket, 即TGT,这个TGT包括如下:
消息二,即client密钥加密的TGS会话密钥等:
TGS session key就是client和TGS之间的共享密钥。
Client 密钥通过提示client输入密码,再加上一个随机串,最后对整个进行哈希得到。可以用该密钥解密TGS发送过来的第二条消息,以获取TGS 会话密钥。显然,如果密码错误,client 将无法解密该条消息。实际上是完成了对client password的隐性验证。
由于不知道TGS 密钥,无法解密TGT,将此加密的TGT存储于credential cache当中。
此时,client拥有TGS session key,但是没有TGS密钥。
现在轮到client发送两条消息了:
第一条消息为Authenticator 准备,用TGS会话密钥加密如下信息:
Client name/ID + timestamp
第二条消息是明文,包含请求的HTTP服务名称和HTTP服务的ticket生命周期同时还有加密的Authenticator和TGT。
TGS收到这两个消息后,首先检查KDC数据库中是否存在所请求的HTTP服务。
如果存在,TGS用TGS密钥解密TGT。因为解密后的TGT包含TGS会话密钥,因此TGS可以解密client所发送的Authenticator。
TGS然后将作如下操作:
此时,TGS开始随机产生client所请求的HTTP服务的会话密钥,准备HTTP服务ticket,包含如下信息:
上述信息由HTTP服务密钥加密。
然后TGS向client发送两条信息:
第一条是由HTTP服务密钥加密的HTTP服务ticket;
第二条是由TGS session key加密的信息:
Client利用cached的TGS session key解密第二条消息,以获取HTTP服务session key。而无法解密第一条消息,因为其没有HTTP服务密钥。
为了访问HTTP服务,client准备了另外的Authenticator消息,包括:
用HTTP服务session key加密后发送给HTTP服务,同时也发送未解密的HTTP服务ticket。
HTTP服务在收到消息后用HTTP服务密钥解密消息二,获取HTTP服务session key。然后用该Key解密client发送的第一条authenticator消息。
类似于TGS,HTTP服务器也要做如下校验:
如果通过校验,HTTP服务然后发送一条Authenticator消息,该消息包含其服务ID及时间戳,由HTTP服务session key加密。
Client收到HTTP服务发送的加密信息,用cache的HTTP 服务session key解密,由此接收到一条HTTP服务ID和时间戳的明文信息。
此时client已经验证通过使用HTTP服务,将来的请求利用cached的HTTP服务ticket,前提是在定义的生命周期内。
这里有个前提,HTTP服务本身必须支持Kerberos。同时,client必须要有一个支持SPNEGO/Negotiate的浏览器。
token VS ticket
]]>设计一个数据库的API,可供不同的WEB前端服务存储和获取数据。一些存储图像,一些存储文本、声音、图像等,如何控制API对数据的上述访问方法?
void do_as_i_say(…) ,其参数是void
函数的目的在于提高访问复杂软件的接口,通过隐藏复杂性,提高软件的易用性, 但是该API并不能直观的告诉调用者如何使用。
int store_jpeg_image(string name, image data);
image get_jpeg_image(string name);
int delete_jpeg_image(string name);
int store_gif_image(string name, image data);
image get_gif_image(string name);
int delete_gif_ image(string name);
int store_tiff_image(string name, image data);
image get_tiff_image(string name);
int delete_tiff_image(string name);
int store_text(string name, string data);
string get_text(string name);
int delete_text(string name);
这里走向了另外一个极端,这里有几个问题:
Unix的文件IO操作为我们提供了一个良好的示例,该接口只有五个API:open、close、read、write、ioctl,其中最重要的API是ioctl,它为程序提供了访问系统底层的能力,以备为设计者预先没有想到的需要,相当于提供了一个后门。
该概念类似面向对象设计中的对象,用于维护一个实体,即可以创建、查找、销毁一个Instance。
cookie的含义很广,在分层设计中,cookie可以用于保存上层的数据,包括配置数据和配置行为(往往通过回调函数定义),这样底层可以无需知道上层的配置细节,直接调用其配置方法即可。
Callback可以实现底层调用上层,这里无需赘述
Callback可以用于实现异步通知机制,比如在一个异步事件通知系统中,可以将事件处理方法和ID组成事件对象加入事件队列,其中事件处理方法采用callback函数,在事件注册时定义,实现处理系统轮询该事件队列,逐个调用,当然也可以加入优先级以改变响应次序。
API设计应该是一个迭代的过程,首先你提供你认为用户需要的,同时流出后门。然后观察用户在使用过程中利用后门所做的操作,再在API全集中添加给API,依次类推。
]]>两大基本需求:
其中包括对XML的合法性检查,作者利用了JAVA中的JDOM类。
最近在工作中又碰到了DH(Diffie-Hellman)算法,鉴于之前对该算法理解地支离破碎,因此在博文中总结归纳下。
DH算法用于在不安全的公共通道中协商密钥,安全性体现在:在有限域上计算离散代数非常困难。上两位大牛Whitfield Diffie 和 Martin Hellman的照片:
算法描述:
假定Alice和Bob期望在一个不安全的网络中协商一个共同的密钥,那么进行如下步骤:
A = p^a mod g
, 发送给Bob。此时, Alice手握Bob发过来的B,结合自己产生的a开始这样计算:
B^a mod p = (p^b mod g)^a mod p = p^ab mod g
。
Bob也拿到了Alice发来的A,同时结合自己的b,也开始计算:
B^a mod p = (p^b mod g)^a mod p = p^ab mod g
。
这样Alice和Bob都得到了相同的密钥。
DH不仅支持两点密钥交互,也可以支持多点密钥交互。但是DH算法本身存在安全性缺陷,即没有对交互双方进行身份验证,容易遭受中间人攻击。攻击思路具体如下:
C = p^c mod g
,发送给Bob。C = p^c mod g
发送给Alice。K1 = p^ac mod g
,Bob和EVE计算出共同的密钥K2 = p^bc mod g
。因此必须引入身份验证以防范此类攻击,利用RSA数字签名即可达到此目的。
DH算法作为一种密钥协商机制,可以用于TLS协议当中。
如果在DH交互过程中Alice和Bob始终使用相同的私钥,就会导致后续产生的共享密钥是一样的,如果有嗅探者截获通信双方的所有数据,由于都是同一个密钥加密所得,一旦被破解,后续的通信将全部暴露。
为了保证安全性,必须引入前向保密,即Forward Secrecy。其基本实现思路就是在Alice和Bob在选择各自的私钥是引入随机性,也印证了那句话:要用发展的眼光看问题,不能一成不变。
事实上FS在诸多加密协议中应用广泛,比如IKEv2和802.11i密钥分发中的4-way握手,无一不引入此方法。
那么问题来了,TLS中哪一个才是最安全的cipher呢?就目前而言,最安全的三个候选者如下:
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P521
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256
注:任何一次技术变革不仅仅是技术方案的较量,更多的是市场的博弈。原文链接history of wireless security
802.11在发展早期主要采用两类验证方法— Open System Association (OSA) 和Shared Key Authentication (SKA).
Open systems 相当于没有任何验证手段,任何人可以连接至无线网络。
SKA基于WEP加密,我们会在4个预共享密钥中选择一个作为所有无线站点连接AP的密钥,虽然后来WEP被发现存在安全漏洞。让我们来看看WEP何罪之有?
WEP本身有诸多问题,在wired介质当中是一个足够强健的算法,但是并不适合无线介质,下面是WEP的若干罪状:
随着WEP越来越多的漏洞被发现,IEEE决定成立802.11i工作组以改进无线安全。作为一个安全网络的过渡期,WPA应运而生,其目的在于开发一个强健的安全网络。
802.11i工作组面临诸多困难,其中之一便是如何在仍旧使用现有硬件的基础上对WEP进行安全加密流程进行改造。作为一个过渡性解决方案,WPA在解决WEP现有不足的基础上同时考虑长期的无线安全。
Wi-Fi联盟(Wi-Fi Alliance)的目的在于创建一个在主流厂家之间互通的安全平台;而WPA源于‘Wi-Fi Protected Access’其本身不是一个标准甚至不能称之为框架,仅仅是一个由无线厂商为达到相互之间的互通而形成的ad-hoc联合。
WPA作为802.11i的一个准标准,既采用了特定的密钥管理,也遵循了802.11i中的验证架构-802.1X。由于当时的硬件不支持AES算法,WPA采用TKIP((Temporal Key Integrity Protocol))作为替代算法。
WPA采用PMK建立PTKs,即会话密钥,其目的在于保护每个会话。伪随机数函数PSF (pseudo-random function)确保PTKs包含nonces、MAC地址计数、时间以及随机数。密钥建立机制的重新设计目的在于最大程度地降低主密钥被窃取的可能性。见图1。
WPA在安全性方面的改进之处主要有以下几点:
WPA作为一种安全架构主要包含两类组件:第一类是加密组件TKIP;第二类是验证组件,可以是PSK也可以是RADIUS。除了安全性相比WEP有提升之外,更重要的是保证了向前兼容。
2008年WPA的漏洞被发现,即TKIP exploit,该漏洞通过改变验证组件无法修正,唯一的解决方法是用AES替代TKIP。WPA2应运而生,这就要求硬件进行改变。TKIP可以作为WPA2的可选项。
802.11i和WPA2指的是同一件事情,这样一来,在现有的硬件上可以用统一的的加密方式-AES,这正是IEEE标准下所谓的强健网络。
作为当前无线安全的标准,802.11i/WPA2又像极了当年的WPA准标准,仅在一些细微处有所不同。随着802.11i完成最终的业界应用,现有的硬件将AES加密算法作为内置算法。
WPA2相比WPA有如下两大改变:
802.11i采用802.1x保证密钥的建立与验证。EAPoL虽然本身是基于有线设计的,但是很容易适用于无线。802.1X相比之前的验证方法的最大不同之处在于验证过程发生在station和RADIUS server之间,而不是station和AP之间。由于AP不参与密钥产生,所以session key(由PTK导出)和每包密钥需要和AP共享,这些密钥由一个安全通道TLS传输。
图2描述了多种通信流,用不同的颜色予以标记。EAPoL通信由黑色标记,而EAP握手由蓝色区分。RADIUS采用绿色,TLS则使用了橙色。从该图可以清晰地看到station、AP以及RADIUS server之间的交互流程。
802.11i利用802.1X不仅建立密钥同时也完成了验证, 相应地,RADIUS服务器也通过该途径向client验证其自身。这条从RADIUS服务器端到客户端的隧道提供了安全通信的机制,从而保证了验证过程中的密钥交互。
WPA2也同样包含两类组件,一是基于AES的加密组件,二是基于PSK或者是RADIUS的验证组件。但并不意味着WPA2就没有漏洞,比如可以通过错误配置PEAP验证组件进行攻击。
尽管业界对于802.1X仍然有一些争议,但其已经成为主流的安全无线验证机制并被广泛接受。总之,802.1X囊括了安全密钥交换、密钥轮转、主密钥保护以及AES加密等,成为目前一种具有单项优势的技术。
auto lo
iface lo inet loopback
# The primary network interface
# Internal network
auto eth0
#iface eth0 inet dhcp
iface eth0 inet static
address 10.151.122.138
netmask 255.255.255.0
gateway 10.151.122.1
dns-nameservers 10.151.122.5 10.151.120.21 10.140.2.48
dns-search wyse.com
# This is an autoconfigured IPv6 interface
iface eth0 inet6 auto
# External network
auto eth1
iface eth1 inet static
address 10.151.120.190
netmask 255.255.255.0
gateway 10.151.120.1
dns-nameservers 10.151.122.5 10.151.120.21 10.140.2.48
dns-search wyse.com
controller
修改/etc/hosts文件如下:
127.0.0.1 localhost
10.151.122.138 controller
10.151.122.139 compute1
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
mysql_install_db
mysql_secure_installation
</code></pre> }
单一责任原则,即一个类只能有一种原因去驱动改变,该原则的是同open-closed原则有异曲同工之妙:尽量避免现有代码的修改。当违反了单一原则的时候,一个模块有多种原因需要去修改。更糟的是,多个不同的责任集中于一个模块当中会耦合在一起,使得测试与修改变得非常复杂。
单一责任原则本质是集中性,便于进行多层抽象,而不仅仅是一个过程上下文,简单地讲类替换为函数将有利于我们在此原则基础上分析算法。
由于在/etc/keystone/keystone.conf中定义了admin_token,这是一个keystone和other openstack services的预共享密钥,在创建租户时会用到。
keystone tenant-create —name=admin —description=”Admin Tenant”
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| description | Admin Tenant |
| enabled | True |
| id | 5e7178983104482a92433f55b09c6dcd |
| name | admin |
+-------------+----------------------------------+
}
keystone tenant-create --name=service --description="Service Tenant"
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| description | Service Tenant |
| enabled | True |
| id | 2b1ee1230ed34dcf9b73941f9e6b89c1 |
| name | service |
+-------------+----------------------------------+
}
- 创建管理员用户
keystone user-create --name=admin --pass=ADMIN_PASS --email=yangyongbupt168@gmail.com
+----------+----------------------------------+
| Property | Value |
+----------+----------------------------------+
| email | yangyongbupt168@gmail.com |
| enabled | True |
| id | d9308be3ba924b94ac051eb8d0750cd6 |
| name | admin |
+----------+----------------------------------+
}
- 创建角色:角色由各种OpenStack服务对应的policy.json文件指定,缺省的策略文件赋予admin角色访问大多数服务的权利。
+----------+----------------------------------+
| Property | Value |
+----------+----------------------------------+
| id | 372478ee2d2647858d864ce9066acccd |
| name | admin |
+----------+----------------------------------+
}
- 为用户添加角色:用户总是以租户身份登录,角色通过租户被赋予给用户。例如,当用户admin以admin租户的身份登录时,赋予其admin角色。
keystone user-role-add --user=admin --tenant=admin --role=admin
####2.3、定义services和API endpoints
- 为Identity services创建一个服务实体
keystone service-create --name=keystone --type=identity --description="Keystone Identity Service"
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| description | Keystone Identity Service |
| id | 7d36ad0a2c0f4ec893b1fedc95d5e9de |
| name | keystone |
| type | identity |
+-------------+----------------------------------+
}
- 利用返回的service ID(随机产生)创建API endpoint,其实就是创建内外网API等URL。
keystone endpoint-create \
--service-id=7d36ad0a2c0f4ec893b1fedc95d5e9de \
--publicurl=http://controller:5000/v2.0 \
--internalurl=http://controller:5000/v2.0 \
--adminurl=http://controller:35357/v2.0
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| adminurl | http://controller:35357/v2.0 |
| id | 42eb21997c244fa480320fa8bd4102dc |
| internalurl | http://controller:5000/v2.0 |
| publicurl | http://controller:5000/v2.0 |
| region | regionOne |
| service_id | 7d36ad0a2c0f4ec893b1fedc95d5e9de |
+-------------+----------------------------------+
}
####2.4、验证Identity Services安装
本质上是利用user/token验证绑定user/password的过程,期望通过user/password 查找token。
- 删除临时环境变量
unset OS_SERVICE_TOKEN OS_SERVICE_ENDPOINT
- 基于用户的验证:用admin的user/passord请求验证token
keystone --os-username=admin --os-password=ADMIN_PASS \
--os-auth-url=http://controller:35357/v2.0 token-get
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Property | Value |
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| expires | 2014-01-10T03:29:38Z |
| id | MIIC8QYJKoZIhvcNAQcCoIIC4jCCAt4CAQExCTAHBgUrDgMCGjCCAUcGCSqGSIb3DQEHAaCCATgEggE0eyJhY2Nlc3MiOiB7InRva2VuIjogeyJpc3N1ZWRfYXQiOiAiMjAxNC0wMS0wOVQwMzoyOTozOC4xNTMwNDAiLCAiZXhwaXJlcyI6ICIyMDE0LTAxLTEwVDAzOjI5OjM4WiIsICJpZCI6ICJwbGFjZWhvbGRlciJ9LCAic2VydmljZUNhdGFsb2ciOiBbXSwgInVzZXIiOiB7InVzZXJuYW1lIjogImFkbWluIiwgInJvbGVzX2xpbmtzIjogW10sICJpZCI6ICJkOTMwOGJlM2JhOTI0Yjk0YWMwNTFlYjhkMDc1MGNkNiIsICJyb2xlcyI6IFtdLCAibmFtZSI6ICJhZG1pbiJ9LCAibWV0YWRhdGEiOiB7ImlzX2FkbWluIjogMCwgInJvbGVzIjogW119fX0xggGBMIIBfQIBATBcMFcxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVVbnNldDEOMAwGA1UEBwwFVW5zZXQxDjAMBgNVBAoMBVVuc2V0MRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20CAQEwBwYFKw4DAhowDQYJKoZIhvcNAQEBBQAEggEAAS-Fdhv74-kaGYHDuVnJo3ZHhfQ0sdXtdJE68V86gB3TnRMBvDizYaaBlUEDkVLEsOTf-SwO+2fVDnEQ82eARFwXi5RgGUW-rSPX-DU9iJob0ajc4Pn7tRioEzjUByON+8kYge7V5hmowc4e+qM7vvbUcLkTC3eKkF4b4xNg7JSCEMeRCIPSDlqOR3a-S5jODUSdQlfkcZyjocQjcQdaEBW8Rm+bv4TTVYi2PotbqoZQHGvVPg45cjeFnLFSs9XVCkFMx9rUgXmj4+FdV61t6wH0goJ5LriIHKOlKD0dp6V8RX0p1hpmFoD1dI+yDFD68dmmUV5t6gtluyZA5dJNBw== |
| user_id | d9308be3ba924b94ac051eb8d0750cd6 |
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
}
这是一个包含用户ID的token paired。
keystone --os-username=admin --os-password=ADMIN_PASS \
--os-tenant-name=admin --os-auth-url=http://controller:35357/v2.0 token-get
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Property | Value |
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| expires | 2014-01-10T03:41:25Z |
| id | MIIEsgYJKoZIhvcNAQcCoIIEozCCBJ8CAQExCTAHBgUrDgMCGjCCAwgGCSqGSIb3DQEHAaCCAvkEggL1eyJhY2Nlc3MiOiB7InRva2VuIjogeyJpc3N1ZWRfYXQiOiAiMjAxNC0wMS0wOVQwMzo0MToyNS45NjI4MzUiLCAiZXhwaXJlcyI6ICIyMDE0LTAxLTEwVDAzOjQxOjI1WiIsICJpZCI6ICJwbGFjZWhvbGRlciIsICJ0ZW5hbnQiOiB7ImRlc2NyaXB0aW9uIjogIkFkbWluIFRlbmFudCIsICJlbmFibGVkIjogdHJ1ZSwgImlkIjogIjVlNzE3ODk4MzEwNDQ4MmE5MjQzM2Y1NWIwOWM2ZGNkIiwgIm5hbWUiOiAiYWRtaW4ifX0sICJzZXJ2aWNlQ2F0YWxvZyI6IFt7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly9jb250cm9sbGVyOjM1MzU3L3YyLjAiLCAicmVnaW9uIjogInJlZ2lvbk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vY29udHJvbGxlcjo1MDAwL3YyLjAiLCAiaWQiOiAiNzMzOWEwZDY1NDBlNGM0MGI4YWE4NGI1NDEzODNlMTYiLCAicHVibGljVVJMIjogImh0dHA6Ly9jb250cm9sbGVyOjUwMDAvdjIuMCJ9XSwgImVuZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJpZGVudGl0eSIsICJuYW1lIjogImtleXN0b25lIn1dLCAidXNlciI6IHsidXNlcm5hbWUiOiAiYWRtaW4iLCAicm9sZXNfbGlua3MiOiBbXSwgImlkIjogImQ5MzA4YmUzYmE5MjRiOTRhYzA1MWViOGQwNzUwY2Q2IiwgInJvbGVzIjogW3sibmFtZSI6ICJhZG1pbiJ9XSwgIm5hbWUiOiAiYWRtaW4ifSwgIm1ldGFkYXRhIjogeyJpc19hZG1pbiI6IDAsICJyb2xlcyI6IFsiMzcyNDc4ZWUyZDI2NDc4NThkODY0Y2U5MDY2YWNjY2QiXX19fTGCAYEwggF9AgEBMFwwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVVuc2V0MQ4wDAYDVQQHDAVVbnNldDEOMAwGA1UECgwFVW5zZXQxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbQIBATAHBgUrDgMCGjANBgkqhkiG9w0BAQEFAASCAQA3-Ce1g6lmH707ibg+NDRTn2xiTds9SkWUvpHNTPSDvbnW5-eoSSE+Ty7EVMb5XFkCZlDYHHKuDRfdG9reFfQq9qVcXzS4R8FAxJF8V9Nfx4z76l1yGyZxRbn566uKPYc2BUlp3ZydeP7eX+k54GCNkPj+dMgCcr4weSCfuIXaVejKVsKaz1fJu7AmypeDa24fTjseJUTzqUNkCLp4+RkVMsb2SaQDgNSSJf890m+TB70zPJN63IasJvH8DnAzZ2G2F2MIw+zBAQ7kTl7lskRKOGVtUIZxxxb8TGWP+Y34VrAHLoUoXsP+fQhIlv-dKfuNt5qoWe0AlnBkrwQl3HK1 |
| tenant_id | 5e7178983104482a92433f55b09c6dcd |
| user_id | d9308be3ba924b94ac051eb8d0750cd6 |
+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
}
期望返回一绑定租户的token。
- 脚本操作:上述手动操作的替代方法
#!/bin/bash
export OS_USERNAME=admin
export OS_PASSWORD=ADMIN_PASS
export OS_TENANT_NAME=admin
export OS_AUTH_URL=http://controller:35357/v2.0
}
- 确保admin账户已经被验证
keystone user-list
+----------------------------------+-------+---------+---------------------------+
| id | name | enabled | email |
+----------------------------------+-------+---------+---------------------------+
| d9308be3ba924b94ac051eb8d0750cd6 | admin | True | yangyongbupt168@gmail.com |
+----------------------------------+-------+---------+---------------------------+
通过命令行客户端进行API调用,可以设置在脚本中自动执行。在OpenStack内部,可以运行cURL命令执行内嵌API请求。OpenStack是一种基于HTTP协议REST风格的API,包括方法、URI、介质类型和响应代码。
每个Openstack服务都有自己的命令行客户端。
keystone service-create —name=glance —type=image \
—description=”Glance Image Service”
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| description | Glance Image Service |
| id | 21b15dc9b1d846149883e953af08d906 |
| name | glance |
| type | image |
+-------------+----------------------------------+
keystone endpoint-create —service-id=21b15dc9b1d846149883e953af08d906 —publicurl=http://controller:9292 —internalurl=http://controller:9292 \
—adminurl=http://controller:9292
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| adminurl | http://controller:9292 |
| id | 9cf77dd344514215812b1391dbf2b498 |
| internalurl | http://controller:9292 |
| publicurl | http://controller:9292 |
| region | regionOne |
| service_id | 21b15dc9b1d846149883e953af08d906 |
+-------------+----------------------------------+
验证安装:
root@controller:/home/fyang/project/images# glance image-create —name=”CirrOS 0.3.0” —disk-format=qcow2 —container-format=bare —is-public=true < cirros-0.3.0-x86_64-disk.img
root@controller:/home/fyang/project/images# glance image-create —name=”CirrOS 0.3.0” —disk-format=qcow2 —container-format=bare —is-public=true < cirros-0.3.0-x86_64-disk.img
glance image-list
+--------------------------------------+--------------+-------------+------------------+---------+--------+
| ID | Name | Disk Format | Container Format | Size | Status |
+--------------------------------------+--------------+-------------+------------------+---------+--------+
| 59c63869-ce77-4647-90b5-782d258e3354 | CirrOS 0.3.0 | qcow2 | bare | 9761280 | active |
| 05f2ae82-0926-4b8a-9d11-dfc99d8ee845 | CirrOS 0.3.1 | qcow2 | bare | 9761280 | active |
+--------------------------------------+--------------+-------------+------------------+---------+--------+
nova-manage db sync
2014-01-09 17:45:46.814 7918 INFO migrate.versioning.api [-] 215 -> 216...
2014-01-09 17:45:46.837 7918 INFO migrate.versioning.api [-] done
keystone user-create —name=nova —pass=NOVA_PASS —email=nova@example.
+----------+----------------------------------+
| Property | Value |
+----------+----------------------------------+
| email | nova@example. |
| enabled | True |
| id | 26e1951aa2264a11800c68cc58dbce59 |
| name | nova |
+----------+----------------------------------+
root@controller:/home/fyang/project/images# keystone service-create —name=nova —type=compute —description=”Nova Compute service”
+-------------+----------------------------------+
| Property | Value |
+-------------+----------------------------------+
| description | Nova Compute service |
| id | 0d67dd130ebc4de2ba8e54ac241e92d0 |
| name | nova |
| type | compute |
+-------------+----------------------------------+
root@controller:/home/fyang/project# keystone endpoint-create —service-id=0d67dd130ebc4de2ba8e54ac241e92d0 —publicurl=http://controller:8774/v2/%\(tenant_id\)s —internalurl=http://controller:8774/v2/%\(tenant_id\)s —adminurl=http://controller:8774/v2/%\(tenant_id\)s
+-------------+-----------------------------------------+
| Property | Value |
+-------------+-----------------------------------------+
| adminurl | http://controller:8774/v2/%(tenant_id)s |
| id | 21f036edca9547e196b5976a4c31f561 |
| internalurl | http://controller:8774/v2/%(tenant_id)s |
| publicurl | http://controller:8774/v2/%(tenant_id)s |
| region | regionOne |
| service_id | 0d67dd130ebc4de2ba8e54ac241e92d0 |
+-------------+-----------------------------------------+
]]>本篇英文原文所发布的站点Adam Petersen是一个个人网站,本文翻译了其中reactor C实现章节, 水平有限,欢迎指正。
1、多客户端示例
2、单一责任原则
3、违反Open-Closed原则
4、从性能谈起
5、问题总结
6、REACTOR模式
6.1、事件探测
6.2、实现机制
6.3、Reactor注册策略
6.4、Reactor实现
6.5、触发Reactor
6.6、处理注册
6.7、事件多样性
7、REACTOR vs OBSERVER
8、结论
9、总结
本文将研究一种适用于event-driven应用的模式。Reactor Pattern将应用的不同职责解耦,允许应用由多个潜在的client端分离并分发事件。
为了简化对大系统的维护,工程师可以单独搜集子系统的诊断信息然后再合并至中心。每个子系统利用TCP/IP连接至诊断服务器,由于TCP/IP是一个面向连接的协议,客户端(不同的子系统)不得不单独向server请求相应的连接。一旦连接建立,client可以随时发送诊断信息。
为了解决这个问题,最容易想到的方法就是服务器依次扫描客户端的连接请求和诊断信息,如下图所示:
尽管这是一个极其简化的例子,这里仍然存在不少潜在的问题。首先是该方法将应用逻辑、网络代码、事件分发代码这几个毫不相干的逻辑捆绑在一起,会带来严重的维护、测试和可扩展问题,这样的设计违反了基本的设计原则。
单一责任原则,即一个类只能有一种原因去驱动改变,该原则的是同open-closed原则有异曲同工之妙:尽量避免现有代码的修改。当违反了单一原则的时候,一个模块有多种原因需要去修改。更糟的是,多个不同的责任集中于一个模块当中会耦合在一起,使得测试与修改变得非常复杂。
单一责任原则本质是集中性,便于进行多层抽象,而不仅仅是一个过程上下文,简单地讲类替换为函数将有利于我们在此原则基础上分析算法。
如果违反了Open-Closed原则,上述例子的模块将变得难以维护,或者该代码在设计之初就没打算扩展性。不幸的是,该中设计已经违反了Open-Closed原则,若不改变现有代码,新的功能将无法加入。总而言之,这种设计使得修改代码变得代价高昂。
这种方案的糟糕之处还在于整个系统的性能变得很差,当线性扫描所有事件时,尽快采用了超时机制,然而很多时间被白白浪费了。
一个潜在的性能问题就是并发失败的处理,一种解决方案是引入多线程,诊断服务器轮询事件,但是此时的轮询代价已经只剩下处理连接请求,一旦请求建立,分配相应的线程处理该连接上的所有消息处理。
然后多线程方案无法从根本上解决这种设计的问题:违反了单一责任原则和Open-Closed原则。尽管扫描代码和诊断消息处理从主循环中移除,添加一个新的服务端口仍需修改现有代码。
从设计的角度看,线程不能改变任何事情,事实上,即使考虑到性能,这种改进导致上下文切换和同步,反而比单线程方法更糟。
总结以上的经验,我们发现上述设计方南的失败之处归结于它们以三种责任为解决前提。问题即违反了Open-Closed原则,导致难于修改现有代码。
理想的解决方案应该是易于扩展、封装和多种责任解耦,并能够同时服务多个客户端,即使不引入多线程。Reactor模式通过在事件处理中封装应用逻辑和在事件分发中隔离代码很好的解决了这些问题。
Reactor模式定义:“reactor架构使得事件驱动型的不同应用实现分离,并将来自一个或多个客户端的服务请求分发至一个应用中”。
其中包含的参与者如下:
在一些文献中关于 reactor 的描述中,定义了一个工具,即 synchronous Event Demultiplexer , 该分离器由 reactor 调用等待已经注册的 Handles 的事件。
该事件分离器一般由操作系统提供,比如 poll() ,select() 和 WaitForMutipleObjects() 。
EventHandler 和 Reactor 之间的合作关系类似OBSERVER模式中的observer和它的对象。
为了将Reactor和它的事件处理器解耦,同时Reactor仍能够通知到他们,每个具体的事件处理器必须关联一个唯一的instance。这里的C实现中,采用void 作为通用类型以描述 *EventHandler 接口。
|
|
|
|
|
|
当实现具体的事件处理器时定义抽象数据结构,这样做的好处是封装具体的注册处理细节,这样就隐藏了具体的信息,client甚至无需了解如何同reactor交互。
Reactor的另外一个好处体现在server内部,比如说对handle的封装通过getServerSocket获得,这样我们为reactor提供一个方式获取handle;同时,reactor对事件处理器进行访问控制:只有注册的事件处理器方可被调用相应的handle及其相关资源。
Reactor的实现依赖于具体的同步事件分发器,如果OS提供了多种同步事件分发器,比如select()和poll(),Reactor就需要针对这些分发器实现多个reactor实例,并由链接器根据问题选择,即所谓的链接多态。
每种Reactor的实现必须确定该应用下所需要的reactor实例。在多数场景中,应用只需一个reactor实例,即single reactor。如果应用需要多个reactor实例,则可以对reactor本身进行抽象。
为了独立于具体的分离机制,Reactor必须维护一个已注册具体的事件处理器集合。简单地做法就是采用数组,适用于最大的客户端数目已知的情形。
基于Poll()实现的Reactor:PollReactor.c
|
|
反应事件轮询是Reactor的核心,其职责在于根据具体的事件处理器控制已注册事件的分离和分发。事件循环典型由main()函数调用。
驱动reactor的客户端代码如下:
|
|
为了整体设计,需要单独为HandleEvents()创建一个文件实现,PollReactor.c,其中每个元素都将已注册事件的句柄和用于同poll()交互的结构绑定在一起。另外一种替代方案是维护两个不同的链表以确保二者的一致性。UNIX实现采用select(),定义为“一个以UNIX I/O句柄值为索引的数组,范围为0至FD_SETSIZE-1”。
在本实现中,通过将poll结构和注册组合在一起,由于采用数组用于同poll()交互,因此该数组必须在每次事件轮询进入的时刻建立。
在本文的reactor实现中,server通过创建一个新的client来响应通知,这样一来,必须注册方可再次激活。
一种解决方案是维护一个单独的数组用于同同步事件分离器进行交互, 该数组在事件循环中不能被修改。
而句柄的实现则依赖平台,句柄ID有可能被重用;在拷贝中的一个信号句柄有可能属于一个未注册的事件句柄,但是由于注册重用了句柄ID,新的事件处理器可能被错误触发。我们可以加入哨兵数据以标识是否重用来解决该问题。
实例代码仅仅考虑了一种类型的事件(read事件),其类型是hardcoded。Reactor本身并未受类型限制,能够很好的支持多种类型。
两种通用的事件通知分发机制:
尽管用于实现二者的机制相关,但是仍存在差异,主要的不同点在于通知机制。在OBSERVER实现中,当一个Subject改变其状态时,它的所有依赖者(observers)都被通知。而在REACTOR实现中,通知的关系式一对一,即一个已探测到的事件导致Reactor通知其对应的实例(EventHandler)。
另外,一个典型的OBSERVER模式下的subject是低内聚的,除了服务其核心目的,一个subject也会负责管理和通知observers。相反,一个Reactor则仅仅分发已经注册的处理器。
应用REACTOR模式的主要结论如下:
Reactor模式通过对不同责任的解耦和不同模块的封装简化了事件驱动型应用的设计。关于该模式更多的讨论可以参考面向对象的软件模式卷2。
]]>硬件加速引擎的目的在于减少加解密中软件的干预,从而提高性能.该硬件加密引擎共有四个引擎,每次运行一个,支持的加密算法有:
对AES而言,首先将报文、配置信息以及密钥和初始化向量放入一连串的TDMA描述符当中,并配置chain模式,最后启动TDMA,即可连续加密。 IV是由用户配置的,后续的IV由硬件自己填充,CTR模式可以通过软件实现。
中断发生,会将对于位置位,通知软件取数。
支持的验证算法为SHA或者MD5。
在硬件加速实现中,CPU和加速引擎精密协作但具体分工又不尽相同。
对CPU而言,主要完成三件事:
struct sa_accel_sram
,并映射至硬件当中对应的镜像位置(偏移为SRAM_CONFIG
, SRAM_KEY
, SRAM_DATA_IN
等)。而加速引擎的职责包括计算、与CPU交互和数据搬运,具体来说,包括CESA和TDMA两个子引擎,其中CESA负责对数据进行操作,主要流程如下:
而TDMA只是负责在主存和SRAM之间拷贝数据。
Access
CPU和SEAS交替控制,但在一个时间点上只能有一个作为HOST,TDMA当中有owner位,指示出当前的HOST是TDMA设备还是CPU。
Control
若干特殊的命令控制器,比如SHA-1/MD5相关的命令寄存器:
|
|
输入
输出
操作原则
地址映射
|
|
寄存器配置
实例: 修改DOVE寄存器
|
|
加密流程
aes_term
是否为1,类似轮询操作;AES_CTRL_REG
: 密钥长度和数据大小字节序;解密流程
当加密操作完成时,key发生了变化,必须根据最后的加密key生成key schedule;具体如下:
其中解密密钥生成步骤:
主机可能会在内存中保存解密key,这样key计算可以在下次忽略(采用相同的key):
启用CESA,本质上是数据在DRAM和SRAM之间的拷贝,如果采用DMA,需要将DRAM和SRAM中数据的虚拟地址转化为对应的物理地址。
由于CESA只能处理在CESA SRAM中的报文,最大支持2KB,因此在包含其他信息的前提下(SA描述符、key、IV),实际处理的数据最大是1.75KB。
应用请求分发方式
对于来自上层的应用请求(AES加解密)可以采用以下两种同步回调模式和异步回调模式。
对于同步回调模式而言,同步操作较为简单,但是当请求被提交时程序会阻塞;调用同步回调函数(实现数据由软件传至硬件)即可,一旦硬件处理完毕,即可返回,流程如下:
而在异步回调模式下,异步请求会被加入硬件队列,同时返回,当硬件完成请求时,硬件调用另外一个处理程序返回,并送入上层协议栈,通常情况下效率更高,除了同步回调函数,必须注册另外一个回调函数(通知回调函数)以方便硬件完成操作之后调用,该回调通知函数在上层实现,主要将结果传至上层协议栈。
目前采用的是同步方式。
中间层
在硬件接口和上层接口层之间加入一个中间层,以此隔离上层对于底层调用的依赖,可以基于纯软件实现AES加解密,也可以基于硬件实现。
流程控制
采用中断方式接收数据并进行处理。中断是硬件处理数据之后的动作,因此,触发中断需要有两级:
具体的流程如下:
DMA
在该平台的硬件引擎中,专门实现了DMA,称之为TDMA。TDMA的基本特点如下:
TDMA最主要的寄存器是TMDA描述符,由四个32位寄存器组成:
TDMA与加密引擎协调工作
TDMA作为一种硬件内置的DMA方式,与其他硬件加速引擎配合工作,较为显著的提高了吞吐率,基本流程如下:
首先设置CESA配置寄存器(0xE08)的waitforTDMA和ActiveTDMA,当TMDA和CESA协同工作的时候,CESA能独立操作DRAM,CESA有能力激活TDMA,决定其状态并提供单独的完成中断;其中软件则需要步骤(五步):
TDMA的基本功能和软件流程综述如下:
其中CESA中的SRAM内存映射如下:
|
|
其中CESA_SRAM_SIZE=2048
, CESA_DATA(0)=sizeof(struct cesa_sa_hdesc)
,字节序则利用宏直接转化。
对于分片报文,可以暂时不支持,以简化设计 CESA 的SDK支持最大报文为64 * 1024,而一次处理1600大小,因此需要分片。
SRAM地址可以直接返回一个全局变量:MV_U32 cesaCryptEngBase = 0
;
对于SA配置,可以定义数据结构sa_config
和相应的配置函数。
在开发前期,为了尽快试错以及避免代码注释混乱,采用switch语句定义不同的dec_key方法,并用全局变量用GUI配置,则无需重新编译即可完成正确方案的选择。而在面对时而正确时而错误的加解密情况,加入打印语句:
基于以上分析思路,解决了数据乱码问题,定位过程如下:
数据拷贝流程分析
分解TDMA和CESA的交互过程,逐步调试,至比如数据从主存至TDMA,是否拷贝正确; 数据处理完之后是否拷贝至主存?
Cache相关
这里涉及两个问题:clean cache和Invalide cache:
DMA相关
DMA要考虑页表连续性和CPU-cache。
现象:
采用软件对比方法,对比几个方面,包括DMA-out/HW caculate,分析出错误点。硬件计算错误的原因(配置错误、密钥IV错误、源目的地址错误),排除了前二者,只能是传入了错误的物理地址给硬件。在进行虚实地址转化的时候,由于转化后的物理地址可能跨页,因此必须分页拷贝,即计算出距离页边界的偏移,判断是否跨页,如果跨了先拷贝当前页数据,在拷贝下一页数据。其中DMA和CPU-cache是两个不同的两个概念:
随机问题定位
在实际的调试中在单个进程中调用时没有问题,在多个进程调用的时候出现问题,因此可以确定可能是进程并发的问题。经过调试发现,中断会在休眠之前发生,因此在需要禁止中断。
另外一个随机问题是在释放版本中发现的,硬件加速引擎偶尔出现超时,但是在开发版本中从不会超时,控制结构体中的一个描述状态的字段不太正常,后者相对前者做了编译优化,导致发生不可预期的错误,因此需要在其定义出加入volitile关键字,问题得以解决。