-
Protocol Buffer 序列化原理大揭秘 - 为什么Protocol Buffer性能这么好?
2017-04-24 09:19:04习惯用 Json、XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存储格式,性能比 Json、XML 真的强!太!多! 由于 Google出品,我相信...前言
- 习惯用
Json、XML
数据存储格式的你们,相信大多都没听过Protocol Buffer
Protocol Buffer
其实 是Google
出品的一种轻量 & 高效的结构化数据存储格式,性能比Json、XML
真的强!太!多!
由于
Google
出品,我相信Protocol Buffer
已经具备足够的吸引力- 今天,我将讲解为什么
Protocol Buffer
的性能如此的好:
a. 序列化速度 & 反序列化速度快
b. 数据压缩效果好,即序列化后的数据量体积小
阅读本文前请先阅读:
目录
1. 定义
一种 结构化数据 的数据存储格式(类似于
XML、Json
)Google
出品 (开源)Protocol Buffer
目前有两个版本:proto2
和proto3
- 因为
proto3
还是beta 版,所以本次讲解是proto2
2. 作用
通过将 结构化的数据 进行 串行化(序列化),从而实现 数据存储 / RPC 数据交换的功能
- 序列化: 将 数据结构或对象 转换成 二进制串 的过程
- 反序列化:将在序列化过程中所生成的二进制串 转换成 数据结构或者对象 的过程
3. 特点
- 对比于 常见的
XML、Json
数据存储格式,Protocol Buffer
有如下特点:
4. 应用场景
传输数据量大 & 网络环境不稳定 的数据存储、RPC 数据交换 的需求场景
如 即时IM (QQ、微信)的需求场景
总结
在 传输数据量较大的需求场景下,
Protocol Buffer
比XML、Json
更小、更快、使用 & 维护更简单!5. 使用流程
关于
Protocol Buffer
的使用流程,具体请看我写的文章:快来看看Google出品的Protocol Buffer,别只会用Json和XML了6. 知识基础
6.1 网络通信协议
- 序列化 & 反序列化 属于通讯协议的一部分
- 通讯协议采用分层模型:
TCP/IP
模型(四层) &OSI
模型 (七层)
-
序列化 / 反序列化 属于
TCP/IP
模型 应用层 和 OSI`模型 展示层的主要功能:- (序列化)把 应用层的对象 转换成 二进制串
- (反序列化)把 二进制串 转换成 应用层的对象
-
所以,
Protocol Buffer
属于TCP/IP
模型的应用层 &OSI
模型的展示层
6.2 数据结构、对象与二进制串
不同的计算机语言中,数据结构,对象以及二进制串的表示方式并不相同。
a. 对于数据结构和对象
-
对于面向对象的语言(如
Java
):对象 =Object
= 类的实例化;在Java
中最接近数据结构 即POJO
(Plain Old Java Object
),或Javabean
(只有setter/getter
方法的类) -
对于半面向对象的语言(如
C++
),对象 =class
,数据结构 =struct
b. 二进制串
- 对于
C++
,因为具有内存操作符,所以 二进制串 容易理解:C++
的字符串可以直接被传输层使用,因为其本质上就是以'\0'
结尾的存储在内存中的二进制串 - 对于
Java
,二进制串 = 字节数组 =byte[]
byte
属于Java
的八种基本数据类型- 二进制串 容易和
String
混淆:String
一种特殊对象(Object)。对于跨语言间的通讯,序列化后的数据当然不能是某种语言的特殊数据类型。
6.3
T - L - V
的数据存储方式-
定义
即Tag - Length - Value
,标识 - 长度 - 字段值 存储方式 -
作用
以 标识 - 长度 - 字段值 表示单个数据,最终将所有数据拼接成一个 字节流,从而 实现 数据存储 的功能
其中
Length
可选存储,如 储存Varint
编码数据就不需要存储Length
- 示意图
- 优点
从上图可知,T - L - V
存储方式的优点是- 不需要分隔符 就能 分隔开字段,减少了 分隔符 的使用
- 各字段 存储得非常紧凑,存储空间利用率非常高
- 若 字段没有被设置字段值,那么该字段在序列化时的数据中是完全不存在的,即不需要编码
相应字段在解码时才会被设置为默认值
7. 序列化原理解析
请记住
Protocol Buffer
的 三个关于数据存储 的重要结论:- 结论1:
Protocol Buffer
将 消息里的每个字段 进行编码后,再利用T - L - V
存储方式 进行数据的存储,最终得到的是一个 二进制字节流
序列化 = 对数据进行编码 + 存储
- 结论2:
Protocol Buffer
对于不同数据类型 采用不同的 序列化方式(编码方式 & 数据存储方式),如下图:
从上表可以看出:
- 对于存储
Varint
编码数据,就不需要存储字节长度Length
,所以实际上Protocol Buffer
的存储方式是T - V
; - 若
Protocol Buffer
采用其他编码方式(如LENGTH_DELIMITED
)则采用T - L - V
- 结论3:因为
Protocol Buffer
对于数据字段值的 独特编码方式 &T - L - V
数据存储方式,使得Protocol Buffer
序列化后数据量体积如此小
下面,我将对不同的编码方式 & 数据存储方式进行逐一讲解。
7.1 Wire Type = 0时的编码 & 数据存储方式
7.1.1 编码方式:
Varint
&Zigzag
A.
Varint
编码方式介绍i. 简介
- 定义:一种变长的编码方式
- 原理:用字节 表示 数字:值越小的数字,使用越少的字节数表示
- 作用:通过减少 表示数字 的字节数 从而进行数据压缩
如:
- 对于
int32
类型的数字,一般需要 4个字节 表示;
- 若采用
Varint
编码,对于很小的int32
类型 数字,则可以用 1个字节 来表示 - 虽然大的数字会需要 5 个 字节 来表示,但大多数情况下,消息都不会有很大的数字,所以采用
Varint
方法总是可以用更少的字节数来表示数字
ii. 原理介绍
- 源码分析
private void writeVarint32(int n) { int idx = 0; while (true) { if ((n & ~0x7F) == 0) { i32buf[idx++] = (byte)n; break; } else { i32buf[idx++] = (byte)((n & 0x7F) | 0x80); // 步骤1:取出字节串末7位 // 对于上述取出的7位:在最高位添加1构成一个字节 // 如果是最后一次取出,则在最高位添加0构成1个字节 n >>>= 7; // 步骤2:通过将字节串整体往右移7位,继续从字节串的末尾选取7位,直到取完为止。 } } trans_.write(i32buf, 0, idx); // 步骤3: 将上述形成的每个字节 按序拼接 成一个字节串 // 即该字节串就是经过Varint编码后的字节 }
从上面可看出:
Varint
中每个 字节 的最高位 都有特殊含义:- 如果是 1,表示后续的 字节 也是该数字的一部分
- 如果是 0,表示这是最后一个字节,且剩余 7位 都用来表示数字
所以,当使用
Varint
解码时时,只要读取到最高位为0的字节时,就表示已经是Varint
的最后一个字节因此:
- 小于 128 的数字 都可以用 1个字节 表示;
- 大于 128 的数字,比如 300,会用两个字节来表示:10101100 00000010
下面,我将用两个个例子来说明
Varint
编码方式的使用- 目的:对 数据类型为Int32 的 字段值为296 和字段值为104 进行
Varint
编码 - 以下是编码过程
从上面可以看出:
- 对于 int32 类型的数字,一般需要 4 个字节 来表示;
- 但采用
Varint
方法,对于很小的Int32
类型 数字(小于256),则可以用 1个字节 来表示;
以此类推,比如300也只需要2个字节
- 虽然大的数字会需要 5 个字节 来表示,但大多数情况下,消息都不会有很大的数字
- 所以采用
Varint
方法总是可以用更少的字节数来表示数字,从而更好地实现数据压缩
下面继续看如何解析经过
Varint
编码的字节Varint
编码方式的不足- 背景:在计算机内,负数一般会被表示为很大的整数
因为计算机定义负数的符号位为数字的最高位
- 问题:如果采用
Varint
编码方式 表示一个负数,那么一定需要 5 个 byte(因为负数的最高位是1,会被当做很大的整数去处理) - 解决方案:
Protocol Buffer
定义了sint32 / sint64
类型表示负数,通过先采用Zigzag
编码(将 有符号数 转换成 无符号数),再采用Varint
编码,从而用于减少编码后的字节数
- 对于
int32 / int64
类型的字段值(正数),Protocol Buffer
直接采用Varint
编码 - 对于
sint32 / sint64
类型的字段值(负数),Protocol Buffer
会先采用Zigzag
编码,再采用Varint
编码
- 总结:为了更好地减少 表示负数时 的字节数,
Protocol Buffer
在Varint
编码上又增加了Zigzag
编码方式进行编码 - 下面将详细介绍
Zigzag
编码方式
B.
Zigzag
编码方式详解i. 简介
- 定义:一种变长的编码方式
- 原理:使用 无符号数 来表示 有符号数字;
- 作用:使得绝对值小的数字都可以采用较少 字节 来表示;
特别是对 表示负数的数据 能更好地进行数据压缩
b. 原理
- 源码分析
public int int_to_zigzag(int n) // 传入的参数n = 传入字段值的二进制表示(此处以负数为例) // 负数的二进制 = 符号位为1,剩余的位数为 该数绝对值的原码按位取反;然后整个二进制数+1 { return (n <<1) ^ (n >>31); // 对于sint 32 数据类型,使用Zigzag编码过程如下: // 1. 将二进制表示数 左移1位(左移 = 整个二进制左移,低位补0) // 2. 将二进制表示数 右移31位 // 对于右移: // 首位是1的二进制(有符号数),是算数右移,即右移后左边补1 // 首位是0的二进制(无符号数),是逻辑左移,即右移后左边补0 // 3. 将上述二者进行异或 // 对于sint 64 数据类型 则为: return (n << 1> ^ (n >> 63) ; } // 附:将Zigzag值解码为整型值 public int zigzag_to_int(int n) { return(n >>> 1) ^ -(n & 1); // 右移时,需要用不带符号的移动,否则如果第一位数据位是1的话,就会补1 }
- 实例说明:将
-2
进行Zigzag
编码:
Zigzag
编码 是补充Varint
编码在 表示负数 的不足,从而更好的帮助Protocol Buffer
进行数据的压缩- 所以,如果提前预知字段值是可能取负数的时候,记得采用
sint32 / sint64
数据类型
总结
Protocol Buffer
通过Varint
和Zigzag
编码后大大减少了字段值占用字节数。7.1.2 存储方式:
T - V
- 消息字段的标识号、数据类型 & 字段值经过
Protocol Buffer
采用Varint
&Zigzag
编码后,以T - V
方式进行数据存储
对于
Varint
&Zigzag
编码,省略了T - L - V
中的字节长度Length下面将详细介绍
T - V
存储方式中的存储细节:Tag
&Value
1. Tag
- 定义:经过
Protocol Buffer
采用Varint
&Zigzag
编码后 的消息字段 标识号 & 数据类型 的值 - 作用:标识 字段
- 存储了字段的标识号(
field_number
)和 数据类型(wire_type
),即Tag
= 字段数据类型(wire_type
) + 标识号(field_number
) - 占用 一个字节 的长度(如果标识号超过了16,则占用多一个字节的位置)
- 解包时,
Protocol Buffer
根据Tag
将Value
对应于消息中的 字段
- 具体使用
// Tag 的具体表达式如下 Tag = (field_number << 3) | wire_type // 参数说明: // field_number:对应于 .proto文件中消息字段的标识号,表示这是消息里的第几个字段 // field_number << 3:表示 field_number = 将 Tag的二进制表示 右移三位 后的值 // field_num左移3位不会导致数据丢失,因为表示范围还是足够大地去表示消息里的字段数目 // wire_type:表示 字段 的数据类型 // wire_type = Tag的二进制表示 的最低三位值 // wire_type的取值 enum WireType { WIRETYPE_VARINT = 0, WIRETYPE_FIXED64 = 1, WIRETYPE_LENGTH_DELIMITED = 2, WIRETYPE_START_GROUP = 3, WIRETYPE_END_GROUP = 4, WIRETYPE_FIXED32 = 5 }; // 从上面可以看出,`wire_type`最多占用 3位 的内存空间(因为 3位 足以表示 0-5 的二进制) // 以下是 wire_type 对应的 数据类型 表
- 实例说明
// 消息对象 message person { required int32 id = 1; // wire type = 0,field_number =1 required string name = 2; // wire type = 2,field_number =2 } // 如果一个Tag的二进制 = 0001 0010 标识号 = field_number = field_number << 3 =右移3位 = 0000 0010 = 2 数据类型 = wire_type = 最低三位表示 = 010 = 2
2. Value
经过
Protocol Buffer
采用Varint
&Zigzag
编码后 的消息字段的值7.1.3 实例说明
下面通过一个实例进行整个编码过程的说明:
- 消息说明
message Test { required int32 id1 = 1; required int32 id2 = 2; } // 在代码中给id1 附上1个字段值:296 // 在代码中给id2 附上1个字段值:296 Test.setId1(300); Test.setId2(296); // 编码结果为:二进制字节流 = [8,-84,2,16, -88, 2]
- 整个编码过程如下
7.2 Wire Type = 1& 5时的编码&数据存储方式
64(32)-bit
编码方式较简单:编码后的数据具备固定大小 = 64位(8字节) / 32位(4字节)
两种情况下,都是高位在后,低位在前
- 采用
T - V
方式进行数据存储,同上。
7.3 Wire Type = 2时的 编码 & 数据存储方式
- 对于编码方式:
- 数据存储方式:
T - L - V
此处主要讲解三种数据类型:
String
类型- 嵌套消息类型(
Message
) - 通过
packed
修饰的repeat
字段(即packed repeated fields
)
1. String类型
字段值(即
V
) 采用UTF-8
编码- 例子:
message Test2 { required string str = 2; } // 将str设置为:testing Test2.setStr(“testing”) // 经过protobuf编码序列化后的数据以二进制的方式输出 // 输出为:18, 7, 116, 101, 115, 116, 105, 110, 103
2. 嵌套消息类型(Message)
- 存储方式:
T - L - V
- 外部消息的
V
即为 嵌套消息的字段 - 在
T - L -V
里嵌套了一系列的T - L -V
- 编码方式:字段值(即
V
) 根据字段的数据类型采用不同编码方式
- 实例
定义如下嵌套消息:
message Test2 { required string str = 1; required int32 id1 = 2; } message Test3 { required Test2 c = 1; } // 将Test2中的字段str设置为:testing // 将Test2中的字段id1设置为:296 // 编码后的字节为:10 ,12 ,18,7,116, 101, 115, 116, 105, 110, 103,16,-88,2
编码 & 存储方式如下
3. 通过
packed
修饰的repeat
字段repeated
修饰的字段有两种表达方式:message Test { repeated int32 Car = 4 ; // 表达方式1:不带packed=true repeated int32 Car = 4 [packed=true]; // 表达方式2:带packed=true // proto 2.1 开始可使用 // 区别在于:是否连续存储repeated类型数据 } // 在代码中给`repeated int32 Car`附上3个字段值:3、270、86942 Test.setCar(3); Test.setCar(270); Test.setCar(86942);
- 背景:对于同一个
repeated
字段、多个字段值来说,他们的Tag都是相同的,即数据类型 & 标识号都相同
repeated
类型可以看成是数组- 问题:若以传统的多个 T - V对存储(不带
packed=true
),则会导致Tag的冗余,即相同的Tag存储多次;
- 解决方案:采用带
packed=true
的repeated
字段存储方式,即将相同的Tag
只存储一次、添加repeated
字段下所有字段值的长度Length
、连续存储repeated
字段值,组成一个大的Tag - Length - Value -Value -Value
对,即T - L - V - V - V
对。
通过采用带
packed=true
的repeated
字段存储方式,从而更好地压缩序列化后的数据长度。特别注意
Protocol Buffer
的packed
修饰只用于repeated
字段 或 基本类型的repeated
字段- 用在其他字段,编译
.proto
文件时会报错
8. 特别注意
- 注意1:若
required
字段没有被设置字段值,那么在IsInitialized()
进行初始化检查会报错并提示失败
所以
required
字段必须要被设置字段值- 注意2:序列化顺序 是根据
Tag
标识号 从小到大 进行编码
和
.proto
文件内 字段定义的数据无关- 注意3:
T - V
的数据存储方式保证了Protobuf
的版本兼容:高<->低 或 低<->高都可以适配
若新版本 增加了
required
字段, 旧版本 在数据解码时会认为IsInitialized()
失败,所以慎用required
字段9. 使用建议
根据上面的序列化原理分析,我总结出以下
Protocol Buffer
的使用建议通过下面建议能有效降低序列化后数据量的大小
- 建议1:多用
optional
或repeated
修饰符
因为若optional
或repeated
字段没有被设置字段值,那么该字段在序列化时的数据中是完全不存在的,即不需要进行编码
相应的字段在解码时才会被设置为默认值
-
建议2:字段标识号(
Field_Number
)尽量只使用 1-15,且不要跳动使用
因为Tag
里的Field_Number
是需要占字节空间的。如果Field_Number
>16时,Field_Number
的编码就会占用2个字节,那么Tag
在编码时也就会占用更多的字节;如果将字段标识号定义为连续递增的数值,将获得更好的编码和解码性能 -
建议3:若需要使用的字段值出现负数,请使用
sint32 / sint64
,不要使用int32 / int64
因为采用sint32 / sint64
数据类型表示负数时,会先采用Zigzag
编码再采用Varint
编码,从而更加有效压缩数据 -
建议4:对于
repeated
字段,尽量增加packed=true
修饰
因为加了packed=true
修饰repeated
字段采用连续数据存储方式,即T - L - V - V -V
方式
10. 序列化 & 反序列化过程
Protocol Buffer
除了序列化 & 反序列化后的数据体积小,序列化 & 反序列化的速度也非常快- 下面我将讲解序列化 & 反序列化的序列化过程
10.1
Protocol Buffer
的序列化 & 反序列化过程序列化过程如下:
- 判断每个字段是否有设置值,有值才进行编码
- 根据 字段标识号&数据类型 将 字段值 通过不同的编码方式进行编码
由于:
a. 编码方式简单(只需要简单的数学运算 = 位移等等)
b. 采用Protocol Buffer
自身的框架代码 和 编译器 共同完成所以
Protocol Buffer
的序列化速度非常快。反序列化过程如下:
- 调用 消息类的
parseFrom(input)
解析从输入流读入的二进制字节数据流
从上面可知,
Protocol Buffer
解析过程只需要通过简单的解码方式即可完成,无需复杂的词法语法分析,因此 解析速度非常快。- 将解析出来的数据 按照指定的格式读取到
Java
、C++
、Phyton
对应的结构类型中
由于:
a. 解码方式简单(只需要简单的数学运算 = 位移等等)
b. 采用Protocol Buffer
自身的框架代码 和 编译器 共同完成所以
Protocol Buffer
的反序列化速度非常快。10.2 对比于
XML
的序列化 & 反序列化过程XML的反序列化过程如下:
- 从文件中读取出字符串
- 将字符串转换为
XML
文档对象结构模型 - 从
XML
文档对象结构模型中读取指定节点的字符串 - 将该字符串转换成指定类型的变量
上述过程非常复杂,其中,将
XML
文件转换为文档对象结构模型的过程通常需要完成词法文法分析等大量消耗 CPU 的复杂计算。因为序列化 & 反序列化过程简单,所以序列化 & 反序列化过程速度非常快,这也是
Protocol Buffer
效率高的原因11.总结
-
Protocol Buffer
的序列化 & 反序列化简单 & 速度快的原因是:
a. 编码 / 解码 方式简单(只需要简单的数学运算 = 位移等等)
b. 采用Protocol Buffer
自身的框架代码 和 编译器 共同完成 -
Protocol Buffer
的数据压缩效果好(即序列化后的数据量体积小)的原因是:
a. 采用了独特的编码方式,如Varint
、Zigzag
编码方式等等
b. 采用T - L - V
的数据存储方式:减少了分隔符的使用 & 数据存储得紧凑
- 关于
Protocol Buffer
系列文章,请看:
接下来我会讲解
Protocol Buffer
的源码分析,感兴趣的同学可以继续关注carson_ho的微信公众号请帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!
- 习惯用
-
LeetCode 报错解决 heap-buffer-overflow Heap-use-after-free Stack-buffer-overflow Global-buffer-...
2019-03-24 12:20:38文章目录前言Heap-buffer-overflowHeap-use-after-freeStack-buffer-overflowGlobal-buffer-overflow 前言 在做LeetCode题时发现一个有趣的事情。 对于C语言来说,如果直接访问超出Index的数组,会报错: int main...
LeetCode题解文章分类:LeetCode题解
LeetCode 所有题目总结:LeetCode 所有题目总结
前言
在做LeetCode题时发现一个有趣的事情。
对于C语言来说,如果直接访问超出Index的数组,会报错:int main(int argc, char **argv) { int array [100]; array[101] = -1; int res = array[-1]; return res; }
报错如下:
Runtime Error: Line 3: Char 10: runtime error: index 101 out of bounds for type 'int [100]' (solution.c)
但是如果你使用malloc分配空间给int数组,index的越界访问是不会直接报错的
Heap-buffer-overflow
但是LeetCode 使用了AddressSanitizer检查是否存在内存非法访问
#include <stdlib.h> int main(int argc, char **argv) { int *array = (int*)malloc(100 * sizeof(int)); array[0] = -1; int res = array[-1]; // BOOM return res; }
LeetCode 报错如下:
================================================================= ==30==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60300000000c at pc 0x000000401749 bp 0x7ffc91bd0570 sp 0x7ffc91bd0568 WRITE of size 4 at 0x60300000000c thread T0 #3 0x7ff2c35d42e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0) 0x60300000000c is located 4 bytes to the left of 20-byte region [0x603000000010,0x603000000024) allocated by thread T0 here: #0 0x7ff2c4a5e2b0 in malloc (/usr/local/lib64/libasan.so.5+0xe82b0) #4 0x7ff2c35d42e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0) Shadow bytes around the buggy address: 0x0c067fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c067fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0c067fff8000: fa[fa]00 00 04 fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==30==ABORTING
其实这是AddressSanitizer 这个工具的内存损坏检查报的错。
可以在Linux上运行如下命令,检查程序是否存在内存非法访问:gcc -O -g -fsanitize=address test.c ./a.out
Linux下运行报错如下:
allocated by thread T0 here: #0 0x7f8eb21bfd28 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc1d28) #1 0x563aa79a68bd in main /root/test4.c:3 SUMMARY: AddressSanitizer: heap-buffer-overflow /root/test4.c:5 in main Shadow bytes around the buggy address: 0x0c287fff9f70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9f90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9fa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c287fff9fc0: fa fa fa fa fa fa fa[fa]00 00 00 00 00 00 00 00 0x0c287fff9fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff9fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c287fff9ff0: 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa 0x0c287fffa000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fffa010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==7489==ABORTING
Heap-use-after-free
同时,AddressSanitizer也可以检查Heap-use-after-free的错:
int main(int argc, char **argv) { int *array = new int[100]; delete [] array; return array[argc]; // BOOM }
g++ -O -g -fsanitize=address heap-use-after-free.c ./a.out
报错如下:
================================================================= ==7849==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe44 at pc 0x56282de47977 bp 0x7fff9cfc65e0 sp 0x7fff9cfc65d8 READ of size 4 at 0x61400000fe44 thread T0 #0 0x56282de47976 in main /root/heap-use-after-free.c:4 #1 0x7fabfddb72e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0) #2 0x56282de47819 in _start (/root/a.out+0x819) 0x61400000fe44 is located 4 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0) freed by thread T0 here: #0 0x7fabfea96370 in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc3370) #1 0x56282de47941 in main /root/heap-use-after-free.c:3 previously allocated by thread T0 here: #0 0x7fabfea95d70 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc2d70) #1 0x56282de47931 in main /root/heap-use-after-free.c:2 SUMMARY: AddressSanitizer: heap-use-after-free /root/heap-use-after-free.c:4 in main Shadow bytes around the buggy address: 0x0c287fff9f70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9f90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9fa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fff9fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c287fff9fc0: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd 0x0c287fff9fd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c287fff9fe0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c287fff9ff0: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa 0x0c287fffa000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c287fffa010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==7849==ABORTING
Stack-buffer-overflow
int main(int argc, char **argv) { int stack_array[100]; stack_array[1] = 0; return stack_array[argc + 100]; // BOOM }
gcc -O -g -fsanitize=address test.c ./a.out
报错如下:
================================================================= ==8078==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffe55a7b04 at pc 0x555dec997a0e bp 0x7fffe55a7940 sp 0x7fffe55a7938 READ of size 4 at 0x7fffe55a7b04 thread T0 #0 0x555dec997a0d in main /root/test6.c:4 #1 0x7f903bdab2e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0) #2 0x555dec997819 in _start (/root/a.out+0x819) Address 0x7fffe55a7b04 is located in stack of thread T0 at offset 436 in frame #0 0x555dec99792f in main /root/test6.c:1 This frame has 1 object(s): [32, 432) 'stack_array' <== Memory access at offset 436 overflows this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext (longjmp and C++ exceptions *are* supported) SUMMARY: AddressSanitizer: stack-buffer-overflow /root/test6.c:4 in main Shadow bytes around the buggy address: 0x10007caacf10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10007caacf20: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 0x10007caacf30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10007caacf40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10007caacf50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x10007caacf60:[f4]f4 f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 0x10007caacf70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10007caacf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10007caacf90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10007caacfa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10007caacfb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==8078==ABORTING
Global-buffer-overflow
int global_array[100] = {-1}; int main(int argc, char **argv) { return global_array[argc + 100]; // BOOM }
gcc -O -g -fsanitize=address test.c ./a.out
报错如下:
SUMMARY: AddressSanitizer: global-buffer-overflow /root/test6.c:3 in main Shadow bytes around the buggy address: 0x0ab033158fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033158ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033159000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033159010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033159020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0ab033159030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9 0x0ab033159040: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033159050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033159060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033159070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ab033159080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==8158==ABORTING
-
Buffer Manager
2017-05-06 04:28:33As a part of the team you will be responsible for the Buffer Manager. Data blocks being read by DBMS from the hard drive are stored in the main memory in a fixed number of pre-allocated buffers. Each... -
Protocol Buffer 简介
2018-06-27 11:08:44一、Protocol Buffer 与 XML、JSON 的区别 Protocol Buffer 和 XML、JSON一样都是结构数据序列化的工具,但它们的数据格式有比较大的区别: 首先,Protocol Buffer 序列化之后得到的数据不是可读的字符...一、Protocol Buffer 与 XML、JSON 的区别
Protocol Buffer
和XML
、JSON
一样都是结构数据序列化
的工具,但它们的数据格式有比较大的区别:- 首先,Protocol Buffer 序列化之后得到的数据不是可读的字符串,而是
二进制流
- 其次,XML 和 JSON 格式的数据信息都包含在了序列化之后的数据中,不需要任何其它信息就能还原序列化之后的数据;但使用 Protocol Buffer 需要事先定义数据的格式(
.proto 协议文件
),还原一个序列化之后的数据需要使用到这个定义好的数据格式 - 最后,在传输数据量较大的需求场景下,Protocol Buffer 比 XML、JSON
更小(3到10倍)、更快(20到100倍)、使用 & 维护更简单
;而且 Protocol Buffer 可以跨平台、跨语音使用
- 首先,Protocol Buffer 序列化之后得到的数据不是可读的字符串,而是
二、Protocol Buffer 的作用
- 通过将结构化的数据(拥有多种属性)进行序列化,从而实现(内存与硬盘之间)数据存储和交换的功能
- 序列化: 按照
.proto
协议文件将数据结构或对象
转换成二进制流
的过程 - 反序列化:将在序列化过程中所生成的
二进制流
转换成数据结构或对象
的过程
- 序列化: 按照
三、构建 Protocol Buffer 消息对象模型
1、通过 Protocol Buffer 语法描述需要存储的数据结构
- Protocol Buffer 定义数据格式的文件一般保存在
.proto
文件中,每一个message
代表了一类结构化的数据,message
里面定义了每一个属性的类型和名字
。 - 一个消息对象可以将
其他消息对象类型
用作字段类型
,情况如下:
# 1、包名 package protocobuff_Demo; # 2、option 选项 option java_package = "com.carson.proto"; option java_outer_classname = "Demo"; # 3、消息模型 # 3.1、生成 Person 消息对象(包含多个字段) message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } # 该消息类型定义在 Person 消息类型的内部 # 即 Person 消息类型是 PhoneNumber 消息类型的父消息类型 message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } # 直接使用内部消息类型 repeated PhoneNumber phone = 4; } # 3.2 使用外部消息类型,直接使用了 Person 消息类型作为消息字段 message AddressBook { repeated Person person = 1; }
2、通过 Protocol Buffer 编译器编译 .proto 文件
- 作用:将
.proto
文件 转换成对应平台(python、C++、Java)的代码文件 - 编译指令如下所示:
# 在终端输入下列命令进行编译 protoc -I=$SRC_DIR --xxx_out=$DST_DIR $SRC_DIR/addressbook.proto # 参数说明 # 1. $SRC_DIR:指定需要编译的.proto文件目录 (如没有提供则使用当前目录) # 2. --xxx_out:xxx根据需要生成代码的类型进行设置 """ 对于 Java ,xxx = java ,即 -- java_out 对于 C++ ,xxx = cpp ,即 --cpp_out 对于 Python,xxx = python,即 --python_out """ # 3. $DST_DIR :编译后代码生成的目录 (通常设置与$SRC_DIR相同) # 4. 最后的路径参数:需要编译的.proto 文件的具体路径 # 编译通过后,Protoco Buffer会根据不同平台生成对应的代码文件
四、参考资料
1、这是一份很有诚意的 Protocol Buffer 语法详解
2、手把手教你如何安装 Protocol Buffer -
FIFO buffer 和普通buffer区别
2018-01-23 09:55:18而buffer往往就是一段缓冲的数据区域 2.FIFO的数据是先进先出的,而buffer没有这个限制,可以全局访问 3.buffer往往就是一段累积的存储空间,而fifo有时候还可以帮助系统解决时钟域不同步或者数据宽度不一样的...1.FIFO可以说一块具体的硬件存储设备,也可以说程序在内存中开辟的一段内存区域。而buffer往往就是一段缓冲的数据区域
2.FIFO的数据是先进先出的,而buffer没有这个限制,可以全局访问
3.buffer往往就是一段累积的存储空间,而fifo有时候还可以帮助系统解决时钟域不同步或者数据宽度不一样的情况
-
NIO buffer
2016-05-24 16:31:49Buffer的capacity,position和limit 为了理解Buffer的工作原理,需要熟悉它的三个属性: capacitypositionlimit position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式,... -
IoBuffer
2017-02-07 14:45:01Mina中传输的所有二进制信息都存放在IoBuffer中,IoBuffer是对Java NIO中ByteBuffer的封装(Mina2.0以前版本这个接口也是ByteBuffer),提供了更多操作二进制数据,对象的方法,并且存储空间可以自增长,用起来非常... -
乒乓buffer
2016-12-17 13:57:34module ping_pong_buffer( input clk, input rst_n, //external write interface input[7:0] i_data, input i_data_valid, //ping ram buffer interface output ping_ram_buffer_wclk, output ping_ram_ -
Circular buffer
2014-10-16 11:28:47ringbuffer -
Direct Buffer vs. Heap Buffer
2015-06-09 12:49:482、 区别:Direct Buffer不是分配在堆上的,它不被GC直接管理(但Direct Buffer的JAVA对象是归GC管理的,只要GC回收了它的JAVA对象,操作系统才会释放Direct Buffer所申请的空间),它似乎给人感觉是“内核缓冲区... -
Mysql Buffer Pool
2018-07-15 17:02:51Buffer Pool 简介:Innodb维护了一个缓存区域叫做Buffer Pool,用来缓存数据和索引在内存中。Buffer Pool可以用来加速数据的读写,如果Buffer Pool越大,那么Mysql就越像一个内存数据库,所以了解Buffer Pool的配置... -
buffer定义
2017-12-28 13:20:43 这篇蚊帐转自:... Buffer 类是 java.nio 的构造基础。一个 Buffer 对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里,数据可被存储并在之后用于检索。 -
Stencil buffer
2015-06-24 20:40:46模版缓冲(stencil buffer)或印模缓冲,是在OpenGL三维绘图等计算机图像硬件中常见的除颜色缓冲、像素缓冲、深度缓冲之外另一种数据缓冲。词源模版(stencil)是指一种印刷技术,通常以蜡纸或者钢板印刷文字或图形... -
Buffer对象,常用的Buffer方法;
2017-09-17 15:06:271.创建Buffer的两种方法: var buf = Buffer.alloc(); //返回一个指定大小的被填满的Buffer实例; var buf = Buffer.from() //返回一个新建的包含所提供的字节数组的副本的Buffer; 例:var buf = Buffer.alloc... -
oracle buffer cache中各种状态的buffer描述
2016-11-10 19:43:26...buffer cache中的buffer根据不同的状态可以分为以下4种类型: ...free buffer/unused buffer:buffer cache初始化时或者在执行alter system flush buffer_cache以后的buffer,该buffer中 -
Node.js Buffer模块深入理解Buffer.allocUnsafe与 Buffer.from及其字符串与Buffer之间的转换
2019-01-16 00:59:501) 当数据没有汉字,则完全等价 ...var buf = Buffer.allocUnsafe(data.length); // for(var i = 0; i < data.length; i++){ // buf[i] = data.charCodeAt(i); // } buf.fill(data, 0); console.log(buf... -
理解一下Buffer.from(), Buffer.alloc()
2020-06-18 22:48:17Buffer.from(), Buffer.alloc() 都是创建一个buffer, Buffer.from()从字符串或者数组创建一个buffer, Buffer.alloc()是创建一个指定大小的buffer。 用法示例 // 从字符串创建一个buffer const buffer1 = Buffer.... -
ping pong buffer
2018-08-21 16:37:17Ping pong buffer is a buffer used in a transmission that contains two separate buffers, while one buffer is receiving new transmission information the other buffer is deleting the previous transmissio... -
Nodejs Buffer拼接
2019-09-25 20:21:39本文记录下最近遇到的buffer拼接问题。如果需要知道如何拼接 直接点击第二节 文章目录1 背景2 buffer拼接3 buffer 截取 1 背景 最近有一个需求需要将缩略图和视频文件合并到一起用HTTP POST 发送给服务器,服务器... -
vb2_buffer和v4l2_buffer
2017-11-08 15:38:49videobuf2-core.h中的vb2_buffer,记录了v4l2_buffer ,驱动可以对vb2_buffer的v4l2_buffer进行操控,vb2_buffer是v4l2框架层的代码,而v4l2_buffer也是用户空间的应用程序,可以读取的一个数据结构。videobuf2-core... -
String和Buffer
2019-05-22 15:45:19一、JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一个与String对等的全局构造函数Buffer来提供对二进制数据的操作 // 可以将文件的内容读取到Buffer实例,或者是直接构造Buffer实例。 var ... -
buffer cache
2013-08-23 14:40:28今天看到一ocp关于buffer cache 的题目,在这里记录一下 Which is the correct description of a pinned buffer in the database buffer cache? A.The buffer is currently being accessed. B.The buffer is ... -
NIO之旅Buffer
2020-08-18 22:38:45在上一篇文章中聊了一下Channel,这篇文章聊一下Java NIO中的另一个重要的组件:Buffer。 Buffer的概念 首先我们先看一下Buffer的类图,Buffer在java.nio中被定义为抽象类: Buffer被称为内存缓冲区,本质上也是... -
将Buffer对象结合创建为一个新的Buffer对象
2016-06-29 22:55:511、问题背景 将不同的Buffer对象重新结合为一个新的Buffer对象2、实现源码> A=new Buffer('AA'); > B=new Buffer('BB'); > C=new Buffer('CC'); > D=new Buffer('DD'); > E=new Buffer('EE'); > F=new Buffer... -
mysql的sort buffer和join buffer介绍
2016-11-29 17:23:05摘要:如果应用中,很少出现join语句,则可以不用太在乎join_buffer_size参数的设置大小。如果join语句不是很少的话,个人建议可以适当增大join_buffer_size到1MB左右,如果内存充足可以设置为2MB... 相关... -
-
Java NIO Buffer
2014-11-20 08:48:44Java NIO Buffer -
Kernel的环形Buffer(Ring Buffer)——以Logger Buffer为例
2016-12-19 12:41:01在Android L之前的日志系统是Kernel层实现了若干个环形Buffer实现的。系统各个日志读写操作都是针对这几个RingBuffer来实现的。那就来一窥Kernel是怎么做的。相关源码是位于driver/staging/android/下面的logger.c和... -
图片 Buffer ArrayBuffer 转换
2020-04-16 14:24:44关于ArrayBuffer与Buffer具体查询相关资料 后端转换 Buffer转ArrayBuffer 原生Buffer可以直接通过 res.send() 显示图片 let buffer = Buffer.from('this is buffer data') console.log(buffer) // Buffer ...
-
中文说明worldserver.conf
-
Leetcode 47. Permutations II
-
【数据分析-随到随学】量化交易策略模型
-
KDDCUP99.zip
-
旋转LED电子时钟.zip
-
FPGA 之 SOPC 系列(四)NIOS II 外围设备--标准系统搭建
-
【数据分析-随到随学】数据分析建模和预测
-
微信支付2021系列之扫码支付一学就会java版
-
FPGA 之 SOPC 系列(六)Nios II 程序开发 II
-
Java Web开发之Java语言基础
-
android笔试面试和实战课程
-
课程设计综合最终材料.zip
-
第3章 入门程序、常量、变量
-
【数据分析-随到随学】Python数据获取
-
抢购京东平台的茅台1499
-
超级好用老板键.rar
-
我的Git笔记.pdf
-
易语言开发通达信DLL公式接口
-
开发者日志:nodejs做vue和flask的反向代理,遇到了/js路径匹配失败的问题
-
第1章 Java入门基础及环境搭建【java编程进阶】