문제

다중 스레드 임베디드 소프트웨어(C 또는 C++로 작성)에서 스레드는 오버플로 없이 작업을 완료할 수 있도록 충분한 스택 공간을 제공해야 합니다.일부 실시간 임베디드 환경에서는 스택의 올바른 크기 조정이 중요합니다. 왜냐하면 (적어도 제가 작업한 일부 시스템에서는) 운영 체제가 이를 감지하지 않기 때문입니다.

일반적으로 새 스레드(메인 스레드 제외)의 스택 크기는 스레드가 생성될 때 지정됩니다(예:pthread_create() 등에 대한 인수).종종 이러한 스택 크기는 코드를 원래 작성하거나 테스트할 당시에 양호하다고 알려진 값으로 하드 코딩됩니다.

그러나 향후 코드 변경으로 인해 하드 코딩된 스택 크기의 기반이 되는 가정이 깨지는 경우가 많으며, 어느 운명적인 날 스레드가 호출 그래프의 더 깊은 분기 중 하나로 들어가 스택을 오버플로하여 전체 시스템을 다운시키는 일이 발생합니다. 또는 자동으로 메모리를 손상시킵니다.

나는 스레드에서 실행된 코드가 스택의 구조체 인스턴스를 선언하는 경우에 이 문제를 개인적으로 보았습니다.추가 데이터를 보유하기 위해 구조체가 확장되면 그에 따라 스택 크기가 확장되어 잠재적으로 스택 오버플로가 발생할 수 있습니다.나는 이것이 구조에 필드를 추가하는 전체 효과를 즉시 알 수 없는 기존 코드베이스에 큰 문제가 될 수 있다고 생각합니다(해당 구조가 사용되는 모든 장소를 찾기에는 스레드/함수가 너무 많습니다).

"스택 크기" 질문에 대한 일반적인 응답은 "이식성이 없습니다"이므로 이 조사를 위해 컴파일러, 운영 체제 및 프로세서가 모두 알려진 수량이라고 가정해 보겠습니다.또한 재귀가 사용되지 않는다고 가정하여 "무한 재귀" 시나리오 가능성을 다루지 않습니다.

스레드에 필요한 스택 크기를 추정하는 신뢰할 수 있는 방법은 무엇입니까?저는 오프라인(정적 분석) 및 자동 방법을 선호하지만 모든 아이디어를 환영합니다.

도움이 되었습니까?

해결책

런타임 평가

온라인 방법은 0xaaaa (또는 0xaa, 너비가 무엇이든)와 같은 특정 값으로 전체 스택을 페인트하는 것입니다. 그런 다음 그림이 얼마나 많이 손대지 않은지 확인하여 과거에 스택이 얼마나 큰지 확인할 수 있습니다.

살펴보십시오 이것 그림에 대한 설명 링크.

장점은 간단하다는 것입니다. 단점은 스택 크기가 테스트 중에 중고 스택의 양을 초과하지 않을 것이라고 확신 할 수 없다는 것입니다.

정적 평가

정적 검사가 있으며,이를 수행하려는 해킹 된 GCC 버전이 있다고 생각합니다. 내가 말할 수있는 유일한 것은 정적 점검이 일반적인 경우에 매우 어렵다는 것입니다.

또한 살펴보십시오 이것 의문.

다른 팁

정적 분석 도구와 같은 정적 분석 도구를 사용할 수 있습니다 Stackanalyzer, 대상이 요구 사항에 맞는 경우.

상당한 돈을 쓰고 싶다면 Klocwork와 같은 상업용 정적 분석 도구를 사용할 수 있습니다. Klocwork는 주로 소프트웨어 결함 및 보안 취약점을 탐지하는 것을 목표로합니다. 그러나 작업 또는 스레드 내에서 스택 오버플로를 감지하는 데 사용할 수있는 'KwstackoverFlow'라는 도구도 있습니다. 나는 내가 작업하는 임베디드 프로젝트에 사용하고 있으며 긍정적 인 결과를 얻었습니다. 나는 이와 같은 도구가 완벽하다고 생각하지 않지만 이러한 기업 도구가 매우 좋다고 생각합니다. 내가 제가 얻은 대부분의 도구는 기능 포인터로 어려움을 겪습니다. 또한 Green Hills와 같은 많은 컴파일러 공급 업체가 이제 컴파일러에 유사한 기능을 구축한다는 것을 알고 있습니다. 컴파일러는 스택 크기에 대한 정확한 결정을 내리는 데 필요한 모든 세부 사항에 대한 친밀한 지식을 가지고 있기 때문에 이것은 아마도 최상의 솔루션 일 것입니다.

시간이 있다면 스크립팅 언어를 사용하여 자신의 스택 오버플로 분석 도구를 만들 수 있다고 확신합니다. 스크립트는 작업 또는 스레드의 진입 점을 식별하고 완전한 기능 호출 트리를 생성 한 다음 각 기능이 사용하는 스택 공간의 양을 계산해야합니다. 완전한 기능 통화 트리를 생성하여 더 쉽게 만들 수있는 무료 도구가있을 수 있습니다. 플랫폼의 세부 사항이 스택 공간을 생성하는 것을 알고 있다면 각 기능을 사용하는 것은 매우 쉬울 수 있습니다. 예를 들어, PowerPC 함수의 첫 번째 어셈블리 명령어는 종종 기능에 필요한 양으로 스택 포인터를 조정하는 업데이트 명령이 포함 된 저장 단어입니다. 첫 번째 명령어에서 바이트로 크기를 가져갈 수 있으며, 이는 비교적 쉽게 사용되는 총 스택 공간을 결정할 수 있습니다.

이러한 유형의 분석은 모두 당신이 알고 싶은 스택 사용에 대한 최악의 사례 상한의 근사치를 제공합니다. 물론, 제가 함께 일하는 것과 같은 전문가들은 당신이 너무 많은 스택 공간을 할당하고 있다고 불평 할 수도 있지만, 좋은 소프트웨어 품질에 관심이없는 공룡입니다 :)

스택 사용을 계산하지는 않지만 다른 가능성은 프로세서의 메모리 관리 장치 (MMU)를 사용하여 스택 오버플로를 감지하는 것입니다. PowerPC를 사용하여 VXWorks 5.4 에서이 작업을 수행했습니다. 아이디어는 간단합니다. 쓰기 보호 메모리 페이지를 스택의 맨 위에 놓으십시오. 오버플로가 발생하면 프로세서 실행이 발생하고 스택 오버플로 문제에 대해 빠르게 경고됩니다. 물론, 스택 크기를 늘리는 데 얼마나 필요한지 말하지는 않지만, 디버깅 예외/코어 파일을 사용하는 것이 좋으면 적어도 스택을 오버플로 한 호출 시퀀스를 파악할 수 있습니다. 그런 다음이 정보를 사용하여 스택 크기를 적절하게 늘릴 수 있습니다.

-djhaus

무료는 아니지만 은폐 스택의 정적 분석을 수행합니다.

정적 (오프라인) 스택 점검은 어렵지 않습니다. 나는 우리의 임베디드 IDE를 위해 그것을 구현했습니다 (빠른)-현재 ARM7 (NXP LPC2XXX), Cortex-M3 (STM32 및 NXP LPC17XX), X86 및 사내 MIPS ISA 호환 FPGA 소프트 코어에서 작동합니다.

기본적으로 우리는 실행 가능한 코드의 간단한 구문 분석을 사용하여 각 기능의 스택 사용을 결정합니다. 가장 중요한 스택 할당은 각 함수의 시작 부분에서 수행됩니다. 최적화 수준이 다르고 해당되는 경우 ARM/THUMB 명령 세트 등이 어떻게 변경되는지 확인하십시오. 또한 작업에는 일반적으로 자체 스택이 있으며 ISR은 종종 별도의 스택 영역을 공유합니다!

각 기능을 사용하면 구문 분석에서 콜 트리를 구축하고 모든 기능의 최대 사용량을 계산하는 것이 매우 쉽습니다. 우리의 IDE는 귀하를 위해 스케줄러 (효과적인 얇은 RTOS)를 생성하므로 어떤 기능이 '작업'으로 지정되고 있고 ISRS인지 정확히 알고 있으므로 각 스택 영역에 대해 최악의 사용을 알 수 있습니다.

물론,이 수치는 거의 항상 실제 최고. 같은 기능을 생각하십시오 sprintf a 많은 스택 공간이지만 제공하는 형식 문자열과 매개 변수에 따라 크게 다릅니다. 이러한 상황에서는 동적 분석을 사용할 수 있습니다. 스타트 업에서 알려진 값으로 스택을 채우고 디버거에서 잠시 동안 실행하고 잠시 멈추고 각 스택의 금액이 여전히 값으로 채워져 있는지 확인하십시오 (높은 워터 마크 스타일 테스트). .

접근 방식은 완벽하지는 않지만 두 가지를 결합하면 실제 사용법이 어떻게 될지에 대한 상당히 좋은 그림을 줄 것입니다.

답변에서 논의 된 바와 같이 이 질문, 일반적인 기술은 알려진 값으로 스택을 초기화 한 다음 코드를 잠시 동안 실행하고 패턴이 정지되는 위치를 확인하는 것입니다.

이것은 오프라인 방법이 아니지만 제가 작업중인 프로젝트에는 응용 프로그램 내의 모든 작업 스택에 높은 수상 표시를 읽는 디버그 명령이 있습니다. 이것은 각 작업에 대한 스택 사용 테이블과 사용 가능한 헤드 룸의 양을 출력합니다. 많은 사용자 상호 작용으로 24 시간 실행 후이 데이터를 확인하면 정의 된 스택 할당이 "안전"이라는 확신을 갖게됩니다.

이것은 알려진 패턴으로 스택을 채우는 잘 시도 된 기술을 사용하여 작동하며, 이것이 다시 작성 될 수있는 유일한 방법은 일반 스택 사용에 의한 것이라고 가정하지만, 다른 사람에 의해 작성되는 경우 스택 오버플로는 최소한 당신의 걱정!

우리는 내 작업에서 임베디드 시스템 에서이 문제를 해결하려고 노력했습니다. 신뢰할 수있는 답변을 얻기에는 너무 많은 코드 (우리 자신의 제 3 자 프레임 워크)가 너무 많습니다. 운 좋게도, 우리의 장치는 Linux 기반이므로 모든 스레드에 2MB를 제공하고 Virtual Memory Manager가 사용을 최적화 할 수있는 표준 동작으로 돌아갔습니다.

이 솔루션의 한 가지 문제는 mlock 전체 메모리 공간에서 (성능을 향상시키기 위해 이상적). 이로 인해 스레드의 각 스레드 (75-150)에 대한 2MB의 스택이 모두 페이징되었습니다. 우리는 메모리 공간의 절반을 잃어 버릴 때까지 메모리 공간의 절반을 잃었습니다.

Sidenote : Linux의 VMM (Virtual Memory Manager)은 RAM을 4K 청크로 할당합니다. 새 스레드가 스택을 위해 2MB의 주소 공간을 요청하면 VMM은 가짜 메모리 페이지를 가장 상단 가장 많은 페이지를 제외하고 할당합니다. 스택이 가짜 페이지로 커지면 커널은 페이지 결함을 감지하고 가짜 페이지를 실제 RAM (실제 RAM의 또 다른 4K 소비)으로 바꿉니다. 이런 식으로 스레드의 스택은 필요한 모든 크기로 성장할 수 있으며 (2MB 미만인 한) VMM은 최소한의 메모리 만 사용됩니다.

이미 수행된 몇 가지 제안 외에도 임베디드 시스템에서는 스택 크기를 적절한 크기로 유지해야 하기 때문에 스택 사용량을 엄격하게 제어해야 하는 경우가 많다는 점을 지적하고 싶습니다.

어떤 의미에서 스택 공간을 사용하는 것은 메모리 할당과 비슷하지만 할당이 성공했는지 확인할 수 있는 (쉬운) 방법이 없으므로 스택 사용을 제어하지 않으면 시스템이 다시 충돌하는 이유를 파악하는 데 영원히 어려움을 겪게 됩니다.따라서 예를 들어 시스템이 스택의 로컬 변수에 메모리를 할당하는 경우 malloc()을 사용하여 해당 메모리를 할당하거나, malloc()을 사용할 수 없는 경우 자체 메모리 핸들러를 작성하세요(충분히 간단한 작업임).

아니 아니:

void func(myMassiveStruct_t par)
{
  myMassiveStruct_t tmpVar;
}

예-예:

void func (myMassiveStruct_t *par)
{
  myMassiveStruct_t *tmpVar;
  tmpVar = (myMassiveStruct_t*) malloc (sizeof(myMassicveStruct_t));
}

매우 명백해 보이지만 그렇지 않은 경우가 많습니다. 특히 malloc()을 사용할 수 없는 경우에는 더욱 그렇습니다.

물론 여전히 문제가 있을 수 있으므로 이는 도움이 될 뿐 문제가 해결되지는 않습니다.그러나 나중에 스택 크기를 추정하는 데 도움이 됩니다. 일단 스택에 적합한 크기를 찾은 다음 일부 코드 수정 후 다시 스택 공간이 부족해지면 여러 버그를 감지할 수 있거나 다른 문제(하나에 비해 호출 스택이 너무 깊음)

100% 확실하지는 않지만 이것이 또한 이루어질 수 있다고 생각합니다. JTAG 포트가 노출 된 경우 TRACE32에 연결하여 최대 스택 사용을 확인할 수 있습니다. 이를 위해서는 초기 꽤 큰 임의의 스택 크기를 제공해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top