OpenSSL学习笔记——内存分配
来源:互联网 发布:radium mac 编辑:程序博客网 时间:2024/06/11 03:10
环境:OpenSSL 0.9.8l,Fedora 12
今天学习的是《OpenSSL编程》第五章 内存分配。这一章主要讲了OpenSSL在内存分配时,是如何做的。在C语言中,一般使用的是malloc和free来申请和释放内存的。当然,在OpenSSL中,我们同样也可以使用前面两个函数的使用内存。不过,OpenSSL也为我们提供了一些更强大的内存管理函数。这些函数主要强大在,可以方便地检查内存的泄露点,可以知道某一次对内存的申请造成了多大的内存泄露,是哪个文件,哪行语句造成的。如果在程序中完全使用OpenSSL为我们提供的这些函数,我们就可以方便地找到内存泄露点。
在开始之前,得先介绍几个变量:
LHASH *mh = NULL;//这个哈希表是用来存内存分配请求的,每请求一次内存,就给里面添一项,把内存释放时,就把相应的那一项去掉。key是MEM::addr。
LHASH *amih = NULL;//这个哈希表是用来存APP_INFO的。key是APP_INFO::thread。
下面,是在OpenSSL在内存方面使用的数据结构:
typedef struct app_mem_info_st
{
unsigned long thread; //申请内存的线程号
const char *file; //申请内存的文件名
int line; //申请内存的行号
const char *info;
struct app_mem_info_st *next; /* tail of thread's stack */
int references; //被引用的次数
} APP_INFO;
这个结构体是用来记录用户申请的内存信息的。
typedef struct mem_st
{
void *addr; //分配的内存的地址
int num; //分配内存的大小
const char *file; //申请内存的文件名
int line; //申请内存的行号
unsigned long thread; //申请内存的线程号
unsigned long order; //申请有内存的次数
time_t time; //申请内存的时间
APP_INFO *app_info; //相关信息
} MEM;
这个结构是用来描述内存块的,用户每使用OPENSSL_malloc成功申请一次内存,就会产生一个这样的结构体,并把这个结构体存入哈希表mh中。
下面,我想从两个函数(OPENSSL_malloc和OPENSSL_free)来说明OpenSSL在内存管理方面是怎么做的,其实OpenSSL只是给我们常用的malloc和free加了一个外壳,这使得用户在申请内存时,它可以把用户的信息记录下来并存入一个哈希表中。
函数OPENSSL_maloc
函数OPENSSL_malloc是OpenSSL中在申请内存时,用来代替malloc的。其实,OPENSSL_malloc是一个宏定义,定义在文件/cypto/crypto.h中:
#define OPENSSL_malloc(num) CRYPTO_malloc((int)num,__FILE__,__LINE__)
从这里,很明显可以看出,当我们调用OPENSSL_malloc时,OpenSSL可以通过宏__FILE__和__LINE__很方便地把文件名和行号录下来。那么,函数CRYPTO_malloc又是什么呢?它是一个函数,定义在文件/crypto/mem.c中:
void *CRYPTO_malloc(int num, const char *file, int line)
{
void *ret = NULL;
extern unsigned char cleanse_ctr; //值为0
if (num <= 0) return NULL;
allow_customize = 0;
if (malloc_debug_func != NULL)
{
allow_customize_debug = 0;
malloc_debug_func(NULL, num, file, line, 0);
}
ret = malloc_ex_func(num,file,line); //其实内存就是在这一句被分配的
#ifdef LEVITTE_DEBUG_MEM
fprintf(stderr, "LEVITTE_DEBUG_MEM: > 0x%p (%d)/n", ret, num);
#endif
if (malloc_debug_func != NULL)
malloc_debug_func(ret, num, file, line, 1);
/* Create a dependency on the value of 'cleanse_ctr' so our memory
* sanitisation function can't be optimised out. NB: We only do
* this for >2Kb so the overhead doesn't bother us. */
if(ret && (num > 2048))
((unsigned char *)ret)[0] = cleanse_ctr;
return ret;
}
从上的函数,我们可以看出内存分配是通过malloc_ex_func完成的,在这个函数的上下方,分别调用了两次malloc_debug_func。
我们先说malloc_ex_func,它是一个函数指针,它的定义在文件/crypto/mem.c中:
static void *(*malloc_func)(size_t) = malloc;
static void *default_malloc_ex(size_t num, const char *file, int line)
{ return malloc_func(num); }
static void *(*malloc_ex_func)(size_t, const char *file, int line)
= default_malloc_ex;
其实,函数malloc_ex_func就是malloc。
我们再来看函数,malloc_debug_func,它也是一个函数指针,它的定义在文件/crypto/mem.c中:
static void (*malloc_debug_func)(void *,int,const char *,int,int) = NULL;
可以看到,这个函数指针的初值为0,也就是说,如果用户在编程时没有改变它的值,函数CRYPTO_malloc就相当于是函数malloc,此时,OpenSSL不提供内存泄露检查的功能。要使用OpenSSL的内存泄露检查功能时,得在程序开始时调用函数CRYPTO_malloc_debug_init。这个函数定义在文件/crypto/mem_dbg.c中:
void CRYPTO_malloc_debug_init(void)
{
CRYPTO_set_mem_debug_functions(
CRYPTO_dbg_malloc,
CRYPTO_dbg_realloc,
CRYPTO_dbg_free,
CRYPTO_dbg_set_options,
CRYPTO_dbg_get_options);
CRYPTO_set_mem_info_functions(
CRYPTO_dbg_push_info,
CRYPTO_dbg_pop_info,
CRYPTO_dbg_remove_all_info);
}
函数CRYPTO_set_mem_debug_functions定义在文件/crypto/mem.c中:
int CRYPTO_set_mem_debug_functions(void (*m)(void *,int,const char *,int,int),
void (*r)(void *,void *,int,const char *,int,int),
void (*f)(void *,int),
void (*so)(long),
long (*go)(void))
{
if (!allow_customize_debug)
return 0;
malloc_debug_func=m;
realloc_debug_func=r;
free_debug_func=f;
set_debug_options_func=so;
get_debug_options_func=go;
return 1;
}
从上面两个函数可以看出,当用户调用函数CRYPTO_malloc_debug_init以后,这些相应的函数指针就被赋上的相应的值。下面,我们需要看一下函数CRYPTO_dbg_malloc的实现,这个函数的实现在文件/crypto/mem_dbg.c中(省略了一些不重要的代码):
static unsigned long break_order_num=0;
void CRYPTO_dbg_malloc(void *addr, int num, const char *file, int line, int before_p)
{
MEM *m,*mm;
APP_INFO tmp,*amim;
switch(before_p & 127)
{
case 0:
break;
case 1:
if (addr == NULL)
break;
if (is_MemCheck_on())
{
...
//把用户申请内存的信息存入结构体m中
m->addr=addr;
m->file=file;
m->line=line;
m->num=num;
if (options & V_CRYPTO_MDEBUG_THREAD)
m->thread=CRYPTO_thread_id();
else
m->thread=0;
if (order == break_order_num)
{
/* BREAK HERE */
m->order=order;
}
m->order=order++;
...
if (options & V_CRYPTO_MDEBUG_TIME)
m->time=time(NULL);
else
m->time=0;
tmp.thread=CRYPTO_thread_id();
m->app_info=NULL;
if (amih != NULL
&& (amim=(APP_INFO *)lh_retrieve(amih,(char *)&tmp)) != NULL)
{//在哈希表amih中查找,是否存在与当前线程相关的APP_INFO
m->app_info = amim;
amim->references++; //引用数加1
}
if ((mm=(MEM *)lh_insert(mh,(char *)m)) != NULL)
{//把结构体m插入到哈希表mh中去。如果返回值不是NULL,则说明mh中原来就有一个MEM的addr与传进来的addr相同。
/* Not good, but don't sweat it */
if (mm->app_info != NULL)
{
mm->app_info->references--;
}
OPENSSL_free(mm);
}
err:
MemCheck_on(); /* release MALLOC2 lock
* if num_disabled drops to 0 */
}
break;
}
return;
}
看完上面的代码,应该不难理解函数OPENSSL_malloc的执行过程了,通过函数malloc_ex_func(也就是malloc)分配内存,通过函数CRYPTO_malloc_dbg把用户申请内存的信息存入哈希表mh中。
函数OPENSSL_free
函数OPENSSL_free是OpenSSL在释放内存时,用来代替free的。OPENSSL_free是一个宏定义,定义在文件/crypto/crypto.h中:
#define OPENSSL_free(addr) CRYPTO_free(addr)
我们再来看一下函数CRYPTO_free,实现在文件/crypto/mem.c中:
void CRYPTO_free(void *str)
{
if (free_debug_func != NULL)
free_debug_func(str, 0);
#ifdef LEVITTE_DEBUG_MEM
fprintf(stderr, "LEVITTE_DEBUG_MEM: < 0x%p/n", str);
#endif
free_func(str);
if (free_debug_func != NULL)
free_debug_func(NULL, 1);
}
这里,我们可以看到函数CRYPTO_free和函数CRYPTO_malloc的结构几乎是一样的。变量free_debug_func和变量free_func是两个函数指针,定义在文件/crypto/mem.c中:
static void (*free_func)(void *) = free;
static void (*free_debug_func)(void *,int) = NULL;
从函数CRYPTO_malloc_debug_init和函数CRYPTO_set_mem_debug_functions中可以看出,变量free_debug_func的值应该为CRYPTO_dbg_free,这个函数的定义在文件/crypto/mem_dbg.c中:
void CRYPTO_dbg_free(void *addr, int before_p)
{
MEM m,*mp;
switch(before_p)
{
case 0:
if (addr == NULL)
break;
if (is_MemCheck_on() && (mh != NULL))
{
MemCheck_off(); /* make sure we hold MALLOC2 lock */
m.addr=addr;
mp=(MEM *)lh_delete(mh,(char *)&m); //在哈希表mh中删除分配内存首地址为addr的MEM
if (mp != NULL)
{//如果不为NULL,则说明上一句lh_delete删除成功,下面就应该删除与之对应的APP_INFO
#ifdef LEVITTE_DEBUG_MEM
fprintf(stderr, "LEVITTE_DEBUG_MEM: [%5d] - 0x%p (%d)/n",
mp->order, mp->addr, mp->num);
#endif
if (mp->app_info != NULL)
app_info_free(mp->app_info);
OPENSSL_free(mp);
}
MemCheck_on(); /* release MALLOC2 lock
* if num_disabled drops to 0 */
}
break;
case 1:
break;
}
}
所以,当我们调用OPENSSL_malloc申请内存,而没有调用OPENSSL_free释放内存时,我们只需要遍历哈希表mh,就可以知道哪些内存还没有被释放。
用于输出内存泄露信息的函数,我们可以使用CRYPTO_mem_leaks,这个函数把内存泄露的信息输出到一个BIO。
下面,我们来看一下OpenSSL一些常用的内存管理的函数:
1. #define OPENSSL_malloc(num) CRYPTO_malloc((int)num,__FILE__,__LINE__)
2. #define OPENSSL_strdup(str) CRYPTO_strdup((str),__FILE__,__LINE__)
char *CRYPTO_strdup(const char *str, const char *file, int line)
{
char *ret = CRYPTO_malloc(strlen(str)+1, file, line);
strcpy(ret, str);
return ret;
}
3. #define OPENSSL_realloc(addr,num) CRYPTO_realloc((char *)addr,(int)num,__FILE__,__LINE__)
此函数用法与realoc相同。
4. #define OPENSSL_free(addr) CRYPTO_free(addr)
5. void CRYPTO_mem_leaks_fp(FILE *fp);
此函数的作用是把内存泄露信息输出到文件fp,这个函数是通过CRYPTO_mem_leaks实现的。
6. void CRYPTO_mem_leaks(struct bio_st *bio);
此函数的作用是把内存泄露信息输出到bio。
7. void CRYPTO_malloc_debug_init(void);
- OpenSSL学习笔记——内存分配
- 学习笔记—java中的内存分配
- openssl内存分配
- C++ Primer学习笔记——$20 内存分配
- C++ Primer学习笔记——$20 内存分配
- linux设备驱动程序学习笔记——分配内存
- C学习笔记——malloc内存分配
- C语言学习笔记——动态内存分配
- C++学习笔记(一)——内存分配问题
- C++ 内存分配学习笔记
- JAVA 学习笔记-内存分配
- JAVA学习笔记--内存分配
- C++笔记——动态内存分配
- OpenSSL学习笔记——堆栈
- OpenSSL学习笔记——哈希表
- OpenSSL学习笔记——CRL
- 学习之路——内存分配
- openssl内存分配,查看内存泄露
- Javascript操作select控件大全
- OpenSSL学习笔记——哈希表
- 综述:航空制造企业精益制造与制造执行系统
- Quality Center 学习笔记(1)
- Scripts to manage Local Users—windows用户管理脚本
- OpenSSL学习笔记——内存分配
- C# 图像合成
- tr 命令 转换字符
- 配置Apache和OpenSSL
- 类初始化器 (Initializers)
- 配置LAMP服务器
- magento -- 在Magento中使用自己写的SQL语句
- IE 兼容问题的解决办法之一
- OpenSSL学习笔记——CRL