RTOS가있는 RAM 제한 임베디드 마이크로 컨트롤러 용 C 프로그래밍입니다.좋은 디자인으로 스택 공간을 보존하는 방법은 무엇입니까?
정기적으로 내 코드를 짧은 함수로 나눕니다. 그러나 모든 함수 호출은 더 많은 메모리를 스택해야합니다. 모든 작업에는 스택이 필요하며 이는 프로젝트의 중요한 메모리 사용자 중 하나입니다.
메모리를 보존하고 코드를 잘 구성하고 읽을 수있는 대안이 있습니까?
RTOS가있는 RAM 제한 임베디드 마이크로 컨트롤러 용 C 프로그래밍입니다.좋은 디자인으로 스택 공간을 보존하는 방법은 무엇입니까?
정기적으로 내 코드를 짧은 함수로 나눕니다. 그러나 모든 함수 호출은 더 많은 메모리를 스택해야합니다. 모든 작업에는 스택이 필요하며 이는 프로젝트의 중요한 메모리 사용자 중 하나입니다.
메모리를 보존하고 코드를 잘 구성하고 읽을 수있는 대안이 있습니까?
봅니다, d()
를 호출 c()
를 호출 b()
를 호출 a()
너무 대신, 호출 스택 아첨을 a()
전화 b()
, c()
및 d()
자체를 가지고있다.
함수가 한 번만 참조되는 경우 해당 컴파일러가 이것을 지원한다고 가정하면 inline
으로 표시하십시오.
최적화, 특히 공격적인 인라이닝을 사용합니다. 컴파일러는 호출을 최소화하기 위해 메소드를 인라인 할 수 있어야합니다. 사용하는 컴파일러 및 최적화 스위치에 따라 일부 메서드를 inline
으로 표시하면 도움이 될 수 있습니다 (또는 무시 될 수 있음).
GCC의 경우 "-finline-functions"(또는 -O3) 플래그와 "-finline-limit = n"플래그를 추가하십시오.
많은 메인 메모리를 절약 할 수 있지만 작은 스택 조각이있는 경우 정적 할당을 평가하는 것이 좋습니다.
C에서 함수 내에서 선언 된 모든 변수는 자동으로 관리되므로 스택에 할당됩니다.
선언을 "정적"으로 한정하면 스택 대신 주 메모리에 저장됩니다. 그들은 기본적으로 전역 변수처럼 행동하지만 전역 변수를 과도하게 사용하는 나쁜 습관을 피할 수 있습니다. 크고 수명이 긴 버퍼/변수를 정적으로 선언하여 스택의 부담을 줄이는 좋은 사례를 만들 수 있습니다.
응용 프로그램이 다중 스레드이거나 재귀를 사용하는 경우에는 잘 작동하지 않으므로주의하십시오.
일반적으로 정적 할당을 위해 스택 RAM과 RAM의 질적 인 차이는 없습니다. 링커 제어 파일과 같은 것을 통해 할당을 제어해야합니다. 온칩 RAM 및 별도의 외부 RAM과 같이 여러 RAM 뱅크가있는 복잡한 프로세서가있는 경우가 아니면 –
로컬 변수 중 일부를 대체 할 수 있습니까? 특히 배열은 스택을 먹을 수 있습니다.
상황에 따라 함수간에 일부 전역을 공유 할 수있는 경우 메모리 발자국을 줄일 수있는 기회가 있습니다.
트레이드 오프 비용은 복잡성이 증가하고 기능간에 원하지 않는 부작용의 위험이 커지며 메모리 발 인쇄량이 줄어들 수 있습니다.
함수에 어떤 종류의 변수가 있습니까? 어떤 크기와 제한이 있습니까?
컴파일러에 따라, 그리고 최적화 옵션이 얼마나 적극적인지에 따라, 사용자가 모든 함수 호출에 대해 스택 사용을 갖게됩니다. 따라서 시작하려면 함수 호출의 깊이를 제한해야 할 것입니다. 일부 컴파일러는 스택 사용량을 줄이는 간단한 함수 대신 분기 대신 점프를 사용합니다. 물론 어셈블러 매크로를 사용하여 직접 함수 호출이 아닌 함수로 이동할 수 있습니다.
다른 답변에서 언급했듯이 인라인은 코드 크기가 커지더라도 사용할 수있는 옵션 중 하나입니다.
스택을 먹는 다른 영역은 로컬 매개 변수입니다. 이 영역에는 통제권이 있습니다. 정적 (파일 레벨) 통계를 사용하면 정적 램 할당 비용을 들여 스택 할당을 피할 수 있습니다. 마찬가지로 전세계.
극단적 인 경우에 고정 된 수의 전역 변수를 스택의 로컬 대신 임시 저장소로 사용하는 함수에 대한 규칙을 생각해 볼 수 있습니다. 까다로운 부분은 동일한 전역 변수를 사용하는 함수가 동시에 호출되지 않도록하는 것입니다. (따라서 규칙)
당신의 스택 사용에 3 개 요소가 있습니다
의 핵심은 스택 사용을 최소화하는 것은 매개 변수 전달 및 자동 변수를 최소화하는 것입니다. 실제 함수 호출 자체의 공간 소비는 다소 적습니다. 파라미터 문제를 해결하기
파라미터
한가지 방법은 파라미터 대신에 다수의 (포인터를 통하여) 구조체를 통과하는 것이다.
foo(int a, int b, int c, int d)
{
...
bar(int a, int b);
}
대신 이렇게 :
struct my_params {
int a;
int b;
int c;
int d;
};
foo(struct my_params* p)
{
...
bar(p);
};
이 전략은 좋은 당신이 매개 변수를 많이 아래로 전달하는 경우. 매개 변수가 모두 다르면 잘 작동하지 않을 수 있습니다. 많은 다른 매개 변수를 포함하는 큰 구조가 전달 될 것입니다. 이 경향
자동 변수 (지역 주민)
는 스택 공간의 가장 큰 소비자가 될 수 있습니다.
모든 로컬 변수를 로컬 범위에서 모듈 범위로 간단히 이동하려는 경우에는 아무 공간도 저장하지 않은 것입니다. 데이터 세그먼트 공간을 위해 스택 공간을 교환했습니다.
일부 RTOS는 스레드 단위로 "전역"저장소를 할당하는 스레드 로컬 저장소를 지원합니다. 이렇게하면 작업별로 여러 개의 독립적 인 전역 변수를 가질 수 있지만 코드가 단순하지는 않습니다.
임베디드 설정에서 코드의 스택 요구 사항을 평가하기위한 어딘가에있는 트릭은 알려진 패턴으로 시작하는 스택 공간을 채우는 것입니다 (16 진수의 DEAD가 가장 좋아함). 잠시.
정상 실행 후 스택 공간을 읽고 작동 중에 스택 공간이 대체되지 않았는지 확인하십시오. 운동을하지 않을 수도있는 모호한 코드 경로를 다루기 위해 최소한 150 %를 남겨 두도록 디자인하십시오.
아니요, 그렇지 않습니다. 제 요점은 당신이 100 % 코드 커버리지를 달성하지 못할 수도 있고 몇 가지 코드 경로가 누락 될 수 있다는 것입니다. 내가 따라야 할 엄지 손가락의 규칙. –
스택 공간을 보존해야하는 경우 더 나은 컴파일러 또는 더 많은 메모리를 가져야합니다.
소프트웨어는 일반적으로 증가 할 것이므로 (새로운 기능 등) 스택 공간을 보존하는 방법에 대해 생각함으로써 프로젝트를 시작해야하는 경우 처음부터 운명적입니다.
예, RTOS는 작업 스택 사용을 위해 RAM을 실제로 먹을 수 있습니다. 내 경험으로는 RTOS의 새로운 사용자로서 필요한 것보다 많은 작업을 사용하는 경향이 있다는 것입니다.
RTOS를 사용하는 임베디드 시스템의 경우 RAM은 귀중한 상품이 될 수 있습니다. RAM을 유지하려면 간단한 기능을 위해 협업 멀티 태스킹 설계로 라운드 로빈 방식으로 실행되는 한 가지 태스크에서 여러 기능을 구현하는 것이 효과적 일 수 있습니다. 따라서 총 작업 수를 줄이십시오.
나는 당신이 여기에 존재하지 않는 문제를 상상할 것이라고 생각합니다. 대부분의 컴파일러는 자동 변수를 스택에 "할당"할 때 실제로 아무 것도하지 않습니다.
스택은 "main()"이 실행되기 전에 할당됩니다. 함수 a()에서 b() 함수를 호출하면 a가 사용한 마지막 변수 바로 다음의 저장 영역 주소가 b()로 전달됩니다. b()가 함수 c()를 호출하면 b()에 의해 정의 된 마지막 자동 변수 다음에 c의 스택이 시작되면 b()의 스택이 시작됩니다.
스택 메모리가 이미 있고 할당되어 있으므로 초기화가 수행되지 않으며 관련된 유일한 처리는 스택 포인터를 전달하는 것입니다.
문제가되는 것은 세 가지 기능 모두에서 대량의 저장소를 사용하는 경우 스택이 세 가지 기능의 메모리를 모두 수용해야한다는 것입니다. 호출 스택 맨 아래에 많은 양의 저장 공간을 할당하는 기능을 유지하십시오. 즉, 호출 스택의 다른 기능을 호출하지 마십시오.
메모리로 제한된 시스템의 또 다른 트릭은 함수의 메모리 호깅 부분을 별도의 자체 포함 함수로 분리하는 것입니다.
+1 인라인 .... 현대 컴파일러가 자동으로 이것을 수행하는지 궁금하십니까? – kenny