25. Embedded Systems Programming

25.1. Embedded Systems

임베디드 컴퓨터는 우리 실생활 전체에 자리잡고 있다. 이런 컴퓨터의 특징은:

  • 신뢰성이 매우 중요하다.
  • 자원이 매우 한정되어 있다.
  • 실시간 반응이 필수적이다.
  • 몇 년 동안에도 시스템이 중단되지 않아야 한다.
  • 즉각적인 유지보수가 불가능하거나 매우 어렵다.

25.2. Basic Concepts

기본적인 개념들을 되새겨보자.

  • 정확성.
  • 실패에 대한 대처.
  • 고장 시간 없음.
  • 실시간 조건.
  • 예측 가능성.
  • 동시성.

25.2.1. Predictability

C++에서 예측 가능하지 않은 특성들은 다음과 같다.

  • operator new, delete
  • 예외
  • dynamic_cast

이는 리얼타임 애플리케이션에서는 지양되어야 한다.

25.2.2. Ideals

이상적으로는 임베디드에서도 고수준 추상화된 코드를 쓰는 것이 좋다. 지나친 최적화에 집착하지 말라.

25.2.3. Living with failure

실패에 대처하려면 엣지 케이스와 그에 관련된 도메인 지식을 알고 있어야 한다. 하지만 일반적인 룰들이 존재한다.

  • 자원 누수를 하지 말라.
  • 실패에 대비한 복제를 준비하라.
  • 스스로 체크하라.
  • 코드를 모듈화해서 잘못된 부분을 빠르게 시정할 수 있도록 하라.
  • 부분 시스템을 모니터하라.

25.3. Memory Management

C++에는 메모리 할당 방법이 3가지 있다.

  • 정적 메모리.
  • 스택 (자동) 메모리.
  • 힙 (동적) 메모리.

스택 메모리는 제한되어 있다는 문제점이 있다. 동적 메모리는 할당/해제에 시간이 오래 걸리고, 파편화의 문제점이 있다.

25.3.1. Free-store problems

동적 메모리 할당은 파편화의 문제가 있다.

25.3.2. Alternatives to the general free store

그래서 임베디드 시스템 프로그래밍에서는 동적 할당 대신 스택과 풀을 사용한다. STL 컨테이너는 기본적으로 동적 할당을 함을 명심하자. 하지만 그렇다고 포인터를 남용하는 것은 별로 좋지 않을 것이다.

25.3.3. Pool example

메모리 풀은 다음과 같이 쓸 수 있다.

template<typename T, int N>
class Pool {                               // Pool of N objects of type T
public:
          Pool();                            // make pool of N Ts
          T* get();                         // get a T from the pool; return 0 if no free Ts
          void free(T*);               // return a T given out by get() to the pool
          int available() const;   // number of free Ts
private:
          // space for T[N] and data to keep track of which Ts are allocated
          // and which are not (e.g., a list of free objects)
};

25.3.4. Stack example

메모리 스택은 다음과 같이 쓸 수 있다.

template<int N>
class Stack {                               // stack of N bytes
public:
          Stack();                             // make an N-byte stack
          void* get(int n);              // allocate n bytes from the stack;
                                                        // return 0 if no free space
          void free();                      // return the last value returned by get() to the stack
          int available() const;      // number of available bytes
private:
          // space for char[N] and data to keep track of what is allocated
          // and what is not (e.g., a top-of-stack pointer)
};

25.4. Addresses, Pointers, and Arrays

포인터 사용에는 2가지 문제점이 있다.

  • 안전하지 않은 명시적 형변환.
  • 배열 원소에 전달되는 포인터.

25.4.1. Unchecked conversions

가끔 reinterpret_cast, static_cast가 필요할 때가 있다. 그러나 사용을 최소화하라.

25.4.2 A problem: dysfunctional interfaces

다음과 같은 코드는 매우 나쁘다.

void poor(Shape* p, int sz)                      // poor interface design
{
          for (int i = 0; i<sz; ++i) p[i].draw();
}

void f(Shape* q, vector<Circle>& s0)   // very bad code
{
          Polygon s1[10];
          Shape s2[10];
          // initialize
          Shape* p1 = new Rectangle{Point{0,0},Point{10,20}};
          poor(&s0[0],s0.size());          // #1 (pass the array from the vector)
          poor(s1,10);                            // #2
          poor(s2,20);                            // #3
          poor(p1,1);                             // #4
          delete p1;
          p1 = 0;
          poor(p1,1);                              // #5
          poor(q,max);                          // #6
}

std::vector가 가능하다면 std::vector를 참조자로 넘겨라. 가능하지 않다면..

25.4.3. A solution: an interface class

std::span을 쓰라.

25.4.4. Inheritance and containers

클래스 계층을 지원하기 위해서는 형변환이 지원되는 operator()로 래핑해야 한다.

25.5. Bits, Bytes, and Words

메모리 표현에 대해 알아보자.

25.5.1. Bits and bit operations

비트는 대개 8바이트의 나열, 워드는 대개 4바이트의 나열로 이해하면 쉽다. 비트 패턴은 unsigned를 쓰거나, std::bitset을 쓴다.

25.5.2. std::bitset

std::bitset으로 비트 배열을 사용할 수 있다.

25.5.3. Signed and unsigned

절대로 부호 없는 정수형과 부호 있는 정수형을 섞어 쓰지 말라. char를 산술 연산에 쓰지 말라.

25.5.4. Bit manipulation

비트 플래그는 때로 플래그에 유용하다.

25.5.5. Bitfields

구조체의 비트 필드를 다음과 같이 쓸 수 있다.

struct PPN {                                       // R6000 Physical Page Number
          unsigned int PFN : 22 ;         // Page Frame Number
          int : 3 ;                                     // unused
          unsigned int CCA : 3 ;         // Cache Coherency Algorithm
          bool nonreachable : 1 ;
          bool dirty : 1 ;
          bool valid : 1 ;
          bool global : 1 ;
};

25.5.6. An example: simple encryption

다음의 코드 예제는 간단한 암호화/복호화 코드이다.

void encipher(
          const unsigned long *const v,
          unsigned long *const w,
          const unsigned long * const k)
{
          static_assert(sizeof(long)==4,"size of long wrong for TEA");

          unsigned long y = v[0];
          unsigned long z = v[1];
          unsigned long sum = 0;
          const unsigned long delta = 0x9E3779B9;

          for (unsigned long n = 32; n––>0; ) {
                    y += (z<<4 ^ z>>5) + z^sum + k[sum&3];
                    sum += delta;
                    z += (y<<4 ^ y>>5) + y^sum + k[sum>>11 & 3];
          }
          w[0]=y;
          w[1]=z;
}

void decipher(
          const unsigned long *const v,
          unsigned long *const w,
          const unsigned long * const k)
{
          static_assert(sizeof(long)==4,"size of long wrong for TEA");

          unsigned long y = v[0];
          unsigned long z = v[1];
          unsigned long sum = 0xC6EF3720;
          const unsigned long delta = 0x9E3779B9;

          // sum = delta<<5, in general sum = delta * n
          for (unsigned long n = 32; n–– > 0; ) {
                    z –= (y << 4 ^ y >> 5) + y ^ sum + k[sum>>11 & 3];
                    sum –= delta;
                    y –= (z << 4 ^ z >> 5) + z ^ sum + k[sum&3];
          }
          w[0]=y;
          w[1]=z;
}

int main()           // sender
{
          const int nchar = 2*sizeof(long);        // 64 bits
          const int kchar = 2*nchar;                   // 128 bits

          string op;
          string key;
          string infile;
          string outfile;
          cout << "please enter input file name, output file name, and key:\n";
          cin >> infile >> outfile >> key;
          while (key.size()<kchar) key += '0';    // pad key
          ifstream inf(infile);
          ofstream outf(outfile);
          if (!inf || !outf) error("bad file name");

          const unsigned long* k =
                    reinterpret_cast<const unsigned long*>(key.data());

          unsigned long outptr[2];
          char inbuf[nchar];
          unsigned long* inptr = reinterpret_cast<unsigned long*>(inbuf);
          int count = 0;

          while (inf.get(inbuf[count])) {
                    outf << hex;                               // use hexadecimal output
                    if (++count == nchar) {
                              encipher(inptr,outptr,k);
                              // pad with leading zeros:
                              outf << setw(8) << setfill('0') << outptr[0] << ' '
                                        << setw(8) << setfill('0') << outptr[1] << ' ';
                              count = 0;
                    }
          }

          if (count) {  // pad
                    while(count != nchar) inbuf[count++] = '0';
                    encipher(inptr,outptr,k);
                    outf << outptr[0] << ' ' << outptr[1] << ' ';
          }
}

25.6. Coding Standards

단체마다 여러 코딩 기준이 존재한다.

25.6.1. What should a coding standard be?

좋은 코딩 기준은 필수적이다. 나쁜 코딩 기준은 없는 것만 못하다. C++을 C처럼 쓰게 하는 기준 등.

25.6.2. Sample rules

인터넷의 여러 샘플들을 참고해 보자.

25.6.3. Real coding standards

좋은 코딩 기준 : https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중