obs-studio源码分析 之 内存管理

obs-studio源码中关于内存管理的代码在bmem.h和bmem.c中。其内存管理的特点总结下来,有一下几点:

  1. 跨平台
  2. 内存对齐,提高存取效率
  3. 提供了内存申请释放的统一的C接口

最大的亮点就是内存管理是经过内存对齐的,那我们首先讲讲什么是内存对齐。

内存对齐的原因?(百度百科)

  1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

以下图为例,从地址0处读取4字节,1次读取即可。从地址1处读取4字节,需要读取2次。

读取2次时,还要进行如下的合并操作。将 0-3 的数据向上偏移 1 字节,将 4-7 的数据向下偏移 3 字节。最后再将两块数据合并放入寄存器。

原因就是,

CPU 并不是以字节为单位存取数据的。每次内存存取都会产生一个固定的开销,减少内存存取次数将提升程序的性能。所以 CPU 一般会以 2/4/8/16/32 字节为单位来进行存取操作。我们将上述这些存取单位称为内存存取粒度。

内存对齐规则

  1. 数据成员对齐规则:struct 或 union (以下统称结构体)的数据成员,第一个数据成员放在偏移为 0 的地方,以后每个数据成员的偏移为 #pragma pack 指定的数值和这个数据成员自身长度中较小那个的整数倍。

  2. 数据成员为结构体:如果结构体的数据成员还为结构体,则该数据成员的“自身长度”为其内部最大元素的大小。(struct a 里存有 struct b,b 里有char,int,double等元素,那 b “自身长度”为 8)

  3. 结构体的整体对齐规则:在数据成员按照 #1 完成各自对齐之后,结构体本身也要进行对齐。对齐会将结构体的大小增加为 #pragma pack 指定的数值和结构体最大数据成员长度中较小那个的整数倍。

注:#pragma pack (n) 表示设置为 n 字节对齐。 Xcode 默认为 8 字节对齐。当设置为 #pragma pack (1) 时就代表不进行内存对齐,上述代码打印的结果就都为 8。

obs-studio内存管理源码分析

// 内存管理器
struct base_allocator {
	void *(*malloc)(size_t);
	void *(*realloc)(void *, size_t);
	void (*free)(void *);
};

这又是一个C语言实现的类似多态的结构体,根据对函数指针的实际赋值,动态决定如何进行内存申请和释放。

那么接下来,就是如何通过实际malloc、realloc、free的定义来实现跨平台和内存的效果了。

直接上粘源码片段吧,代码中已经把注释写的很详细了,大家自己看吧。

/*
 * NOTE: totally jacked the mem alignment trick from ffmpeg, credit to them:
 * 内存对齐归功于ffmpeg
 *   http://www.ffmpeg.org/
 */

// 通过4字节(32位)进行对齐
#define ALIGNMENT 32

/* TODO: use memalign for non-windows systems */
#if defined(_WIN32)
#define ALIGNED_MALLOC 1
#else
#define ALIGNMENT_HACK 1
#endif

static void *a_malloc(size_t size)
{
#ifdef ALIGNED_MALLOC
	// Windows平台提供了基于内存对齐的内存申请接口
	return _aligned_malloc(size, ALIGNMENT);
#elif ALIGNMENT_HACK
	void *ptr = NULL;
	long diff;
	// 多申请4字节内存
	ptr = malloc(size + ALIGNMENT);
	if (ptr) {
		// 计算内存对齐偏移量
		diff = ((~(long)ptr) & (ALIGNMENT - 1)) + 1;
		// 起始位置调整
		ptr = (char *)ptr + diff;
		// 记录内存对齐偏移量,方便后续释放内存使用
		((char *)ptr)[-1] = (char)diff;
	}

	return ptr;
#else
	return malloc(size);
#endif
}

// 注:我们默认ptr是通过a_malloc申请的内存指针
static void *a_realloc(void *ptr, size_t size)
{
#ifdef ALIGNED_MALLOC
	// Windows平台提供了基于内存对齐的realloc内存申请接口
	return _aligned_realloc(ptr, size, ALIGNMENT);
#elif ALIGNMENT_HACK
	long diff;

	if (!ptr)
		return a_malloc(size);
	// 获取内存对齐偏移量
	diff = ((char *)ptr)[-1];
	// realloc内存,注意,ptr一定要指向实际内存的起始地址
	ptr = realloc((char *)ptr - diff, size + diff);
	if (ptr)
		ptr = (char *)ptr + diff;
	return ptr;
#else
	return realloc(ptr, size);
#endif
}

static void a_free(void *ptr)
{
#ifdef ALIGNED_MALLOC
	_aligned_free(ptr);
#elif ALIGNMENT_HACK
	if (ptr)
		// 释放内存
		// 细节:被free的指针指向一定是ptr减去对齐偏移量的,否则会造成内存泄露
		free((char *)ptr - ((char *)ptr)[-1]);
#else
	free(ptr);
#endif
}

// 在此明确malloc、relloc和free的实际实现
static struct base_allocator alloc = {a_malloc, a_realloc, a_free};

据obs作者所说,将内存对齐发扬光大的是ffmpeg,ffmpeg的源代码也是非常优秀的,C语言中透露着面向对象的影子。大家有空可以自己去研究一下。

参考资料:

【1】内存对齐

【2】内存对齐

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值