📄 mems.c
字号:
//----释放准静态分配的内存块---------------------------------------------------
//功能:释放准静态分配的一个内存块
//参数:pl_mem,待释放的内存块指针
//返回:true = 成功释放,false = 释放失败
//备注: 1.准静态分配与静态内存分配类似,没有保护措施,正确性要程序员自己保证.这种
// 分配方法也不会引起阻塞,在执行module_init_heap_dynamic之前,所有的内存分配
// 均采用准静态分配
// 2.本函数在初始化完成之前调用,中断尚未开启,无需考虑关闭中断的问题.
//-----------------------------------------------------------------------------
bool_t __m_static_free(void * pl_mem)
{
return true;
}
//----从内存堆中分配内存-------------------------------------------------------
//功能:1.规格化内存尺寸,计算满足要求的最小内存尺寸,计算该块内存尺寸的阶数
// 2.读取该级访问路径深度,沿访问路径逐级查找,直到找到空闲内存为止。
// 3.重置内存分配表
// 4.把内存分配信息置入链表中,以备清理内存用.
// 5.如果内存不足,则把事件挂在tg_mem_global.event_wait下,并引发事件切换.
//参数:size,欲分配的内存块尺寸
// timeout,超时设置,单位是毫秒,cn_timeout_forever=无限等待,0则立即按
// 超时返回。非0值将被向上调整为cn_tick_ms的整数倍
//返回:分配的内存指针,NULL表示没有内存可以分配
//备注: 如果在多事件调度启动前调用本函数,记录拥有者时全部算在系统服务事件中。
// 系统服务事件永不结束,故等同于全局分配。
//-----------------------------------------------------------------------------
void *m_malloc(ptu32_t size,uint32_t timeout)
{
uint8_t *ua_address;
ufast_t uf_grade_th;
bool_t en_scheduler;
void *result;
uint16_t *pl_id,id;
uint32_t page;
//启动多事件调度后,dynamic成员不会再发生变化,即使并发访问也是安全的
if(tg_mem_global.dynamic==false)
return(__m_static_malloc(size)); //内存尚未初始化,执行准静态内存分配
//不能在此直接判断size是否满足,因为取得互斥量前可能发生切换而判断无效.
// int_save_asyn_signal();
if(mutex_pend(&tg_mem_mutex,timeout) == false)
return NULL;
en_scheduler = y_query_sch();
if((tg_mem_global.ua_free_block_max < size)
&& ((timeout == 0) || !en_scheduler))
{
result = NULL;
}else
{
if( !__m_check_memory(size,timeout) ) //没有合适的空闲内存块
{
result = NULL;
}else //有合适的空闲内存块
{
uf_grade_th=__m_get_grade(size); //取阶号
ua_address=__malloc_block(uf_grade_th); //申请内存
pg_event_running->local_memory++;
// __record_mem(ua_address,uf_grade_th); //记录内存
//阅读以下条件句请结合mem_global_t中index_event_id成员定义的注释.
pl_id = tg_mem_global.index_event_id;
id = pg_event_running->event_id;
page = (ptu32_t)(ua_address-tg_mem_global.heap_bottom)
>>cn_page_size_suffix_zero;
if(uf_grade_th==0)
{//分配1页
pl_id[page] = id;
}else if(uf_grade_th==1)
{//分配2页
pl_id[page] = -1;
pl_id[page+1] = id;
}else
{ //分配多页
pl_id[page] = -2;
pl_id[page+1] = id;
pl_id[page+2] = uf_grade_th;
}
result = ua_address;
}
}
mutex_post(&tg_mem_mutex);
// int_restore_asyn_signal();
return result;
}
//----分配全局内存-------------------------------------------------------------
//功能:1.规格化内存尺寸,计算满足要求的最小内存尺寸,计算该块内存属于第几阶。
// 2.读取该级访问路径深度,沿访问路径逐级查找,直到找到空闲内存为止。
// 3.重置内存分配表
// 4.如果内存不足,则把事件挂在tg_mem_global.event_wait下,并引发事件切换.
//参数:size,欲分配的内存块尺寸
// timeout,超时设置,单位是毫秒,cn_timeout_forever=无限等待,0则立即按
// 超时返回。非0值将被向上调整为cn_tick_ms的整数倍
//返回:分配的内存指针,NULL表示没有内存可以分配
//备注: 用此函数分配的内存,并不会在事件完成时被收回.
//-----------------------------------------------------------------------------
void *m_malloc_gbl(ptu32_t size,uint32_t timeout)
{
uint8_t *ua_address;
ufast_t uf_grade_th;
uint16_t *pl_id;
bool_t en_scheduler;
void *result;
if(tg_mem_global.dynamic==false)
return(__m_static_malloc(size)); //内存尚未初始化,执行准静态内存分配
//不能在此直接判断size是否满足,因为关取得互斥量前可能发生切换而判断无效.
if(mutex_pend(&tg_mem_mutex,timeout) == false)
return NULL;
en_scheduler = y_query_sch();
if((tg_mem_global.ua_free_block_max < size)
&& ((timeout == 0) || !en_scheduler))
{
result = NULL;
}else
{
if( ! __m_check_memory(size,timeout))
{
result = NULL;
}else
{
uf_grade_th=__m_get_grade(size); //取阶号
ua_address=__malloc_block(uf_grade_th); //申请内存
//以下在id表中记录本次分配的性质
//阅读本段代码请结合mem_global_t中index_event_id成员定义的注释.
pl_id = &tg_mem_global.index_event_id
[(ptu32_t)(ua_address-tg_mem_global.heap_bottom)
>>cn_page_size_suffix_zero];
if(0==uf_grade_th)
*pl_id = -3;
else
{
*pl_id++ = -4;
*pl_id = uf_grade_th;
}
result = ua_address;
}
}
mutex_post(&tg_mem_mutex);
return result;
}
//----分配1块内存--------------------------------------------------------------
//功能: 从内存堆中分配一个内存块,内部调用,不开放给用户
//参数: grade,欲分配的块的阶号,0=1阶,1=2阶,类推之
//返回: 获得的内存块指针,如果不能分配则返回NULL
//-----------------------------------------------------------------------------
void *__malloc_block(ufast_t grade)
{
ptu32_t ua_temp1;
ptu32_t ua_pages_number;
ufast_t uf_word_shift,uf_grade_th,uf_classes;
ufast_t *pl_classes;
ucpu_t ***pppl_bitmap;
ucpu_t **ppl_bitmap;
ucpu_t *pl_bitmap;
ptu32_t ua_word_offset,ua_bit_num;
ucpu_t uc_msk; //字内偏移量
pppl_bitmap=tg_mem_global.ppp_bitmap; //空闲金字塔位图指针的指针表的首指针
ppl_bitmap=pppl_bitmap[grade]; //该阶空闲金字塔位图指针表的首址
pl_classes=tg_mem_global.p_classes; //读取各阶空闲金字塔级数表指针.
uf_classes=pl_classes[grade]-1; //该阶空闲金字塔最高一级指针在
//ppl_bitmap中的偏移量
tg_mem_global.free_pages_num -= (1<<grade); //剩余可用页数
ua_pages_number=0;
do
{//本循环查找第一个可分配块(即bit=0)的偏移位数,从高到低查找,方法:
//首先,找出该阶空闲位图金字塔最高级位图中第一个bit=0的位偏移量.
//然后,根据这个偏移量计算次高级含0位的字偏移,读出该字并找出bit=0的位偏移.
//如此反复可以找到最后一级第一个0位的偏移,这就是本次分配的目标块.
pl_bitmap=ppl_bitmap[uf_classes]; //读取各级位图的指针
//ua_pages_number是上一级的位偏移,ua_temp1是本级最后一字的字内位偏移
ua_temp1=cn_cpu_bits-1-__m_leading_ucpu_zero(~pl_bitmap[ua_pages_number]);
//上一级的字偏移×字长+本级字内位偏移得到本级总位偏移
ua_pages_number =(ua_pages_number<<cn_cpu_bits_suffix_zero)+ua_temp1;
}while(uf_classes-- >0);
ua_pages_number <<= grade; //目标块的首页的页号
//下面开始在bitmap表标记本次分配情况
for(uf_grade_th=0;uf_grade_th<=grade;uf_grade_th++)
{//从0阶直到当前阶(不包含),处理阶号低于当次申请的内存的位图.
//块的起始页号右移阶号即得到在该阶0级位图中的偏移
ua_word_offset = ua_pages_number>>uf_grade_th;
ppl_bitmap=pppl_bitmap[uf_grade_th]; //取得该阶分配表指针表的首址
ua_bit_num=1<<(grade-uf_grade_th); //计算掩码位数
for(ua_temp1=0;ua_temp1<pl_classes[uf_grade_th];ua_temp1++)
{//从低级到高级遍历该阶各级位图
pl_bitmap=ppl_bitmap[ua_temp1]; //相应级相应路径分配表首址
//计算字内位偏移,下面算式起取模的作用
uf_word_shift=ua_word_offset&cn_low_xbit_msk[cn_cpu_bits_suffix_zero];
ua_word_offset=ua_word_offset>>cn_cpu_bits_suffix_zero; //字偏移
if(ua_bit_num>=cn_cpu_bits) //ua_bit_num保存的是需要置1的位
{//位数大于或等于1个字宽,因为采用2的n次方分阶,所以要处理的位数肯定
//是整数个字,按整数个字置1就可以了.
//计算字数,也是下一级位图中需置1的位数
ua_bit_num >>=cn_cpu_bits_suffix_zero;
//uc_word_shift在这里当临时变量使用
for(uf_word_shift =0; uf_word_shift < ua_bit_num; uf_word_shift++)
pl_bitmap[ua_word_offset+uf_word_shift]=cn_allbit_1;
}else if(ua_bit_num>0)
//位数小于一个字宽,且大于0
{
uc_msk=cn_low_xbit_msk[ua_bit_num]<<uf_word_shift;
pl_bitmap[ua_word_offset] |=uc_msk;
if(pl_bitmap[ua_word_offset] == cn_allbit_1)
//被置1的位所在的字变为全1时,该字对应的下一级位图相应的位需置1
ua_bit_num=0;
else
break;
}else
{//前一次操作使一个字从非全1变成全1,置位该字对应的下一级路径相应位
pl_bitmap[ua_word_offset] |= 1<<uf_word_shift;
if(pl_bitmap[ua_word_offset] == cn_allbit_1)
//被置1的位所在的字变为全1时,该字对应的下一级位图相应的位需置1
;
else
break;
}
}
}
for(; uf_grade_th < tg_mem_global.uf_grades; uf_grade_th++)
{//当前阶(包含)到最高阶,处理阶号高于或等于当次申请的内存的位图.每次只需1位置1
//块的起始页号右移阶号即得到在该阶0级位图中的偏移
ua_word_offset = ua_pages_number >> uf_grade_th;
if(ua_word_offset >= tg_mem_global.ua_pages_num>>uf_grade_th)
//该阶无对应块,这是可能的,比如一个共10页的内存,第9页和第10页都在第3阶中没有
//映射.条件式右边是本阶总块数,左端是从0起计的偏移量,最大只能是总块数-1
break;
ppl_bitmap=pppl_bitmap[uf_grade_th]; //取得该阶分配表指针表的首址
for(ua_temp1=0;ua_temp1<pl_classes[uf_grade_th];ua_temp1++)
{
pl_bitmap = ppl_bitmap[ua_temp1];
//计算字内位偏移,下面算式起取模的作用
uf_word_shift=ua_word_offset&cn_low_xbit_msk[cn_cpu_bits_suffix_zero];
ua_word_offset=ua_word_offset>>cn_cpu_bits_suffix_zero; //字偏移
pl_bitmap[ua_word_offset] |= 1<<uf_word_shift;
if(pl_bitmap[ua_word_offset] == cn_allbit_1)
//被掩模的位所在的字变为全1时,该字对应的下一级位图相应的位需置1
;
else
break;
}
}
//刷新最大空闲内存块
uf_grade_th=tg_mem_global.uf_grades-1; //最高阶号
do{
uf_classes= pl_classes[uf_grade_th]; //第uf_grade_th阶总级数
ppl_bitmap = pppl_bitmap[uf_grade_th]; //位图索引表指针
pl_bitmap = ppl_bitmap[uf_classes-1]; //最高级位图指针
if(*pl_bitmap != (~0))
{//路径顶端只要不是全1就表示该阶肯定有空闲块.
//根据阶号计算最大空闲块的尺寸.
tg_mem_global.ua_free_block_max=cn_page_size<<uf_grade_th;
break;
}
}while(uf_grade_th-- >0); //从最高阶(最大块)扫描到0阶
return tg_mem_global.heap_bottom
+(ua_pages_number<<cn_page_size_suffix_zero);
}
//----查询内存尺寸-------------------------------------------------------------
//功能: 根据给定的指针,查询该指针所在的内存块的尺寸.
//参数: mp,动态分配的内存指针.
//返回: 内存块尺寸,返回0有几种含义:1.非法指针,2.mp是由准静态分配的指针.
//----------------------------------------------------------------------------
ptu32_t m_check_size(void * mp)
{
ptu32_t ua_pages_number;
uint8_t *temp;
uint16_t *pl_id;
ufast_t uf_free_grade_th;
if(((uint8_t*)mp<tg_mem_global.static_bottom)
|| ((uint8_t*)mp>=tg_mem_global.heap_top))
{
return 0; //错误,指针不在堆范围内
}
if((uint8_t*)mp < tg_mem_global.heap_bottom) //该指针在静态分配区
{
temp = (uint8_t*)((uint8_t*)mp - sizeof(ptu32_t));
return *(ptu32_t*)temp;
}else //该指针在块相联动态分配区
{
//计算释放的内存的页号
ua_pages_number=(ptu32_t)((ptu32_t)mp-(ptu32_t)tg_mem_global.heap_bottom)
>>cn_page_size_suffix_zero;
//查找释放的内存块的阶号,从0起计.通过阶号也可以确定内存块的大小.
//确定内存分配类型,局部分配需要知道拥有该内存的事件id,
//全局分配无需清除内存分配跟踪表,无需知道拥有该内存的事件id
pl_id = &tg_mem_global.index_event_id[ua_pages_number];
switch(pl_id[0])
{
case 0xffff :
{//双页局部分配,-1+id
uf_free_grade_th = 1;
}break;
case 0xfffe :
{//多页局部分配:-2+id+阶号
uf_free_grade_th = pl_id[2];
}break;
case 0xfffd :
{//单页全局内存:-3
uf_free_grade_th = 0;
}break;
case 0xfffc :
{//双(多)页全局内存:-4+阶号.
uf_free_grade_th = pl_id[1];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -