FreeRTOS에는 메모리관리 기능이 제공된다!! 아래내용은 최근 FreeRTOS를 심층(?) 분석해 실무에 적용하기 위해 기록을 남긴 내용이다. 다소 부족하고 정리가 안될 수도 있다. 질문, 반박은 언제나 환영이다.
메모리 정적/동적 옵션 설정
- 정적(static)으로 메모리 영역을 할당하기 위해서는
configSUPPORT_STATIC_ALLOCATION
옵션을set
해야 한다. - 동적(dynamic)으로 메모리 영역을 할당하기 위해서
configSUPPORT_DYNAMIC_ALLOCATION
옵션을set
해야 한다. 1로 설정하지 않아도 관계 없다. - 두 가지 옵션을 모두 1로 설정하는 것도 가능하다.
메모리 할당(malloc)/해제(Free)
- malloc() 대신
pvPortMalloc()
- free() 대신
pvPortFree()
FreeRTOS에서 제공하는 memory allocation schemes
FreeRTOS에서는 heap_n.c(n = 1~5) 파일을 제공한다. 구현된 함수의 이름은 모두 동일하지만 구현 내용(알고리즘)은 모두 다르기 때문에 상황에 적합한 파일을 적용해야 한다.
stm32cubemx의 software Packs Comopnent selector에서 선택 가능
CMSIS RTOS2 -> RTOS2 -> Heap에서 선택 가능, 실제 코드 경로는 아래와 같다.
\STM32Cube\Repository\Packs\STMicroelectronics\X-CUBE-FREERTOS\1.2.0\Middlewares\Third_Party\FreeRTOS\Source\portable\MemMang
일반적으로 Heap_4.c를 많이 사용한다.
Heap_1.c
가장 기본적인 기능인 pvPortMalloc()
만 구현되어 있다. pvPortMalloc()을 통해 메모리를 할당하게 되면 순차적으로 메모리를 할당한다.
heap의 시작 위치가 0x20000000
이고, TCB와 Stack이 하나의 Task로 0x1000
만큼의 공간을 차지한다고 가정해 보자.
파란색 Task의 시작 위치는 0x20000000
이고 0x1000
만큼의 공간을 차지하기 때문에 다음에 저장된 초록색 Task는 0x20001001
부터 저장돼 0x20002000
까지 메모리 공간을 차지하게 된다.
Heap_2.c
Heap_2.c는 향상된 기능을 포함해 Heap_4.c로 대체되었기 때문에 사용을 권장하지 않는다. 다만 이전 버전과의 호환성을 위해 보관하고 있다.
Heap_3.c
malloc()과 free()로 메모리 할당/해체하고, 메모리를 제어하기 전에 모든 scheduling을 일시적으로 중단해 thread로부터 메모리 제어 동작을 보호하도록 한다.
Heap_4.c
Heap_4.c에서는 메모리를 할당하기 위해 [[../../first-fit|first-fit]] 알고리즘을 사용한다.
- A : 3개의 Task가 Heap 영역에 할당되어 있고(파란색부터 순서대로 1, 2, 3), 충분한 메모리 공간이 남아있음을 나타내고 있다.
- B : 2번 Task가 해체되어 빈 공간이 되었음을 나타낸다.
- C : Queue를 만들어 메모리 공간을 할당한 상황을 나타낸다. Queue가 Task 2번이 차지하고 있던 공간에 할당될 수 있는 크기라면 그곳에 할당하고 남은 영역은 다시 분리하도록 처리한다.
- D : 추가로 사용자(User)가 메모리 영역을 할당했는데 그 크기가 Task 3 - Queue 보다 작으면 Task 3와 Queue 사이의 메모리 영역에 할당된다.
- E ~ F : Queue와 User가 삭제되었을 때 메모리 공간의 상태를 나타낸다.
Heap_4.c는 메모리를 할당할 때 최적의 빈 공간을 찾아서 할당하고 남은 영역은 다시 분리시켜 새로운 메모리 block으로 처리하도록 한다.
Heap_5.c
Heap_5.c는 Heap_4.c와 동일하게 [[../../first-fit|first-fit]] 알고리즘을 사용한다. Heap_5.c는 메모리 영역을 분리해서 사용해야 하는 경우 여러 곳의 메모리 영역을 마치 하나의 연속된 메모리와 같이 사용할 수 있도록 한다.
Heap_5를 사용하기 전에는 vPortDefineHeapRegions()
함수를 호출해 초기화 후 사용해야 한다.
#define RAM1_START_ADDRESS ((uint8_t*) 0x00010000)
#define RAM1_SIZE (64 * 1024)
#define RAM2_START_ADDRESS ((uint8_t*) 0x00020000)
#define RAM2_SIZE (32 * 1024)
#define RAM3_START_ADDRESS ((uint8_t*) 0x00030000)
#define RAM3_SIZE (32 * 1024)
typedef struct {
uint8_t* pucStartAddress;
size_t xSizeInBytes;
} HeapRegion_t; /*해당 구조체는 portable.h에 정의*/
const HeapRegion_t xHeapRegions[] =
{
{RAM1_START_ADDRESS, RAM1_SIZE},
{RAM2_START_ADDRESS, RAM2_SIZE},
{RAM3_START_ADDRESS, RAM3_SIZE},
{NULL, 0}
};
int main( void )
{
vPortDefineHeapRegions( xHeapRegions );
}
위 코드에서 메모리 주소와 사이즈는 아래 그림과 같이 정의되었다.
사용자가 직접 Heap 영역 지정하기
configAPPLICATION_ALLOCATED_HEAP
설정을 set 한
다음 배치할 메모리 영역을 지정해 줘야 한다.
uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] _attribute__ ( ( section( ".my_heap" ) ) );
여기서 ".my_heap"은 .ld 파일에서 수정해줘야 한다. 아래 예시는 heap 영역을 0x200001000 번지부터 시작하도록 하는 예시다.
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.text :
{
*(.text)
*(.text*)
} > FLASH
/* Define the .my_heap section starting at a specific address within RAM */
.my_heap (NOLOAD) :
{
KEEP(*(.my_heap))
} > RAM AT> 0x20001000 /*이 위치에 RAM 이 오게 된다면 0x20000000 부터 시작*/
}
Heap 관련 유틸리티 및 매크로
- Heap의 여유 공간을 byte수로 반환
size_t xPortGetFreeHeapSize();
- FreeRTOS 실행 이후에 Heap의 용량이 가장 적게 남았었던 시점의 값(byte)을 반환
size_t xPortGetMinimumEverFreeHeapSize();
- Heap의 상태를 매개변수를 통해 전달하는 함수
typedef struct xHeapStats { /*현재 사용 가능한 크기*/ size_t xAvailableHeapSpaceInBytes; /*여유 공간 중 가장 큰 블록의 크기*/ size_t xSizeOfLargestFreeBlockInBytes; /*여유 공간 중 가장 작은 블록의 크기*/ size_t xSizeOfSmallestFreeBlockInBytes; /*여유 공간 블록 수*/ size_t xNumberOfFreeBlocks; /*용량이 가장 적게 남았었던 시점의 값*/ size_t xMinimumEverFreeBytesRemaining; /*pvPortMalloc() 함수 호출 수*/ size_t xNumberOfSuccessfulAllocations; /*vPortFree() 함수 호출 수*/ size_t xNumberOfSuccessfulFrees; } HeapStats_t;
void vPortGetHeapStats( HeapStats_t * pxHeapStats );
- pvPortMalloc()을 호출해 메모리 할당을 시도해서 실패한 경우 NULL을 반환하지만 `configUSE_MALLOC_FAILED_HOOK`설정을 `set`하면 메모리 할당 실패 시 callback을 받을 수 있다.
```cpp
void vApplicationMallocFailedHook(void);
- 고속 메모리에 Task stack 배치하기
Task 생성 과정(vTaskCreate)에서 TCB_t와 Task에 할당된 stack을 Heap에 함께 할당한다. Task에서 stack은 매우 빈번하게 사용되는 메모리 영역으로 고속의 메모리에 할당하면 성능이 향상된다. 성능 향상을 목적으로 stack을 별도의 메모리 영역(SRAM 등 고속 메모리)에 할당하려면configSTACK_ALLOCATION_FROM_SEPARATE_HEAP
옵션을set 한
다음 아래 함수에서 사용자가 직접 메모리 할당 기능을 구현해야 한다.
옵션을 활성화시키면 Task 생성 과정에서void *pvPortMallocStack(size_t size); void vPortFreeStack( void * pv );
pvPortMalloc() 함수
대신pvPortMallocStack
함수를 호출한다.
'OS(운영체제) > FreeRTOS' 카테고리의 다른 글
7. Free RTOS - Data Types and Coding Style Guide (0) | 2022.11.09 |
---|---|
6. Free RTOS - Task 제어(with STM32L475VGT, B-L475E-IOT01A1) (0) | 2022.11.09 |
5. Free RTOS - Task 상태와 동작 (0) | 2022.11.08 |
4. Free RTOS - Task 생성하기, Multi Task(with STM32L475VGT, B-L475E-IOT01A1) (0) | 2022.11.07 |
3. Free RTOS - CMSIS_RTOS vs FreeRTOS (0) | 2022.11.02 |