해결된 질문
작성
·
512
1
안녕하세요.
QUEUE 실습 중 질문이 있어서 글 올립니다.
qBuffer의 ucData 배열의 크기를 2000으로 증가시켜봤습니다.
그럴 때 결과를 보면 TASK1이 한 번 받고 더 이상 실행이 안되는데요.
디버거으로 확인해보니까 qid의 주소가 0x200007a0 <ucHeap+1240>로 나오고, Queue struct내용 중 tail의 주소가 0x20002efd <ucHeap+11317>로 나오는 것을 확인할 수 있었습니다.
list 파일을 보면
bss 영역에 있는 것으로 확인이 됩니다.
xQueueCreate API를 사용하면 내부에서 pvPortMalloc를 사용해 Queue를 생성하는 것을 볼 수 있습니다.
이는 pvPortMalloc을 사용하면 bss영역에 할당?이 된다라고 볼 수 있죠. 초반 (FreeRTOS 수업 내용 중 bss영역에 할당되는 것을 확인할 수 있음)
두 가지 질문이 있는데요.
1. 링커 스크립트에서 bss 섹션을 아래 그림처럼 설정을 했는데,
만약 pvPortMalloc API를 많이 사용하여 bss영역이 부족하면 제가 bss섹션의 크기를 늘려주면 되나요?
2. 생성된 Queue의 주소와 크기를 보면 bss섹션 내에 잘 있는데 왜 동작을 안하는건지 이해가 되지 않습니다.
메모리 오버플로우 문제인가 했는데, 디버그로 확인해보니 오버플로우 문제는 아닌 것 같아서요.
답변 부탁드립니다. 감사합니다.
답변 4
0
김상현님 덕분에 버그를 찾게되었네요.
결론은 스택오버플로우입니다.
/* ...........................................................................
*
* 메시지큐 & 사용자 정의 블럭 정의
* ===================
*/
QueueHandle_t qid;
typedef struct tag_qBuffer {
char ucMessageID;
char ucData[100]; <- - - - - - 스택오버플로우 발생원인을 제공
}qBuffer;
배열의 요소수를 20이상을 사용하면 하단 그림과 같은 현상이 나타납니다.
스택의 후미 부분에 당연히 보여야 할 인식패턴(0xA5A5A5A5)이 모두 사라져버린 것을 알수있죠. 스택오버플로우 현상입니다.
이 문제를 찾아내기 위해 아래 소개하는 도구를 이용하여 태스크별 스택영역을 확인하였습니다.
STM32CubeIDE 는 자체적으로 RTOS 을 인식한 유용한 디버깅 도구(RTOS Awareness debugging tool)를 지원해주고 있는데요
이 방법을 이용하면 현재 RTOS 프로그램 실행중 상태에 대한 주요 정보를 볼 수 있습니다.
디버깅 모드에서 상단메뉴 WIndow > Show View > FreeRTOS > FreeRTOS Task List 입니다
▼ 여기서 실제 스택의 유효 영역을 확인해주고요
</br>
코드를 다음과 같이 수정하였습니다.
▼ 메시지큐에 데이터를 넣기 직전 스택 프레임 모습입니다
▼ 메시지큐에 데이터를 넣은 이후 스택 프레임 모습입니다
이제 비로소 안정적으로 동작하는 것을 볼 수 있습니다:)
결론입니다.
아래 소스 코드의 예처럼 배열요소수를 20 미만으로 설정해야 합니다.
/* ...........................................................................
*
* 메시지큐 & 사용자 정의 블럭 정의
* ===================
*/
QueueHandle_t qid;
typedef struct tag_qBuffer {
char ucMessageID;
char ucData[16]; <- - - - - - 스택오버플로우가 발생하지 않도록 수정
}qBuffer;
이미 배포한 소스코드에 이 부분을 패치하도록 하겠습니다.
감사합니다.
김상현님!
스택오버플로우!!! 이 단어하나로 이미 답변이 된 것으로 알았네요. ㅎㅎ
그럼 요청하신 답변드립니다.
금번 이슈 디버깅하는 과정에서 하신 몇가지 질문은 다음과 같았습니다.
1. 링커 스크립트에서 bss 섹션을 아래 그림처럼 설정을 했는데,
만약 pvPortMalloc API를 많이 사용하여 bss영역이 부족하면 제가 bss섹션의 크기를 늘려주면 되나요?
[ANS] BSS 영역은 전역변수 공간이라는 점을 고려하면 김상현님이 특별한 노력을 하지 않으시더라도, 위 링커 스크립트의 설정에 따라 램의 크기가 허용하는 한 전역변수를 많이 선언할 수록 BSS 영역은 자동적으로 늘어납니다.
</br>
2. 여기서 이해가 안되는 것이 있는데 sp는 변하지 않는데 한줄씩 실행하면 0x20000DC8 번지보다 낮은 주소에 있는 값들이 왜 변하는 것인지 궁금합니다. 변하면 sp도 -4씩 낮아져야하지 않나요?
[ANS] 배열의 크기를 100으로 테스트 하신 것 같네요. 제가 코드 베이스를 김상현님이 한 것과 동일한 조건으로 만들어놓고 디버깅해보았습니다.
배열크기를 100으로 하고, 각 태스크의 스택 크기를 512바이트로 하였을때, 스택영역은 다음과 같죠.
스택의시작 스택의끝
task1 0x200009f0 0x20000bf0
task2 0x20000c58 0x20000e58
xQueueSend API 함수 호출전에 이미 스택은 오버플로우 된 것이 확인됩니다. 바로 그렇기 때문에 그런식으로 이해할 수 없는 상황이 생긴것이죠. 따라서 말씀하신 스택프레임에서 보이는 이상한 현상은 해석이 무의미합니다. 이미 훼손된 메모리이기 때문이죠.
</br>
3. xQueueSend 가 호출되기 전 0x20000DD4 주소의 값이 변하는데, 이 값이 변하는 이유를 모르겠습니다.
Full descending으로 알고 있어서 높은 주소에서 낮은 주소로 sp가 변할텐데 0x20000DD8 번지는 sp보다 높은 주소라서요.
시스템은 아래 캡쳐한 곳에서 멈춰버립니다. 참고 부탁 드립니다. (queue.c 파일 내 코드 라인 812)
[ANS] char ucData[0] 변수는 루프문을 돌면서 자연스럽게 변경되는 것이 맞습니다.
typedef struct tag_qBuffer {
char ucMessageID; <- - - - - 이 변수의 주소(0x20000DD4)
char ucData[100]; <- - - - - 이 변수의 주소(0x20000DD5)
}qBuffer;
</br>
그리고, 마지막으로 놀라운 사실을 하나 더 알려드리자면
충분하게 잡아놓았다고 생각한 스택영역의 크기 512는 결정적으로 스택공간을 많이 사용하는 printf 때문에 한방에 무너진것으로 추정됩니다.
김상현님의 코드에서 printf 문을 모두 주석처리해보시면 프로그램이 잘 동작하는 것을 보실 수 있을거에요.
이것은 xQueueSend API 함수 호출전에 이미 스택은 오버플로우 된것이 관찰되었다고 말씀드린것과 일맥상통하는 것이죠.
여기까지입니다.
이 글이 도움이 되셨다면 좋겠습니다.^^
[ANS] char ucData[0] 변수는 루프문을 돌면서 자연스럽게 변경되는 것이 맞습니다.
아하 ucData가 변경되었던거군요. 그 생각을 못했네요.
답변 정말 감사합니다!
0
ucData 배열 크기가 100일 때, qBuffer xMessage를 전역 변수로 선언하면 정상적으로 실행이 됩니다.
말씀하셨던 스택포인터 실행 궤적을 추적해보려고 하지만 좀 힘드네요.
결론부터 말씀 드리면 Task2 내부에서 xQueueSend API에서 시스템이 멈춰버립니다.
Task2의 sp가 0x20000dc8 인 것을 확인할 수 있었습니다.
메모리를 0x20000dc8로 설정하고 한줄씩 실행하면서 xQueueSend API 호출 전까지 실행했을 때의 결과입니다.
1. 여기서 이해가 안되는 것이 있는데 sp는 변하지 않는데 한줄씩 실행하면 0x20000DC8 번지보다 낮은 주소에 있는 값들이 왜 변하는 것인지 궁금합니다. 변하면 sp도 -4씩 낮아져야하지 않나요?
2. xQueueSend 가 호출되기 전 0x20000DD4 주소의 값이 변하는데, 이 값이 변하는 이유를 모르겠습니다.
Full descending으로 알고 있어서 높은 주소에서 낮은 주소로 sp가 변할텐데 0x20000DD8 번지는 sp보다 높은 주소라서요.
시스템은 아래 캡쳐한 곳에서 멈춰버립니다. 참고 부탁 드립니다. (queue.c 파일 내 코드 라인 812)
답변 부탁 드립니다. 감사합니다.
0
김상현님!
메시지큐 실습에 관련된 질문 주셨네요.
적어주신 글에 따르면 태스크 스택의 크기를 512바이트(128 워드)로 하셨네요.
아래 예제처럼 구현하셨다면 일단 스택오버플로우의 위험이 존재하는 것은 맞습니다.
void Task1( void *pvParameters )
{
const char *pcTaskName = "Task1";
qBuffer xMessage; <- - - - - - - - - - - - - - 이 부분이 문제가 생기는 것 맞습니다.
BaseType_t p;
현재 지역변수로 선언하신 qBuffer xMessage 을 전역변수로 변경하신 후 프로그램을 실행하였을 때는 정상적으로 동작하는지 확인해보세요.
만약 이때 프로그램이 정상적으로 실행된다면, 이제는 스택오버플로우를 의심해봐야겠죠.
강의 FreeRTOS 프로그래밍 > 스택오버플로우-Assertion 을 참고하여
스택오버플로우의 실제 발생유무를 확인해보거나, 태스크 스택포인터의 실행 궤적을 추적해보시는 것을 추천드려요. 방법은 간단합니다. 태스크 함수내의 모든 소스코드 라인 하나하나 빠짐없이 중단점(BREAKPOINT)을 설정하시고 실행(RESUME) 를 하시면서 문제가 발생하는 곳을 찾아보세요.
원인을 찾게되시면 이곳에 다시 글 남겨주세요.
0
질문 2에 대한 해답은 찾은 것 같습니다. 좀 멍청했네요.
xTaskCreate( (TaskFunction_t)Task2, "Task2", 128, (void*)Param, TASK_2_PRIO, &xHandle2 ); 에서 Task의 스택 사이즈를 256 바이트로 설정했는데,
Task 내부에서 지역 변수로 256 바이트를 초과하여 사용하니까 스택 오버플로우가 발생한 것 같습니다. 맞나요?
감사합니다. 정말 많은 도움이 되었어요.
하지만 제 질문에 대한 답은 없는 것 같네요. 답변 부탁 드려도될까요? ㅠㅠ