实时系统动态内存算法分析dsa(一)

最近开发过程使用了ucos系统,为了提高系统性能,需要自己移植内存管理算法,看了一些关于实时系统动态内存算法的东西,整理了出来,希望也给同样需求的人提供方便;我们在开发时很少去关注内存是如何分配的,尤其对于PC端的开发人员来说完全不会涉及到,因为PC的资源往往是充裕的,但对于嵌入式开发来说,很多时候物理内存会成为开发时的瓶颈,如何最大程度的利用现有内存资源,这就是我们下面要讨论的;


下面从以下几点来由浅入深的探讨:

一、介绍比较常见的几种内存分配算法(DSA,Dynamic storage allocation);

        二、通过实例来看一个最简单的内存分配算法;

        三、对于TLSF算法代码进行分析;

        四、分析linux中的buddy伙伴系统代码;


参考文章:

 TLSF: a New Dynamic Memory Allocator for Real-Time Systems

A constant-time dynamic storage allocator for real-timesystems      


一、内存分配算法介绍


1、背景:

DSA(Dynamic storage allocation)编程开发中有着重要意义,特别是面向对象编程当中,但是我们往往打怵用DSA,

因为一旦使用不当,DSA的响应时间与碎片会让我们的系统健壮性和鲁棒性变得非常脆弱,衡量内存算法最重要的两个标

准就是其响应时间与碎片化程度;

2、分类:

DSA的目的是让应用程序方便的从内存池里获取未使用的内存,不同的算法主要区别在于如何高效的获取需要的内存

大小,我们先看几个经典算法;这里我做了概括说明,对于其具体的优缺点可以阅读TLSF: a New Dynamic Memory 

Allocator for Real-Time Systems这篇论文,里面有更详细的说明;



实例-简单sda实现

理论性的东西网上很多,这里我就不去赘述了,下面我们看一个sda的实例,非常简单,类似上面提到的Segregated Free Lists算法,这种算法是根据自身系统将一个大的内存池分割为不同大小的小内存池,申请内存时去匹配最合适的内存池申请内存;大内存块在分配内存时出现比自己块小的内存块时将其移到更小内存块,当小内存块被释放,可以合并为大内存块时,需要合并,如下图所示:




这里为了说明问题只添加了4个小的内存池,上面每个pool的大小都是经验估值,每个pool的大小和数量需要根据你自己系统(应用)去修改,用的频率高的pool数量要多些;用三个函数就可以说明这种算法的实现,初始化,malloc和free;

1、内存池初始化:

我用ucos系统来做测试,第一步肯定是跟系统要一块足够的内存区域,ucos没有MMU,所以这块内存区域不可能大于物理内存,我这里跟系统要10k bytes。

#define BLOCK_NUM0                      300
#define BLOCK_NUM1			150
#define BLOCK_NUM2			80
#define BLOCK_NUM3			40

#define BLOCK_SIZE0			32
#define BLOCK_SIZE1                     64
#define BLOCK_SIZE2			128
#define BLOCK_SIZE3			256

//这个数组大小不能小于我们申请的几个分区总和,也不能超过系统RAM
static UINT32 MEM_Pool[10*1024];


//因为我们需要四个分区,用这个变量来保存每个分区信息
int OS_MEM *p_mem_info[4];

int MEM_Init(void)
{
	INT8U err = 0;       
		OS_MEM *p_mem;
		UINT8 *p_mem_pool = (UINT8 *)&MEM_Pool[0];

	//ucos内存初始化
	OS_MemInit();

	//part0
	p_mem = OSMemCreate(p_mem_pool, BLOCK_NUM0, BLOCK_SIZE0, &err);
	if (err == OS_ERR_NONE) {
		p_mem_info[0] = p_mem;

	} else {
		printf("[ERROR]%s:\r\n", FUNCTION_NAME, err);
		return err;
	}
	p_mem_pool += (BLOCK_NUM0 * BLOCK_SIZE0);

	//part1
	p_mem = OSMemCreate(p_mem_pool, BLOCK_NUM1, BLOCK_SIZE1, &err);
	if (err == OS_ERR_NONE) {
		p_mem_info[1] = p_mem;
	} else {
		printf("[ERROR]%s:\r\n", FUNCTION_NAME, err);
		return err;
	}
	p_mem_pool += (BLOCK_NUM1 * BLOCK_SIZE1);

	//part2
	p_mem = OSMemCreate(p_mem_pool, BLOCK_NUM2, BLOCK_SIZE2, &err);
	if (err == OS_ERR_NONE) {
		p_mem_info[2] = p_mem;

	} else {
		printf("[ERROR]%s:\r\n", FUNCTION_NAME, err);
		return err;
	}
	p_mem_pool += (BLOCK_NUM2 * BLOCK_SIZE2);


	p_mem = OSMemCreate(p_mem_pool, BLOCK_NUM3, BLOCK_SIZE3, &err);
	if (err == OS_ERR_NONE) {
		p_mem_info[3] = p_mem;
	} else {
		return err;
	}
	p_mem_pool += (BLOCK_NUM3 * BLOCK_SIZE3);

	return err;
}


要理解这块代码,首先简单看一下ucos系统提供的内存相关的函数:



我们回过头看MEM_Init函数,25行的OS_MemInit()是ucos使用内存的初始化操作,我们来看27-36行,part0部分是对32字节分区初始化,调用OSMemCreate()函数时需要给出申请起始地址,分区大小和数量,我们在开始定义了一个全局的静态变量,用来获取可用地址范围,这里的OSMemCreate()函数返回分区信息;part1,part2,part3部分雷同;


2、内存申请:

上面的初始化不难理解,下面看下我们需要使用内存时如何申请:

void * malloc(UINT32 size)
{
	
	OS_MEM *p_info;
	INT8U err;
	void *p_addr;

	p_info = FindPartitionBySize(Size);
	if (p_info == NULL) {
		printf("No match memory partition found!");
		return NULL;
	}
	
	//Ucos api,内存分配函数,执行此函数后ucos内存地址应用层才可以使用
	p_addr = OSMemGet(p_info->p_mem, &err);
	if (err != OS_ERR_NONE || p_addr == NULL) {
		return NULL;
	}


	return p_addr;
}

这段代码的关键点是FindPartitionBySize函数,定义如下:

OS_MEM *FindPartitionBySize(int size)
{
	UINT8 mid = (4 - 1) / 2, i;
	
	//还记得上个表中对ucos系统变量OS_MEM的说明?当我们调用OSMemCreate函数返回OS_MEM时,分区中的块大小已经固定了
	if (size > (*p_mem_info[mid]).p_mem->OSMemBlkSize) {
		for (i = mid + 1; i < 4; i++) {
			if (size<= (*p_mem_info[i]).p_mem->OSMemBlkSize)
				return p_mem_info[i];
		}		
	} else {
		for (i = 0; i < mid + 1; i++) {
			if (size <= (*p_mem_info[i]).p_mem->OSMemBlkSize)
				return p_mem_info[i];
		}
	}

	return NULL;	
}

这段代码比较简单,使用最简单的二分查找法,先找到中间分区的大小,如果需要的分区大小比中间分区大,则遍历更大分区,反之遍历更小分区;但要注意,FindPartitionBySize返回的只是分区首地址,最后还是通过ucos的OSMemGet()函数来获取分区中实际使用的内存地址;

OS_MEM *FindPartitionBySize(int size)
{
	UINT8 mid = (4 - 1) / 2, i;
	
	//还记得上个表中对ucos系统变量OS_MEM的说明?当我们调用OSMemCreate函数返回OS_MEM时,分区中的块大小已经固定了
	if (size > (*p_mem_info[mid]).p_mem->OSMemBlkSize) {
		for (i = mid + 1; i < 4; i++) {
			if (size<= (*p_mem_info[i]).p_mem->OSMemBlkSize)
				return p_mem_info[i];
		}		
	} else {
		for (i = 0; i < mid + 1; i++) {
			if (size <= (*p_mem_info[i]).p_mem->OSMemBlkSize)
				return p_mem_info[i];
		}
	}


	return NULL;	
}




3、内存释放:

void Free(void *p_Addr)
{
	
	OS_MEM *p_info;
	INT8U err;
	UINT8 *p = (UINT8 *)p_Addr;

	if (p == NULL) {
		return;
	}

	p_info = FindPartitionByAddr((void *)p);
	if (p_info == NULL) {
		printf("No match memory partition found! Address");
		return;
	}


	err = OSMemPut(p_info->p_mem, (void *)p);
	if (err != OS_ERR_NONE) {
		printf("Put memory block error!");
		return;
	}


	return;
}



看下FindPartitionByAddr()函数的定义:
OS_MEM *FindPartitionByAddr(void *p_Addr)
{
	UINT8 i;
	UINT32 addr = (UINT32)p_Addr;

	for (i = 0; i < 4; i++) {
		UINT32 start = (UINT32)p_mem_info[i].p_mem->OSMemAddr;
		UINT32 size = (*p_mem_info[i]).p_mem->OSMemBlkSize * (*p_mem_info[i]).p_mem->OSMemNBlks;
		if (addr >= start &&  addr < (start + size))
			return p_mem_info[i];
	}
	
	return NULL;
}


使用ucos api OSMemPut()来释放内存时,需要知道分区和所需释放内存地址;

好了,分析到这里,我们已经完成了一个简单的sda算法,但这种算法虽然时间度不是很复杂,但容易产生碎片,这种方法对于低功耗、低处理能力的MCU还是很不错的,随着现在硬件水平的提高,这种算法可能无法满足要求,下面更深入一步,继续看看比较复杂的TLSF实现;


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DSA(Digital Signature Algorithm)是一种用于数字签名的加密算法,它基于离散对数问题和有限域上的算术运算。以下是用C++实现DSA算法的示例代码: ```cpp #include <iostream> #include <cstdlib> #include <ctime> #include <cmath> using namespace std; // 求 a^b mod c 的值 int modpow(int a, int b, int c) { int res = 1; while (b > 0) { if (b & 1) { res = (res * a) % c; } a = (a * a) % c; b >>= 1; } return res; } // 判断 n 是否为素数 bool is_prime(int n) { if (n <= 1) return false; if (n <= 3) return true; if (n % 2 == 0 || n % 3 == 0) return false; for (int i = 5; i * i <= n; i += 6) { if (n % i == 0 || n % (i + 2) == 0) { return false; } } return true; } // 生成大素数 int gen_prime() { int p; do { p = rand() % 1000 + 1000; // 生成1000 ~ 1999之间的随机数 } while (!is_prime(p)); return p; } // 生成密钥对 void gen_key_pair(int &p, int &q, int &g, int &x, int &y) { // 生成两个大素数 p 和 q p = gen_prime(); q = gen_prime(); // 计算 g,满足 g^q mod p = 1 for (g = 2; g < p; ++g) { if (modpow(g, q, p) == 1) { break; } } // 生成随机数 x,满足 0 < x < q x = rand() % q + 1; // 计算 y,满足 y = g^x mod p y = modpow(g, x, p); } // 生成签名 void gen_signature(int p, int q, int g, int x, int &r, int &s, int m) { // 随机选择 k,满足 0 < k < q int k = rand() % q + 1; // 计算 r,满足 r = (g^k mod p) mod q r = modpow(g, k, p) % q; // 计算 s,满足 s = (k^-1 * (m + x * r)) mod q int inv_k = modpow(k, q - 2, q); s = (inv_k * (m + x * r)) % q; } // 验证签名 bool verify_signature(int p, int q, int g, int y, int r, int s, int m) { if (r < 1 || r > q || s < 1 || s > q) { return false; } // 计算 w,满足 w = s^-1 mod q int w = modpow(s, q - 2, q); // 计算 u1,满足 u1 = (m * w) mod q int u1 = (m * w) % q; // 计算 u2,满足 u2 = (r * w) mod q int u2 = (r * w) % q; // 计算 v,满足 v = ((g^u1 * y^u2) mod p) mod q int v = ((modpow(g, u1, p) * modpow(y, u2, p)) % p) % q; return v == r; } int main() { srand(time(nullptr)); int p, q, g, x, y; gen_key_pair(p, q, g, x, y); int m = 123456; int r, s; gen_signature(p, q, g, x, r, s, m); bool result = verify_signature(p, q, g, y, r, s, m); if (result) { cout << "Signature is valid." << endl; } else { cout << "Signature is invalid." << endl; } return 0; } ``` 这段代码实现了DSA算法的密钥对生成、签名和验证功能。其中,`modpow()`函数用于求幂取模,`is_prime()`函数用于判断一个数是否为素数,`gen_prime()`函数用于生成一个大素数,`gen_key_pair()`函数用于生成密钥对,`gen_signature()`函数用于生成签名,`verify_signature()`函数用于验证签名。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值