다음을 통해 공유


코딩 기본 사항

Important

Azure Sphere(레거시) 설명서입니다. Azure Sphere(레거시)는 2027년 9월 27일에 사용 중지되며 사용자는 이 시간까지 Azure Sphere(통합)로 마이그레이션해야 합니다. TOC 위에 있는 버전 선택기를 사용하여 Azure Sphere(통합) 설명서를 볼 수 있습니다.

애플리케이션 코드가 이 항목에 정의된 최소 품질 표준을 충족하는 것이 좋습니다. 프로덕션 배포 애플리케이션을 개선하려는 고객과의 파트너십을 통해 몇 가지 일반적인 문제를 발견했습니다. 이 경우 애플리케이션의 성능이 향상되었습니다.

일반적인 문제

  • 대상 API 집합을 설정할 때는 최신 CMake 및 Azure Sphere 도구를 사용하고 최종 릴리스 이진 파일을 설정 AZURE_SPHERE_TARGET_API_SET="latest-lts"하여 컴파일하는 것이 좋습니다. 자세한 내용은 재생 가능한 보안에 대한 코딩을 참조하세요.

참고 항목

특히 제조 프로세스 내에서 테스트용으로 로드할 이미지 패키지를 만드는 경우 디바이스가 공급되거나 복구된 적절한 Azure Sphere OS 버전으로 설정 AZURE_SPHERE_TARGET_API_SET 됩니다. 이렇게 하지 않으면 Azure Sphere OS에서 이미지 패키지를 거부하게 됩니다.

  • 프로덕션에 애플리케이션을 배포할 준비가 되면 릴리스 모드에서 최종 이미지 패키지를 컴파일해야 합니다.
  • 컴파일러 경고에도 불구하고 프로덕션에 배포된 애플리케이션을 보는 것이 일반적입니다. 전체 빌드에 대해 경고 0 정책을 적용하면 모든 컴파일러 경고가 의도적으로 해결됩니다. 가장 자주 발생하는 경고 유형은 다음과 같습니다. 이를 해결하는 것이 좋습니다.
    • 암시적 변환 관련 경고: 수정되지 않은 초기 빠른 구현으로 인한 암시적 변환으로 인해 버그가 자주 발생합니다. 예를 들어 여러 숫자 형식 간에 암시적 숫자 변환이 많은 코드는 심각한 정밀도 손실 또는 계산 또는 분기 오류를 초래할 수 있습니다. 모든 숫자 형식을 올바르게 조정하려면 캐스팅뿐만 아니라 의도적인 분석과 캐스팅을 모두 사용하는 것이 좋습니다.
    • 예상된 매개 변수 형식을 변경하지 마세요. API를 호출할 때 명시적으로 캐스팅하지 않으면 암시적 변환으로 인해 문제가 발생할 수 있습니다. 예를 들어 부호 없는 숫자 형식 대신 부호 있는 숫자 형식이 사용될 때 버퍼를 오버런합니다.
    • const-discarding 경고: 함수에 매개 변수로 const 형식이 필요한 경우 이를 재정의하면 버그 및 예측할 수 없는 동작이 발생할 수 있습니다. 경고의 이유는 const 매개 변수가 그대로 유지되고 특정 API 또는 함수를 디자인할 때 제한을 고려하기 위한 것입니다.
    • 호환되지 않는 포인터 또는 매개 변수 경고: 이 경고를 무시하면 나중에 추적하기 어려운 버그가 숨겨질 수 있습니다. 이러한 경고를 제거하면 다른 애플리케이션 문제의 진단 시간을 줄일 수 있습니다.
  • 일관된 CI/CD 파이프라인 설정은 오래된 애플리케이션 릴리스를 디버깅하기 위해 이진 파일 및 해당 특파원 기호를 쉽게 다시 빌드할 수 있으므로 지속 가능한 장기 애플리케이션 관리를 위한 핵심입니다. 적절한 분기 전략은 릴리스를 추적하는 데도 필수적이며 이진 데이터를 저장하는 데 비용이 많이 드는 디스크 공간을 방지합니다.
  • 가능하면 코드를 더 유지 관리하면서 전체 코드베이스 전체에서 데이터 포인터로 global const char* 사용할 수 있도록 모든 일반 고정 문자열을 하드 코딩하는 대신(예: 명령 내에서 printf ) 정의합니다. 실제 애플리케이션에서 로그 또는 문자열 조작(예: OK, Succeeded또는 JSON 속성 이름)에서 일반 텍스트를 수집하고 상수로 전역화하면 읽기 전용 데이터 메모리 섹션(.rodata라고도 함)이 절약되어 다른 섹션에서 사용할 수 있는 플래시 메모리(예: 코드의 경우 .text)가 절약되는 경우가 많습니다. 이 시나리오는 종종 간과되지만 플래시 메모리를 크게 절약할 수 있습니다.

참고 항목

위의 내용은 단순히 컴파일러 최적화(예: -fmerge-constants gcc)를 활성화하여 달성할 수도 있습니다. 이 방법을 선택하는 경우 컴파일러 출력도 검사하고 원하는 최적화가 적용되었는지 확인합니다. 이는 여러 컴파일러 버전에 따라 다를 수 있습니다.

  • 전역 데이터 구조의 경우 가능하면 동적으로 할당된 메모리에 대한 포인터를 사용하는 대신 비교적 작은 배열 멤버에 고정 길이를 제공하는 것이 좋습니다. 예시:
    typedef struct {
      int chID;
      ...
      char chName[SIZEOF_CHANNEL_NAME]; // This approach is preferable, and easier to use e.g. in a function stack.
      char *chName; // Unless this points to a constant, tracking a memory buffer introduces more complexity, to be weighed with the cost/benefit, especially when using multiple instances of the structure.
      ...
    } myConfig;
  • 특히 자주 호출되는 함수 내에서 가능하면 동적 메모리 할당을 방지합니다.
  • C에서 메모리 버퍼에 대한 포인터를 반환하는 함수를 찾고 참조된 버퍼 포인터 및 관련 크기를 호출자에게 반환하는 함수로 변환하는 것이 좋습니다. 이 작업을 수행하는 이유는 반환된 버퍼의 크기가 강제로 승인되지 않으므로 힙의 일관성을 위험에 빠뜨릴 수 있으므로 버퍼에 대한 포인터만 반환하면 호출 코드에 문제가 발생하는 경우가 많기 때문입니다. 예시:
    // This approach is preferable:
    MY_RESULT_TYPE getBuffer(void **ptr, size_t &size, [...other parameters..])
    
    // This should be avoided, as it lacks tracking the size of the returned buffer and a dedicated result code:
    void *getBuffer([...other parameters..])

동적 컨테이너 및 버퍼

목록 및 벡터와 같은 컨테이너는 포함된 C 애플리케이션에서도 자주 사용되며, 표준 라이브러리를 사용하는 메모리 제한으로 인해 일반적으로 명시적으로 코딩되거나 라이브러리로 연결되어야 합니다. 이러한 라이브러리 구현은 신중하게 설계되지 않은 경우 집약적인 메모리 사용을 트리거할 수 있습니다.

일반적인 정적으로 할당된 배열 또는 메모리 동적 구현 외에도 증분 할당 방법을 사용하는 것이 좋습니다. 예를 들어 미리 할당된 N 개체의 빈 큐 구현으로 시작합니다. (N+1) 큐 푸시에서 큐는 고정된 X 추가 미리 할당된 개체(N=N+X)에 의해 증가합니다. 이 개체는 큐에 추가된 다른 개체가 현재 용량을 오버플로하고 X에 미리 할당된 추가 개체로 메모리 할당을 증가시킬 때까지 동적으로 할당된 상태로 유지됩니다. 결국 사용하지 않는 메모리를 회수하기 위해 자주 호출하는 새 압축 함수를 구현할 수 있습니다(정기적으로 호출하는 데 비용이 너무 많이 들기 때문에).

전용 인덱스는 큐에 대한 활성 개체 수를 동적으로 유지하며, 이는 추가 오버플로 보호를 위해 최대값으로 제한될 수 있습니다.

이 방법은 기존 큐 구현에서 연속 메모리 할당 및 할당 취소에 의해 생성된 "chatter"를 제거합니다. 자세한 내용은 메모리 관리 및 사용을 참조하세요. 목록, 배열 등과 같은 구조체에 대해 유사한 접근 방식을 구현할 수 있습니다.