引言  - malloc 引述

  C标准中堆上内存入口就只有 malloc, calloc, realloc . 内存回收口是 free. 常见的一种写法是

struct person * per = malloc(sizoef(struct person));
if(NULL == ptr) {
fprintf(stderr, "malloc struct person is error!");
// to do error thing ...
...
} // 处理正常逻辑
... // 回收
free(per);

特别是 if NULL == ptr 那些操作实在让人繁琐. 有点不爽, 构建了一组接口, 尝试一种方式来简便一下.

借鉴思路是 上层语言 new 的套路. 简单粗暴, 失败直接崩溃. 大体思路是

    struct header * ptr = malloc(sz + sizeof(struct header));
// 检查内存分配的结果
if(NULL == ptr) {
fprintf(stderr, "_header_get >%s:%d:%s< alloc error not enough memory start fail!\n", file, line, func);
exit(EXIT_FAILURE);
}

利用 exit 结束分配为NULL情况. 毕竟计算机一级内存不足, 一切运行对于软件层都已经是接近"未定义的边缘了"

参照资料 : 云大大的skynet2 demo  https://github.com/cloudwu/skynet2/tree/master/skynet-src

前言 - 定义接口统一处理

  处理的思路很简单, 主要是 提供一个内存申请的入口像new一样, 返回初始化的内存, 并且内存不足直接崩溃. 首先接口设计如下

scalloc.h 

#ifndef _H_SIMPLEC_SCALLOC
#define _H_SIMPLEC_SCALLOC #include <stdlib.h> // 释放sm_malloc_和sm_realloc_申请的内存, 必须配套使用
void sm_free_(void * ptr, const char * file, int line, const char * func);
// 返回申请的一段干净的内存
void * sm_malloc_(size_t sz, const char * file, int line, const char * func);
// 返回重新申请的内存, 只能和sm_malloc_配套使用
void * sm_realloc_(void * ptr, size_t sz, const char * file, int line, const char * func); /*
* 释放申请的内存
* ptr : 申请的内存
*/
#define sm_free(ptr) sm_free_(ptr, __FILE__, __LINE__, __func__)
/*
* 返回申请的内存, 并且是填充'\0'
* sz : 申请内存的长度
*/
#define sm_malloc(sz) sm_malloc_(sz, __FILE__, __LINE__, __func__)
/*
* 返回申请到num*sz长度内存, 并且是填充'\0'
* num : 申请的数量
* sz : 申请内存的长度
*/
#define sm_calloc(num, sz) sm_malloc_(num*sz, __FILE__, __LINE__, __func__)
/*
* 返回重新申请的内存
* ptr : 申请的内存
* sz : 申请内存的长度
*/
#define sm_realloc(ptr, sz) sm_realloc_(ptr, sz, __FILE__, __LINE__, __func__) // 定义全局内存使用宏, 替换原有的malloc系列函数
#ifndef _SIMPLEC_SCALLOC_CLOSE
# define free sm_free
# define malloc sm_malloc
# define calloc sm_calloc
#   define realloc      sm_realloc
#endif #endif // !_H_SIMPLEC_SCALLOC

上面 sm_malloc sm_calloc sm_realloc sm_free 宏相比原先的四个函数, 多了几个编译宏参数, 方便以后查找问题.

_SIMPLEC_SCALLOC_CLOSE 头文件表示 当前是否替代老的 内存相关操作的入口.
这里扯一点, calloc 感觉是设计的失败.
#include <stdlib.h>

void * calloc(size_t nmemb, size_t size);

calloc() allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory.  The memory is set to zero.

上面描述 相当于 calloc(nmemb, size) <=> malloc(nmemb*size) ; memset(ptr, 0, nmemb*size);  感觉好傻.

我么看看源码 在malloc.c 文件中实现 .

void *
__libc_calloc (size_t n, size_t elem_size)
{
mstate av;
mchunkptr oldtop, p;
INTERNAL_SIZE_T bytes, sz, csz, oldtopsize;
void *mem;
unsigned long clearsize;
unsigned long nclears;
INTERNAL_SIZE_T *d; /* size_t is unsigned so the behavior on overflow is defined. */
bytes = n * elem_size;
#define HALF_INTERNAL_SIZE_T \
(((INTERNAL_SIZE_T) ) << ( * sizeof (INTERNAL_SIZE_T) / ))
if (__builtin_expect ((n | elem_size) >= HALF_INTERNAL_SIZE_T, ))
{
if (elem_size != && bytes / elem_size != n)
{
__set_errno (ENOMEM);
return ;
}
} void *(*hook) (size_t, const void *) =
atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, ))
{
sz = bytes;
mem = (*hook)(sz, RETURN_ADDRESS ());
if (mem == )
return ; return memset (mem, , sz);
} sz = bytes; arena_get (av, sz);
if (av)
{
/* Check if we hand out the top chunk, in which case there may be no
need to clear. */
#if MORECORE_CLEARS
oldtop = top (av);
oldtopsize = chunksize (top (av));
# if MORECORE_CLEARS <
/* Only newly allocated memory is guaranteed to be cleared. */
if (av == &main_arena &&
oldtopsize < mp_.sbrk_base + av->max_system_mem - (char *) oldtop)
oldtopsize = (mp_.sbrk_base + av->max_system_mem - (char *) oldtop);
# endif
if (av != &main_arena)
{
heap_info *heap = heap_for_ptr (oldtop);
if (oldtopsize < (char *) heap + heap->mprotect_size - (char *) oldtop)
oldtopsize = (char *) heap + heap->mprotect_size - (char *) oldtop;
}
#endif
}
else
{
/* No usable arenas. */
oldtop = ;
oldtopsize = ;
}
mem = _int_malloc (av, sz); assert (!mem || chunk_is_mmapped (mem2chunk (mem)) ||
av == arena_for_chunk (mem2chunk (mem))); if (mem == && av != NULL)
{
LIBC_PROBE (memory_calloc_retry, , sz);
av = arena_get_retry (av, sz);
mem = _int_malloc (av, sz);
} if (av != NULL)
(void) mutex_unlock (&av->mutex); /* Allocation failed even after a retry. */
if (mem == )
return ; p = mem2chunk (mem); /* Two optional cases in which clearing not necessary */
if (chunk_is_mmapped (p))
{
if (__builtin_expect (perturb_byte, ))
return memset (mem, , sz); return mem;
} csz = chunksize (p); #if MORECORE_CLEARS
if (perturb_byte == && (p == oldtop && csz > oldtopsize))
{
/* clear only the bytes from non-freshly-sbrked memory */
csz = oldtopsize;
}
#endif /* Unroll clear of <= 36 bytes (72 if 8byte sizes). We know that
contents have an odd number of INTERNAL_SIZE_T-sized words;
minimally 3. */
d = (INTERNAL_SIZE_T *) mem;
clearsize = csz - SIZE_SZ;
nclears = clearsize / sizeof (INTERNAL_SIZE_T);
assert (nclears >= ); if (nclears > )
return memset (d, , clearsize); else
{
*(d + ) = ;
*(d + ) = ;
*(d + ) = ;
if (nclears > )
{
*(d + ) = ;
*(d + ) = ;
if (nclears > )
{
*(d + ) = ;
*(d + ) = ;
if (nclears > )
{
*(d + ) = ;
*(d + ) = ;
}
}
}
} return mem;
}

比较复杂, 从中就摘录下面 几行帮助理解

 ...

  /* size_t is unsigned so the behavior on overflow is defined.  */
bytes = n * elem_size; ... return memset (mem, , sz);
... if (av != NULL)
(void) mutex_unlock (&av->mutex); ...

实现起来很复杂, 主要围绕性能考虑,  重新套了一份内存申请的思路. 上面摘录的三点, 能够表明, 从功能上malloc 可以替代 calloc.

最后表明 glibc(gcc) 源码上是线程安全的.后面会分析上面接口的具体实现, 并测试个demo.

正文 - 开始实现, 运行demo

  首先看具体实现, scalloc.c 

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 标识枚举
typedef enum {
HF_Alloc,
HF_Free
} header_e; // 每次申请内存的[16-24]字节额外消耗, 用于记录内存申请情况
struct header {
header_e flag; // 当前内存使用的标识
int line; // 申请的文件行
const char * file; // 申请的文件名
const char * func; // 申请的函数名
}; // 内部使用的malloc, 返回内存会用'\0'初始化
void *
sm_malloc_(size_t sz, const char * file, int line, const char * func) {
struct header * ptr = malloc(sz + sizeof(struct header));
// 检查内存分配的结果
if(NULL == ptr) {
fprintf(stderr, "_header_get >%s:%d:%s< alloc error not enough memory start fail!\n", file, line, func);
exit(EXIT_FAILURE);
} ptr->flag = HF_Alloc;
ptr->line = line;
ptr->file = file;
ptr->func = func;
memset(++ptr, , sz);
return ptr;
} // 得到申请内存的开头部分, 并检查
static struct header * _header_get(void * ptr, const char * file, int line, const char * func) {
struct header * node = (struct header *)ptr - ;
// 正常情况直接返回
if(HF_Alloc != node->flag) {
// 异常情况, 内存多次释放, 和内存无效释放
fprintf(stderr, "_header_get free invalid memony flag %d by >%s:%d:%s<\n", node->flag, file, line, func);
exit(EXIT_FAILURE);
}
return node;
} // 内部使用的realloc
void *
sm_realloc_(void * ptr, size_t sz, const char * file, int line, const char * func) {
struct header * node , * buf;
if(NULL == ptr)
return sm_malloc_(sz, file, line, func); // 合理内存分割
node = _header_get(ptr, file, line, func);
node->flag = HF_Free;
// 构造返回内存信息
buf = realloc(node, sz + sizeof(struct header));
buf->flag = HF_Alloc;
buf->line = line;
buf->file = file;
buf->func = func; return buf + ;
} // 内部使用的free, 每次释放都会打印日志信息
void
sm_free_(void * ptr, const char * file, int line, const char * func) {
if(NULL != ptr) {
// 得到内存地址, 并且标识一下, 开始释放
struct header * node = _header_get(ptr, file, line, func);
node->flag = HF_Free;
free(node);
}
}

这里主要围绕 1 插入申请内存头

// 每次申请内存的[16-24]字节额外消耗, 用于记录内存申请情况
struct header {
header_e flag; // 当前内存使用的标识
int line; // 申请的文件行
const char * file; // 申请的文件名
const char * func; // 申请的函数名
};

围绕2 在 malloc 时候 和 _header_get 得到头检查 时候, 直接exit.

思路很清晰基础, 假如这代码跑在64位机器上,  线上一个服务器, 运行时创建100000万个malloc对象 .

100000 * (4 + 4 + 8 +8)B / 1024 / 1024 = 2.288818359375 MB 的内存损耗. 还有一次取内存检查的性能损耗.

这些是可以接受的, 特殊时候可以通过打印的信息, 判断出内存调用出错的位置.

扯一点 这里用了枚举 方便和宏区分

// 标识枚举
typedef enum {
HF_Alloc,
HF_Free
} header_e;

其实宏和枚举在C中基本一样, 只能人为的添加特殊规范, 约定二者区别. 宏用的太多, 复杂度会越来越大. 双刃剑.

下面我们测试一下 演示demo main.c

#include <stdio.h>
#include "scalloc.h" /*
* 测试内存管理, 得到内存注册信息
*/
int main(int argc, char * argv[]) {
int * piyo = malloc();
free(piyo); puts("start testing..."); // 简单测试一下
free(piyo); getchar();
return ;
}

演示结果

到这里 基本思路都已经介绍完毕了. 主要核心就是偷梁换柱.

后记 - ~○~

  错误是难免的, 有问题再打补丁修复. 欢迎将这思路用在自己的项目构建中.

C基础 内存统一入口的更多相关文章

  1. MVC+Ef项目(3) 抽象数据库访问层的统一入口;EF上下文线程内唯一

    抽象一个数据库访问层的统一入口(类似于EF的上下文,拿到上下文,就可以拿到所有的表).实际这个入口就是一个类,类里面有所有的仓储对应的属性.这样,只要拿到这个类的实例,就可以点出所有的仓储,我们在 R ...

  2. 【OC语法快览】四、基础内存管理

    Basic Memory Management                                                           基础内存管理 If you're w ...

  3. OC基础 内存管理

    OC基础  内存管理 我们所了解的c语言内存管理,如下: (1)c语言的内存分配:char *p = (char*)malloc(100*sizeof(char)); (2)c语言的内存释放:free ...

  4. IdentityServer4与ocelot实现认证与客户端统一入口

    关于IdentityServer4与ocelot博客园里已经有很多介绍我这里就不再重复了. ocelot与IdentityServer4组合认证博客园里也有很多,但大多使用ocelot内置的认证,而且 ...

  5. php框架之自动加载与统一入口

    现在PHP有很多的框架,基本都是以MVC为基础进行设计的.其实很多框架(像thinkphp,zf,symfont等)都有两个特性,自动加载类文件和统一入口.这里就简单实现以上两个特性. 假设PHP使用 ...

  6. CUDA 内存统一分析

    CUDA 内存统一分析 关于CUDA 编程的基本知识,如何编写一个简单的程序,在内存中分配两个可供 GPU 访问的数字数组,然后将它们加在 GPU 上. 本文介绍内存统一,这使得分配和访问系统中任何处 ...

  7. fastdfs集群版搭建(一)- storage集群搭建与统一入口访问

    前言 接着上篇博客:详细的最新版fastdfs单机版搭建,今天来讲讲fastdfs的集群搭建,限于篇幅,今天先搭建stoarge集群,并实现统一的http访问方式: 没看我上篇博客的小伙伴,最好先去瞅 ...

  8. oracle基础——内存管理、优化

    内存图解: 自动管理:11g:AMM   10g:ASMM SGA(system global area):由所有服务进程和后台进程共享 PGA(program global area): 由每个服务 ...

  9. 统一入口&DB代理思想

    参考 https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650764278&idx=2&sn=b7801fb0780 ...

随机推荐

  1. poj2409:Let it Bead(置换群 polya定理)

    题目大意:长度为n的项链,要染m种颜色,可以通过旋转或翻转到达的状态视为同一种,问有多少种染色方案. 学了一波polya定理,发现很好理解啊,其实就是burnside定理的扩展. burnside定理 ...

  2. Matrix-Tree定理题表

    矩阵树这个东西……并不懂什么基尔霍夫矩阵……背了一下结论……(顺便用这个东西加强了一下矩阵)(打板子的时候还是该取负取负,因为不取负才有可能是负数,最后答案一定是正数???(ryf说一定是这样))bz ...

  3. ioctl函数用法小记

    By francis_hao    Aug 27,2017   UNPV1对ioctl有算是比较详细的介绍,但是,这些request和后面的数据类型是从哪里来的,以及参数具体该如何使用呢?本文尝试在不 ...

  4. sql中按in中的ID进行排序输出

    builder.OrderBy("charindex(','+convert(varchar,ID)+',',',"+chufenOrder+"') ");

  5. ubuntu14.04安装GTX 1080 ti遇到黑屏问题

    实验室给我配置了一个1080ti的卡,那个激动,windows下1000+的FPS,跑分40W,无敌,言归正传,ubuntu14.04下配nvidia 1080的驱动还是出现了很多问题,差点就要重装系 ...

  6. 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性.重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同.参数个数不同或者二者都不同)则视 ...

  7. Ultra-QuickSort POJ - 2299 树状数组求逆序对

    In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a seque ...

  8. eclipse的最新版本luna的中建立svn和maven

    http://blog.csdn.net/notillusion/article/details/40950185

  9. UVALive - 5713 最小生成树

    题意: 秦始皇修路,已知n个城市的坐标以及该城市的人口数,修路的费用是两个城市之间的欧几里得距离,其中可以有一条路不用花费代价但是要求这条路连接的两个城市的人口之和A/B尽量大,其中B是修路的总费用. ...

  10. hive executeTask被interrupt处理

    异常信息如下: java.io.IOException: Failed on local exception: java.nio.channels.ClosedByInterruptException ...