임베디드 시스템에서의 malloc 동작
저는 현재 임베디드 프로젝트(STM32F103RB, cooCox CoIDE v.1.7.6 with arm-none-eabi-gcc 4.8 2013q4)를 진행하고 있으며, 그 방법을 이해하려고 합니다.malloc()
꾸밈없이 굴다C
RAM이 가득 찼을 때.
제 STM32는 20kB = 0x5000Bytes의 RAM을 가지고 있으며, 스택에는 0x200이 사용됩니다.
#include <stdlib.h>
#include "stm32f10x.h"
struct list_el {
char weight[1024];
};
typedef struct list_el item;
int main(void)
{
item * curr;
// allocate until RAM is full
do {
curr = (item *)malloc(sizeof(item));
} while (curr != NULL);
// I know, free() is missing. Program is supposed to crash
return 0;
}
나는 기대합니다.malloc()
돌아오다NULL
힙이 너무 작아져서 할당할 수 없게 되는 순간:
0x5000
(램) -0x83C
(bss) -0x200
(스택) =0x45C4
(heap)
그래서 실행할 때는malloc()
열여덟 번째로.1개는 1024=0x400
바이트 크기가 큽니다.
하지만 대신 UC는 그들을HardFault_Handler(void)
(심지어는 아니지만) 18번째 이후에MemManager_Handler(void)
)
어떻게 예측할 수 있는지 조언해 주실 분 계신가요?malloc()
실패 - a를 기다린 이후로NULL
반품이 안 되는 것 같습니다.
감사해요.
겉보기엔 그렇지 않습니다.malloc
어떤 점검도 하지 않고 있습니다오류는 하드웨어가 잘못된 주소에 대한 쓰기를 감지하는 데서 발생합니다. 이 주소는 다음 주소에서 발생한 것일 수 있습니다.malloc
그 자체.
언제malloc
메모리를 할당하고 내부 풀에서 청크를 가져와 사용자에게 반환합니다.그러나, 그것은 몇가지 정보를 저장할 필요가 있습니다.free
할당 해제를 완료할 수 있는 기능입니다.보통, 그것은 덩어리의 실제 길이입니다.그 정보를 저장하기 위해서는malloc
는 청크 자체의 시작 부분에서 몇 바이트를 가져와 정보를 작성한 다음 자신의 정보를 작성한 지점을 지나 주소를 반환합니다.
예를 들어, 10바이트 청크를 요청했다고 가정해 보겠습니다.malloc
예를 들어, 주소에서 사용 가능한 16바이트 청크를 사용할 것입니다.0x3200..0x320F
, 길이(즉, 16)를 바이트 1과 2로 쓰고 반환합니다.0x3202
다시 당신에게로.이제 당신의 프로그램은 10 바이트를 사용할 수 있습니다.0x3202
로.0x320B
. 나머지 4바이트도 사용 가능합니다. 만약 당신이 전화를 걸면.realloc
14바이트를 요구하면 재할당은 없습니다.
중요한 점은 다음과 같습니다.malloc
사용자에게 반환하려는 메모리의 길이(쓰기 대상 주소가 유효해야 함)를 기록합니다.18번째 반복 후 다음 청크의 주소가 음(매우 큰 양으로 변환됨)이므로 CPU가 쓰기를 트랩하고 하드 폴트를 트리거합니다.
힙과 스택이 서로를 향해 증가하는 상황에서는 메모리 부족을 감지하고 마지막 바이트의 메모리를 모두 사용할 수 있는 신뢰할 수 있는 방법이 없습니다. 이는 종종 매우 바람직한 일입니다.malloc
할당 후 사용할 스택의 양을 예측할 수 없으므로 시도조차 하지 않습니다.그렇기 때문에 대부분의 경우 바이트 수를 계산하는 것이 여러분에게 달려있습니다.
일반적으로 임베디드 하드웨어의 경우 공간이 수십 킬로바이트로 제한되어 있는 경우,malloc
"arbitrary" 장소에서 전화를 걸 수 있습니다.대신 미리 계산된 제한을 사용하여 모든 메모리를 미리 할당하고 필요한 구조에 할당한 후에는 절대로 호출하지 않습니다.malloc
다시.
불법 메모리 액세스로 인해 프로그램이 중단될 가능성이 높습니다. 이는 거의 항상 합법적인 메모리 액세스로 인한 간접적(후속적) 결과이지만 사용자가 수행하려고 하지 않은 결과입니다.
예를 들어, 시스템에서 무슨 일이 일어나고 있는지에 대해서는 다음과 같이 추측합니다.
스택 바로 뒤에 힙이 시작될 가능성이 높습니다.이제 스택 오버플로우가 발생했다고 가정합니다.main
. 그 다음에 당신이 수행하는 작업 중 하나가main
, 당연히 합법적인 작업이지만 일부 "junk" 데이터로 힙의 시작을 덮어씁니다.
따라서 다음 번에 힙에서 메모리를 할당하려고 하면 사용 가능한 다음 메모리 청크에 대한 포인터가 더 이상 유효하지 않아 결국 메모리 액세스 위반으로 이어지게 됩니다.
우선 스택 크기를 0x200바이트에서 0x400바이트로 늘릴 것을 강력하게 권장합니다.이는 일반적으로 프로젝트의 링커 설정에서 링커 명령 파일 내에 정의되거나 IDE를 통해 정의됩니다.
만약 당신의 프로젝트가 IAR에 있다면, 당신은 그것을 변경할 수 있습니다.icf
파일:
define symbol __ICFEDIT_size_cstack__ = 0x400
그 외에는 당신의 컴퓨터에 코드를 추가하는 것을 제안합니다.HardFault_Handler
, 충돌 전에 콜 스택을 재구성하고 값을 등록합니다.이렇게 하면 런타임 오류를 추적하고 발생한 위치를 정확하게 파악할 수 있습니다.
startup_stm32f03xx.s' 파일에서 다음 코드가 있는지 확인합니다.
EXTERN HardFault_Handler_C ; this declaration is probably missing
__tx_vectors ; this declaration is probably there
DCD HardFault_Handler
그런 다음 동일한 파일에 다음 인터럽트 핸들러를 추가합니다(다른 핸들러가 모두 있는 곳에).
PUBWEAK HardFault_Handler
SECTION .text:CODE:REORDER(1)
HardFault_Handler
TST LR, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B HardFault_Handler_C
그런 다음 'stm32f03xx.c' 파일에서 다음 ISR을 추가합니다.
void HardFault_Handler_C(unsigned int* hardfault_args)
{
printf("R0 = 0x%.8X\r\n",hardfault_args[0]);
printf("R1 = 0x%.8X\r\n",hardfault_args[1]);
printf("R2 = 0x%.8X\r\n",hardfault_args[2]);
printf("R3 = 0x%.8X\r\n",hardfault_args[3]);
printf("R12 = 0x%.8X\r\n",hardfault_args[4]);
printf("LR = 0x%.8X\r\n",hardfault_args[5]);
printf("PC = 0x%.8X\r\n",hardfault_args[6]);
printf("PSR = 0x%.8X\r\n",hardfault_args[7]);
printf("BFAR = 0x%.8X\r\n",*(unsigned int*)0xE000ED38);
printf("CFSR = 0x%.8X\r\n",*(unsigned int*)0xE000ED28);
printf("HFSR = 0x%.8X\r\n",*(unsigned int*)0xE000ED2C);
printf("DFSR = 0x%.8X\r\n",*(unsigned int*)0xE000ED30);
printf("AFSR = 0x%.8X\r\n",*(unsigned int*)0xE000ED3C);
printf("SHCSR = 0x%.8X\r\n",SCB->SHCSR);
while (1);
}
사용할 수 없는 경우printf
이 특정 Hard-Fault 인터럽트가 발생하는 실행 시점에서, 대신 위의 모든 데이터를 글로벌 버퍼에 저장하여, 도달 후에 볼 수 있습니다.while (1)
.
그런 다음 http://www.keil.com/appnotes/files/apnt209.pdf 의 'Cortex-M Fault Exceptions and Registers' 섹션을 참조하여 문제를 이해하거나 추가 지원이 필요한 경우 여기에 출력을 게시하십시오.
업데이트:
위의 모든 것 외에도 힙의 기본 주소가 올바르게 정의되었는지 확인합니다.프로젝트 설정(일반적으로 데이터 섹션과 스택 바로 뒤에 있음) 내에서 하드 코드화되어 있을 수 있습니다.그러나 프로그램의 초기화 단계에서 런타임 중에 결정할 수도 있습니다.일반적으로 데이터 섹션의 기본 주소와 프로그램 스택(프로젝트 빌드 후 생성된 맵 파일에서)을 확인하고 힙이 둘 중 하나와 겹치지 않도록 해야 합니다.
저는 힙의 기본 주소가 일정한 주소로 설정된 경우가 있었는데, 처음부터 괜찮았습니다.하지만 프로그램에 글로벌 변수를 추가하여 데이터 섹션의 크기를 점차 늘렸습니다.스택은 데이터 섹션 바로 뒤에 위치했고, 데이터 섹션이 커짐에 따라 "앞으로" 이동했기 때문에 둘 중 하나에도 문제가 없었습니다.그러나 결국 힙은 스택의 "위" 부분에 할당되었습니다.그래서 어느 시점부터 힙 작업이 스택의 변수를 무시하기 시작했고 스택 작업이 힙의 내용을 무시하기 시작했습니다.
그arm-none-eabi-*
툴체인 배포판에는 새로운 lib C 라이브러리가 포함됩니다.내장된 시스템에 대해 newlib이 구성된 경우, 사용자 프로그램은 newlib이 제대로 작동하도록 기능을 제공해야 합니다.
malloc()
는 힙 메모리가 어디에서 시작되고 어디에서 끝나는지 파악하는 데에만 의존합니다.첫번째 전화는_sbrk()
힙의 시작을 반환하고, 필요한 양의 메모리를 사용할 수 없는 경우 후속 호출이 반환됩니다.malloc()
답례로NULL
응용 프로그램으로.당신의._sbrk()
사용 가능한 메모리보다 더 많은 메모리를 할당할 수 있기 때문에 고장 난 것처럼 보입니다.당신은 그것이 돌아오도록 고칠 수 있을 것입니다.-1
힙이 스택과 충돌할 것으로 예상되기 전에.
표준사용c malloc
그것은 구별하기가 매우 어렵습니다.malloc
제가 보기엔 버그가 있는 것 같습니다.그래서 당신은 어떤 커스텀을 구현함으로써 메모리를 관리할 수 있습니다.malloc
당신의 RAM 주소를 사용합니다.
이것이 당신에게 도움이 될지는 모르겠지만 나는 약간의 주문을 했습니다.malloc
나의 컨트롤러 관련 프로젝트는 다음과 같습니다.
#define LENGTH_36_NUM (44)
#define LENGTH_52_NUM (26)
#define LENGTH_64_NUM (4)
#define LENGTH_128_NUM (5)
#define LENGTH_132_NUM (8)
#define LENGTH_256_NUM (8)
#define LENGTH_512_NUM (18)
#define LENGTH_640_NUM (8)
#define LENGTH_1536_NUM (6)
#define CUS_MEM_USED (1)
#define CUS_MEM_NO_USED (0)
#define CALC_CNT (0)
#define CALC_MAX (1)
#define __Ram_Loc__ (0x20000000) ///This is my RAM address
#define __TOP_Ram_Loc__ (0x20000000 + 0x8000 -0x10) //Total 32K RAM and last 16 bytes reserved for some data storage
typedef struct _CUS_MEM_BLOCK_S {
char used;
int block_size;
char *ptr;
char *next;
} cus_mem_block_s;
static struct _MEM_INFO_TBL_S {
int block_size;
int num_max;
cus_mem_block_s *wm_head;
int calc[2];
} memInfoTbl[] = {
{36, LENGTH_36_NUM , 0, {0,0} },
{52, LENGTH_52_NUM , 0, {0,0} },
{64, LENGTH_64_NUM , 0, {0,0} },
{128, LENGTH_128_NUM , 0, {0,0} },
{132, LENGTH_132_NUM , 0, {0,0} },
{256, LENGTH_256_NUM , 0, {0,0} },
{512, LENGTH_512_NUM , 0, {0,0} },
{640, LENGTH_640_NUM , 0, {0,0} },
{1536,LENGTH_1536_NUM, 0, {0,0} },
};
#define MEM_TBL_MAX (sizeof(memInfoTbl)/sizeof(struct _MEM_INFO_TBL_S))
BOOL MemHeapHasBeenInitialised = FALSE;
이 매크로는 기본적으로 RAM 주소를 정의하고 자주 할당해야 하는 블록 크기에 대해 더 많은 블록 번호를 수동으로 선택했습니다.36바이트가 더 필요해서 더 많은 숫자가 필요합니다.
이것은 meminit의 init 함수입니다.
void cus_MemInit(void)
{
int i,j;
cus_mem_block_s *head=NULL;
unsigned int addr;
addr = __Ram_Loc__;
for(i=0; i<MEM_TBL_MAX; i++)
{
head = (char *)addr;
memInfoTbl[i].wm_head = head;
for(j=0;j<memInfoTbl[i].num_max; j++)
{
head->used =CUS_MEM_NO_USED;
head->block_size = memInfoTbl[i].block_size;
head->ptr = (char *)(addr + sizeof(cus_mem_block_s));
addr += (memInfoTbl[i].block_size + sizeof(cus_mem_block_s));
head->next =(char *)addr;
head = head->next;
if(head > __TOP_Ram_Loc__)
{
printf("%s:error.\n",__FUNCTION__);
return;
}
}
}
head->ptr = 0;
head->block_size = 0;
head->next = __Ram_Loc__;
MemHeapHasBeenInitialised=TRUE;
}
이것은 할당을 위한 것입니다.
void* CUS_Malloc( int wantedSize )
{
void *pwtReturn = NULL;
int i;
cus_mem_block_s *head;
if(MemHeapHasBeenInitialised == FALSE)
goto done_exit;
for(i=0; i<MEM_TBL_MAX; i++)
{
if(wantedSize <= memInfoTbl[i].block_size)
{
head = memInfoTbl[i].wm_head;
while(head->ptr)
{
if(head->used == CUS_MEM_NO_USED)
{
head->used = CUS_MEM_USED;
pwtReturn = head->ptr;
goto done;
}
head = head->next;
}
goto done;
}
}
done:
if(pwtReturn)
{
for(i=0; i<MEM_TBL_MAX; i++)
{
if(memInfoTbl[i].block_size == head->block_size)
{
memInfoTbl[i].calc[CALC_CNT]++;
if(memInfoTbl[i].calc[CALC_CNT] > memInfoTbl[i].calc[CALC_MAX] )
memInfoTbl[i].calc[CALC_MAX]=memInfoTbl[i].calc[CALC_CNT];
break;
}
}
}
done_exit:
return pwtReturn;
}
이것은 무료입니다.
void CUS_Free(void *pm)
{
cus_mem_block_s *head;
char fault=0;
if( (pm == NULL) || (MemHeapHasBeenInitialised == FALSE) )
goto done;
if( (pm < __RamAHB32__) && (pm > __TOP_Ram_Loc__) )
{
printf("%s:over memory range\n",__FUNCTION__);
goto done;
}
head = pm-sizeof(cus_mem_block_s);
if(head->used)
head->used = CUS_MEM_NO_USED;
else
{
printf("%s:free error\n",__FUNCTION__);
fault=1;
}
if(fault)
goto done;
int i;
for(i=0;i<MEM_TBL_MAX;i++)
{
if(memInfoTbl[i].block_size == head->block_size)
{
memInfoTbl[i].calc[CALC_CNT]--;
goto done;
}
}
done:;
}
결국 위의 기능을 사용할 수 있습니다.
void *mem=NULL;
mem=CUS_Malloc(wantedsize);
그러면 사용한 메모리를 다음과 같이 볼 수 있습니다.
void CUS_MemShow(void)
{
int i;
int block_size;
int block_cnt[MEM_TBL_MAX];
int usedSize=0, totalSize=0;
cus_mem_block_s *head;
if(MemHeapHasBeenInitialised == FALSE)
return;
memset(block_cnt, 0, sizeof(block_cnt));
head = memInfoTbl[0].wm_head;
i=0;
block_size = head->block_size;
vTaskSuspendAll();
while( head->ptr !=0)
{
if(head->used == CUS_MEM_USED )
{
block_cnt[i]++;
usedSize +=head->block_size;
}
usedSize += sizeof(cus_mem_block_s);
totalSize += (head->block_size+ sizeof(cus_mem_block_s));
/* change next memory block */
head = head->next;
if( block_size != head->block_size)
{
block_size = head->block_size;
i++;
}
}
xTaskResumeAll();
usedSize += sizeof(cus_mem_block_s);
totalSize+= sizeof(cus_mem_block_s);
dprintf("----Memory Information----\n");
for(i=0; i<MEM_TBL_MAX; i++) {
printf("block %d used=%d/%d (max %d)\n",
memInfoTbl[i].block_size, block_cnt[i],
memInfoTbl[i].num_max,
memInfoTbl[i].calc[CALC_MAX]);
}
printf("used memory=%d\n",usedSize);
printf("free memory=%d\n",totalSize-usedSize);
printf("total memory=%d\n",totalSize);
printf("--------------------------\n");
}
일반적으로 메모리를 먼저 미리 계산한 후에 내가 가진 것처럼 줍니다.
여기서 verendi의 이전 답변을 기반으로 할당하기에 힙이 너무 작은 경우 malloc()을 "강제"하여 NULL을 반환하는 방법을 찾을 수 있습니다.저는 STACK의 최대량을 추정했고 이를 바탕으로 최악의 경우 스택이 시작될 수 있는 주소를 계산할 수 있었습니다.
#define STACK_END_ADDRESS 0x20020000
#define STACK_MAX_SIZE 0x0400
#define STACK_START_ADDRESS (STACK_END_ADDRESS - STACK_MAX_SIZE)
void * _sbrk_r(
struct _reent *_s_r,
ptrdiff_t nbytes)
{
char *base; /* errno should be set to ENOMEM on error */
if (!heap_ptr) { /* Initialize if first time through. */
heap_ptr = end;
}
base = heap_ptr; /* Point to end of heap. */
#ifndef STACK_START_ADDRESS
heap_ptr += nbytes; /* Increase heap. */
return base; /* Return pointer to start of new heap area. */
#else
/* End of heap mustn't exceed beginning of stack! */
if (heap_ptr <= (char *) (STACK_START_ADDRESS - nbytes) ) {
heap_ptr += nbytes; /* Increase heap. */
return base; /* Return pointer to start of new heap area. */
} else {
return (void *) -1; /* Return -1 means that memory run out */
}
#endif // STACK_START_ADDRESS
}
언급URL : https://stackoverflow.com/questions/22422733/malloc-behaviour-on-an-embedded-system
'programing' 카테고리의 다른 글
정수를 문자 등가로 변환합니다. 여기서 0 = > a, 1 = > b 등 (0) | 2023.11.01 |
---|---|
Oracle 데이터 유형의 문자열 값을 코드로 결정하려면 어떻게 해야 합니까? (0) | 2023.11.01 |
Maria로 전환한 후 MATCH 쿼리 문제DB (0) | 2023.11.01 |
파워셸에 연관 배열이 있습니까? (0) | 2023.11.01 |
console.log가 개체의 현재 상태를 표시하도록 하려면 어떻게 해야 합니까? (0) | 2023.11.01 |