使用mtrace监控堆内存的分配与释放
应用程序中经常会使用malloc来分配堆内存,mtrace是glibc中提供的用于追踪堆内存分配与释放的功能。
头文件mcheck.h包含了mtrace()和muntrace()的声明。一旦使用了mecheck.h中声明的函数,编译器就会就将程序所使用的malloc()、realloc()、free()、memalign()函数,都会指向mtrace定义的malloc()、realloc()、free()、memalign()函数。调用mtrace()以后,这些函数会尝试将内存分配和释放的行为记录到MALLOC_TRACE环境变量所指定的文件中,并且调用被替换掉的分配、释放函数。如果MALLOC_TRACE环境变量没有被指定,或者指向的不是有效文件,那么mtrace()就不会有任何效果。调用muntrace()以后,也会停止记录内存分配、释放活动。
#include <mcheck.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include "../common.h" void __mtracer_on() __attribute__((constructor)); void __mtracer_off() __attribute__((destructor)); void __mtracer_on() { char *p = getenv("MALLOC_TRACE"); /* * Format of tracebuf is: * Filename prefix of maximum length of MAX_STR_LEN; * A DOT "."; * Process index with maximum length of MAX_IDX_LEN; */ char tracebuf[MAX_STR_LEN + sizeof(char) + MAX_IDX_LEN + 1]; if (!p) { p = "mtrace"; } sprintf(tracebuf, "%." STRINGIFY(MAX_STR_LEN) "s.%." STRINGIFY(MAX_IDX_LEN) "d", p, getpid()); setenv("MALLOC_TRACE", tracebuf, 1); atexit(&__mtracer_off); mtrace(); } void __mtracer_off() { muntrace(); }
#ifndef __COMMON_H__ #define __COMMON_H__ #define MAX_STR_LEN 31 #define MAX_IDX_LEN 5 /* * This is a technique to convert a number defined by macro into literal * string. * * For example: * STRINGIFY(MAX_STR_LEN) * turns into * "31" */ #define __STRINGIFY(x) #x #define STRINGIFY(x) __STRINGIFY(x) #endif /* __COMMON_H__ */
用gcc libmtrace.c -fPIC -shared -o libmtrace.so进行编译,产生libmtrace.so文件。
LD_PRELOAD=./libmtrace.so /bin/echo 42
用上面的命令调用echo程序,但是libmtrace会在echo的主函数开始运行之前,就调用mtrace()开始记录内存分配、释放活动。
然后可以使用一个叫做mtrace的Perl脚本小工具,分析产生的日志文件,找到所有没有释放的内存分配。
如果程序包含了编译信息,那么mtrace得到的结果就更详细,可以包含内存泄漏发生的源文件及行号信息。
随机数种子的选择
随机数生成器的质量七绝于其产生的序列是否在任意输入下拟合均匀分布,相对而言,选择一个优秀的随机数生成器不是件特别困难的事情,然而选择优质的随机数种子却有许多需要注意的地方。在只需要产生拟合均匀分布的随机序列时,选取随机数种子没有特别的要求。然而随机数生成器往往会被用于产生密钥的哈希算法,此时对于种子的选取就有特定的要求。
有一些反面教材可供参考:
1995年9月时,当时流行的Netscape浏览器使用当前时间、当前Netscape的进程号和父进程号产生随机数种子。当时加州大学伯克利分校的研究员就实现了暴力破解Netscape的加密信息。
另一个例子是Kerberos协议的v4版本,它将当前时间与一些其他信息异或起来产生随机数种子。这个异或位运算直接将其他有效的信息屏蔽了,只留下20位容易预测的值,将密钥的空间从72万亿左右直接减少到1百万多一点,几秒钟时间就可以被暴力破解。
在选择随机数种子的时候容易犯下面这些错误:
- 从一个很小的空间里选取种子:如果你选择的随机数种子只有8位长度,那么无论随机算法怎样,都最多只有256种可能的随机数序列。
- 仅将当前时间作为随机数种子:尽管UNIX API返回精确到毫秒级的时间,但是大多数操作系统处理时间的精度都不到1/60秒,那么,即使在1个小时是邻域内,也只有216000种可能的取值,对于暴力破解来说太容易了。
- 自己泄漏随机数种子:如果种子中使用了当前时间作为产生随机数种子的依据之一,那么就不要以任何方式泄露这个信息,例如不要在未加密的消息头部包含被作为随机数种子产生依据的时间信息。
- 使用网卡或硬件序列号:这种信息往往都是结构化的,而非散列的,结构化的信息被穷举的难度大大降低了。
对于选择安全的随机数种子,有一些比较好的方案:
- 使用真正随机的数据源,比如一些噪点,装配相应的传感器,分析传入的信号的噪声,各种自然界的信号都是可以的。当然设备提供的现成的模拟信号也是可以的,例如/dev/audio在没有插入麦克风时提供的随机噪音,cat /dev/audio | compress,压缩一下可以进一步模糊值的实际含义。
- 获取系统底层的数据,也就是只有本机进程才有权限获取到的,不容易猜测到相近值,无法预先缩小取值范围的数据。比如系统当前使用的内存分页情况、CPU的温度、到某点的网络延迟的情况之类的。
- 让用户输入一串字符,分析用户按键的间隔时间,作为产生随机数种子的依据。千万不要只是取一个平均什么的,从统计的规律来看,按键的间隔时间一般也是在一个比较小的区间内的,要做取模、相乘等运算,产生不能预知可能分布情况的无意义的值。当然,要是结合鼠标移动的信息,就更好了。
CRUSH:受控的、可扩展的数据副本分散存储方案(Part 3)
CRUSH:受控的、可扩展的数据副本分散存储方案
3.2 副本位置策略
![](/user_files/deltamaster/Image/公式:向量i.gif)
![](/user_files/deltamaster/Image/公式:向量i.gif)
![](/user_files/deltamaster/Image/公式:向量i.gif)
![](/user_files/deltamaster/Image/公式:向量i.gif)
![](/user_files/deltamaster/Image/公式:n×向量i的模.gif)
![](/user_files/deltamaster/Image/公式:向量i.gif)
![图1:某四级集群映射层次结构的部分视图,包含row、cabinet、shelf和disk。粗体字标出的表示在[表1]中描述的Placement Rules和假想的映射关系中,被每次select操作选中的项。](/user_files/deltamaster/Image/图1.png)
动作序列 |
结果![]() |
---|---|
take(root) | root |
select(1,row) | row2 |
select(3,cabinet) | cab21 cab23 cab24 |
select(1,disk) | disk2107 disk2313 disk 2437 |
emit |
[算法1:CRUSH确定对象x的存放位置]
3.2.1 冲突、故障和过载
3.2.2 副本排序
![](/user_files/deltamaster/Image/公式:向量R.gif)
![](/user_files/deltamaster/Image/公式:向量R.gif)
![](http://deltamaster.is-programmer.com/user_files/deltamaster/Image/%E5%85%AC%E5%BC%8F%EF%BC%9Ar'=r%20frn.gif)
![](/user_files/deltamaster/Image/公式:fr.gif)
![](/user_files/deltamaster/Image/图2.png)
![](/user_files/deltamaster/Image/公式:向量R.gif)
![](/user_files/deltamaster/Image/公式:fr.gif)
![](http://deltamaster.is-programmer.com/user_files/deltamaster/Image/%E5%85%AC%E5%BC%8F%EF%BC%9Ar'=r%20frn.gif)
3.3 映射变更与数据移动
![](/user_files/deltamaster/Image/公式:wfailedofW.gif)
![](/user_files/deltamaster/Image/公式:deltawofW.gif)
![](/user_files/deltamaster/Image/公式:deltawofW.gif)
![](/user_files/deltamaster/Image/公式:deltaw.gif)
![](/user_files/deltamaster/Image/公式:hdeltawofW.gif)
![](/user_files/deltamaster/Image/公式:deltaw.gif)
![](/user_files/deltamaster/Image/图3.png)
3.4 桶的类型
项目名 | Uniform | List | Tree | Straw |
速度 | O(1) | O(n) | O(log n) | O(n) |
添加项 | 差 | 最优 | 好 | 最优 |
移除项 | 差 | 差 | 好 | 最优 |
3.4.1 Uniform Bucket
![](/user_files/deltamaster/Image/公式:rltem.gif)
![](/user_files/deltamaster/Image/公式:rgtm.gif)
3.4.2 List Bucket
3.4.3 Tree Bucket
![](/user_files/deltamaster/Image/图4.png)
3.4.4 Straw Bucket
![](/user_files/deltamaster/Image/fwi.gif)
![](/user_files/deltamaster/Image/crxmaxfwihashxri.gif)
![](/user_files/deltamaster/Image/fwi.gif)
CRUSH:受控的、可扩展的数据副本分散存储方案(Part 2)
CRUSH:受控的、可扩展的数据副本分散存储方案
2 相关工作
3 CRUSH算法
3.1 层次化Cluster Map
CRUSH:受控的、可扩展的数据副本分散存储方案(Part 1)
CRUSH:受控的、可扩展的数据副本分散存储方案
摘要
1 简介
在C++中使用成员函数指针
函数指针的语法相对比较复杂,成员函数指针则在此基础上进一步引入了类作用域限定。让我们来了解一下成员函数指针的写法。
首先回顾一下函数指针的使用方法。有这样一个函数:
double func(double x) { return 3.14 * x; }
我们这样声明函数指针,并且进行调用:
double (*pfunc)(double x); pfunc = &func; cout << (*pfunc)(1.1) << endl;
指针的声明包括返回类型、指针名和函数的参数表,对于一个强类型的编译型语言,这些要素都必不可少。由于运算符优先级的规定,还要特别注意在适当的位置加上括号。
对于成员函数指针,就是增加了一个作用域限定。
首先有一个类A,包含几个类型相同的函数。
class A { public: void f1(int x) { cout << "f1:" << x << endl; } void f2(int x) { cout << "f2:" << x << endl; } void f3(int x) { cout << "f3:" << x << endl; } };
然后类B的对象会调用类A的对象的成员函数,然而具体调用哪一个还不知道,我们通过指针的方式来实现,在运行时将指针指向一个类型兼容的成员函数。
class B { public: void (A::*pf) (int x); //注意作用域运算符和*的次序,括号不能省略 void callF(int x) { A a; (a.*pf)(x); } };
我们在外部实例化B的对象,然后为B的成员函数指针赋值,确定将要调用的是哪一个函数。
cout << "Hello world!" << endl; B b; b.pf = &A::f2; b.callF(5);