[씨] 임베디드 시스템 프로그래밍을위한 느슨한 결합 패턴 [닫기]
우리는 비슷한 상황에 있습니다. 이러한 문제를 해결하기 위해 원하는 인터페이스 (사용 된 구현은 컴파일 대상의 기능)의 여러 구현을 지원하고 이식 가능한 래퍼에 포함되지 않은 API 기능의 사용을 피하는 빌드 시스템을 구현했습니다. 래퍼 정의 #include
는 구현 별 헤더 파일 인 .h 파일에 있습니다. 다음 모형은 세마포어 인터페이스를 처리하는 방법을 보여줍니다.
#ifndef __SCHEDULER_H
#define __SCHEDULER_H
/*! \addtogroup semaphore Routines for working with semaphores.
* @{
*/
/* impl/impl_scheduler.h gets copied into place before any file using
* this interface gets compiled. */
#include "impl/impl_scheduler.h"
/* semaphore operation return values */
typedef enum _semaphoreErr_e
{
SEMAPHORE_OK = impl_SEMAPHORE_OK,
SEMAPHORE_TIMEOUT = impl_SEMAPHORE_TIMEOUT
} semaphoreErr_e;
/*! public data type - clients always use the semaphore_t type. */
typedef impl_semaphore_t semaphore_t;
/*! create a semaphore. */
inline semaphore_t *semaphoreCreate(int InitialValue) {
return impl_semaphoreCreate(InitialValue);
}
/*! block on a semaphore. */
inline semaphoreErr_e semaphorePend(semaphore_t *Sem, int Timeout) {
return impl_semaphorePend(Sem, Timeout);
}
/*! Allow another client to take a semaphore. */
inline void semaphorePost(semaphore_t *Sem) {
impl_semaphorePost(Sem);
}
/*! @} */
#endif
공용 API는 사용을 위해 문서화되어 있으며 컴파일 시간까지 구현이 숨겨집니다. 이러한 래퍼를 사용하면 오버 헤드가 발생하지 않아야합니다 (컴파일러에 따라 다를 수 있음). 하지만 순전히 기계적인 타이핑이 많이 있습니다.
-------------------xDAIS 알고리즘 표준을 살펴볼 수 있습니다. DSP 애플리케이션 용으로 설계되었지만 아이디어를 저자 원 임베디드 설계로 조정할 수도 있습니다.
http://en.wikipedia.org/wiki/XDAIS_algorithms
간단히 말해 xDAIS는 C 언어 용 COM과 다르지 않은 OOP 스타일 인터페이스 규칙입니다. 모듈이 함수 포인터 구조를 통해 구현할 수있는 고정 된 인터페이스 세트가 있습니다.
인터페이스는 엄격하게 정의되어 있으므로 구성 요소를 교환하고 함께 쌓아 더 높은 수준의 기능을 구축하는 등의 작업이 매우 쉽습니다. 인터페이스 (및 코드 검사기)는 또한 모든 구성 요소가 분리되어 있는지 확인합니다. 코드 검사기를 사용하면 다른 구성 요소의 개인 함수를 직접 호출하는 구성 요소를 작성할 수 없습니다.
메모리 할당은 일반적으로 초기화 시간과 시스템 설계자의 제어하에 수행됩니다 (모든 구성 요소가 구현해야하는 기본 인터페이스의 일부입니다).
정적 및 동적 할당 전략이 가능합니다. 모든 구성 요소가 다른 메모리 주소로 재배치 될 수 있어야하므로 메모리 조각화 위험없이 동적으로 전환 할 수도 있습니다.
xDAIS 표준은 상속을위한 매우 간결한 OOP 스타일 메커니즘을 정의합니다. 이것은 디버깅 및 로깅 목적으로 매우 편리합니다. 재미있는 일을하는 알고리즘이 있나요? 기존 알고리즘 주위에 간단한 단일 파일 래퍼를 추가하고 모든 호출을 UART 등에 기록합니다. 엄격하게 정의 된 인터페이스로 인해 모듈 작동 방식과 매개 변수 전달 방식을 추측 할 수 없습니다.
나는 과거에 xDAIS를 사용해 왔으며 잘 작동합니다. 익숙해지는 데는 시간이 걸리지 만 플러그 앤 플레이 아키텍처의 이점과 디버깅 용이성은 초기 노력보다 큽니다.
-------------------여기서 답변을 시작하겠습니다. 다른 것이 떠오르면이 문제가 흥미 롭기 때문에 여기로 돌아올 것입니다. 나는 또한 다른 답변을 위해이 질문을 모니터링 할 것입니다.
별도의 논리 및 실행 :
임베디드 시스템은 대규모 비즈니스 애플리케이션과 동일한 유형의 로직 및 I / O 분리의 이점을 누릴 수 있습니다.
예를 들어 값을 읽고 해석하고 이러한 판독 값을 기반으로 변경하는 일부 임베디드 장치에 대해 코딩하는 경우 "로직"부분을 실제로 네트워크, 하드웨어와 통신하는 부분에서 완전히 분리 할 수 있습니다. 사용자 또는 외부 엔티티.
어떤 종류의 메모리 구조 나 C 코드에서 "규칙"을 완전히 설명 할 수있을 때 메시지 전달 루틴이나 이와 유사한 것 외에는 아무 것도 연결하지 않고 설명하려고합니다. 요컨대 부작용을 줄이면 코드가 더 모듈화됩니다.
당신이 쓰레드를 사용하고 있는지 아닌지는 모르겠지만, 어느 쪽이든 proto 쓰레드 는 비슷한 추상화를 제공하고 쓰레드보다 덜 강력하지만 프로그래머를 혼동 할 가능성이 훨씬 적습니다.
Amiga에서 자라면서 나는 그것을 잊기가 힘들다. ROM 가능 운영 체제이지만로드 가능한 라이브러리를 통해 RAM에서 쉽게 확장됩니다. 타이트한 코드와 빠른 메시지 모두 에 포인터 전달을 많이 사용 합니다.
Nils Pipenbrinck의 또 다른 답변을 읽으면서 xDAIS를 사용 하라는 그의 제안 은 이것을 구현하는 좋은 방법으로 보입니다. 대부분의 코드가 이와 같은 메시지 규칙을 사용하는 경우 코드가 모듈 식이고 유지 관리가 가능할 가능성이 있습니다.
또한 타겟을 위해 컴파일하기 전에 코드에서 실행중인 전 처리기 또는 전 처리기 패스를 가져 오지만 회색 영역으로 이동합니다. 이는 언어 전환과 거의 같고 요구 사항은 C였습니다.
-------------------OOP 모방에 대해 언급 한 것은 좋은 방법입니다. 표준을 아는 것 이상으로 어떻게 수행하는지에 대한 아이디어를 드릴 수 있습니다. 실제로 몇 가지 세부 사항을 처리하여 사용하고 있습니다.
- 내부 모듈 구조 숨기기.
- 구조 크기를 애플리케이션에 노출하여 정적 할당을 허용합니다.
- 콜백 인터페이스를 노출하여 다른 모듈에서 기능을 차용합니다.
- 각 모듈을 자체적으로 컴파일 가능하게 유지합니다.
- 마지막으로, 코드를 읽기 쉽고 사용하기 쉽고 수정하기 쉽게 유지하십시오.
@ my_module.c
typedef struct _s_class
{
uint32_t an_attribute;
void (*required_behavior)(uint32_t);
} class_t;
void obj_init(void * obj, void(*req_beh_callback)(uint32_t))
{
((class_t*)obj)->an_attribute = 0;
((class_t*)obj)->required_behavior = req_beh_callback;
}
void obj_method1(void* obj)
{
((class_t*)obj)->an_attribute++;
required_behavior(((class_t*)obj)->an_attribute);
}
size_t get_object_size()
{
return sizeof(class_t);
}
@ my_module.h
void obj_init(void * obj, void(*req_beh_callback)(uint32_t));
void obj_method1(void* obj);
size_t get_object_size();
// run get_object_size() once to get the number
// that goes in this macro. may differ between CPU
// architectures.
#define OBJECT_SIZE 4
@ my_application.c
#include "my_module.h"
uint8_t my_object[OBJECT_SIZE]; // static allocation :)
void callback_for_obj(uint32_t i)
{
... do stuff ...
}
int main()
{
obj_init(my_object, callback_for_obj);
obj_method1(my_object);
return 0;
}
제안이나 질문이 있으면 알려주세요. 더 많은 것을 배우는데도 도움이됩니다!
-------------------작은 장치를 많이 사용하지는 않지만 메모리 제약이있는 장치도 있습니다. 정적 버퍼를 할당하고 있지만 때때로 동적 메모리 할당이 실제로 메모리 사용량을 줄이는 데 도움이된다는 것을 발견했습니다. 힙 크기 및 할당 정책을 엄격하게 제어하고 메모리 부족 상태를 오류가 아닌 정상 작동으로 확인하고 처리해야합니다. 예를 들어 메모리가 부족하여 보유한 데이터를 보내고 버퍼를 지우고 남은 곳에서 작업을 다시 시작합니다.
C ++로 전환하지 않는 이유는 무엇입니까? 나는 싶습니다. 주로 다음과 같은 이유로 전환하지 않습니다.
- 우리 코드 원숭이는 그것을 괴롭히지 않을 것이며 배우기를 꺼려합니다.
- C ++ 라이브러리는 훨씬 더 큽니다 (이 문제를 해결할 수는 있지만).
- 정말 작은 RTOS 장치의 경우 일반적으로 필요하지 않습니다. 임베디드 Linux를 실행하는 더 큰 장치의 경우 좋을 것입니다.
고정 레이아웃이 원하는 것임을 확신하는 것이 좋습니다! 그것을 뜯어 내고 역동적 인 것을 넣는 것은 매우 까다로울 수 있습니다!
임베디드 프레임 워크가 관리하려는 문제는 다음과 같습니다.
데이터에 대한 오프셋 계산
모든 메모리에 대해 단일 구조체를 생성하는 것이 가능해야하지만이 * so * 는 올바른 방법이라고 생각하지 않습니다. C 컴파일러는 일반적으로 멀티 메가 바이트 구조로 작업하도록 요청하지 않으며 컴파일러간에 이식성이 매우 낮다는 느낌을받습니다.
구조가 사용되지 않으면 본질적으로 데이터 스키마를 기반으로 5 개의 정의 세트가 필요합니다.
- 간단한 유형의 크기
- 그룹 유형 내의 필드 오프셋
- 그룹 유형의 크기
- 그룹 실행에서 그룹 오프셋
- 그룹 실행 규모
- (성능이 요구하는 경우 그룹 실행의 절대 주소)
이러한 정의에는 트리와 유사한 종속성 트리가 있으며, 이는 최적의 성능을 위해 유형이 일반적으로 압축 / 정렬되어야하기 때문에 원시 C에서 매우 복잡해집니다. 완전히 확장 된 정의는 일부 컴파일러가 처리하는 것보다 훨씬 더 복잡해질 수 있습니다.
원시 C 프로젝트에서 이러한 문제를 관리하는 가장 쉬운 방법은 프로젝트 의 "빌드 도구"실행 파일 인 C 프로그램으로 오프셋을 계산하고 명시적인 opffset 번호를 포함하는 .h 파일로 프로젝트에 가져 오는 것입니다. 이 접근 방식을 사용하면 데이터 구조의 각 리프에 액세스하기 위해 기본 컴파일 중에 기본 주소 및 관련 인덱스를 사용하는 단일 매크로를 사용할 수 있어야합니다.
함수 포인터 손상 방지 및 디버깅 생산성 극대화
함수 포인터가 개체에 저장되면 데이터 손상에 더 취약하여 신비한 오류가 발생합니다. 더 좋은 방법 (수시로 다른 객체 유형을 포함하는 메모리 범위의 경우)은 함수 포인터 세트 세트에 조회 인덱스 인 vtable 코드를 객체에 저장하는 것입니다.
vtables는 실행 가능한 "빌드 도구"인 생성기 C 프로그램에 의해 #defines를 사용하여 .h 파일로 다시 계산되고 생성 될 수 있습니다.
객체 사용을 초기화하기 위해 적절한 vtable ID를 작성하려면 특수 생성자 매크로가 필요합니다.
이 두 가지 문제는 모두 목적 -C 전 처리기 (원시 C를 출력 함)에 의해 이미 효과적으로 잘 해결되었지만, 아주 작은 도구 세트를 사용하고 싶다면 처음부터 할 수 있습니다.
정적 메모리 구조의 리소스 / 작업에 메모리 블록 할당
다중 스레딩을 지원해야하는 경우 단기 동적 작업을 트리 구조의 특정 인덱스와 연결 (동등한 절차 / OO 프로그램에서 개체를 할당하는 가장 가까운 방법)은 다음을 사용하여 임의 인덱스를 시험 잠금하여 수행하는 것이 가장 좋습니다. 예를 들어 원자 적 증분 (== 1 검사로 0부터) 또는 뮤텍스, 블록이 사용 가능한지 확인하고 사용 가능한 경우 사용 된 것으로 표시 한 다음 블록 잠금을 해제합니다.
멀티 스레딩 지원이 필요하지 않은 경우에는 필요하지 않습니다. 다중 스레드 또는 단일 스레드 모드에서 실행할 수있는 이러한 리소스 할당 프로세스를 관리하기위한 사용자 지정 프레임 워크를 작성하여 나머지 코드 기반 이이 주제와 무관 하도록 허용하고 단일에서 더 빠른 성능을 허용 하는 것이 좋습니다. -스레드 시스템.
출처
https://stackoverflow.com/questions/2005791