커널 산책 - 함수와 시스템 콜 (3)

C 언어는 함수를 지원하는 언어 입니다. 

펌웨어 개발자는 이제 골치 아픈 어셈블러로 개발하는 것보다 C 언어로 개발하는 것이 편해졌죠...

펌웨어는 하드웨어를 제어하고 프로세서에 매우 의존적인 프로그램을 많이 작성합니다. 

그리고 한번 사용한 프로세서는 계속적으로 사용하는 경향이 있죠

시리얼 통신을 하는 프로그램을 작성할 때마다 동일한 시리얼 통신 초기화 루틴을 사용하게 됩니다. 

LED 를 제어 하는 프로그램 역시 같은 하드웨어를 사용하게 되고 동일한 코드를 사용하게 됩니다. 

그래서 그때 마다 다시 작성하기 보다는 이전에 작성한 코드를 재 사용하게 되죠

이런 행위는 꼭 펌웨어 개발에만 사용하는 것이 아니죠

일반적인 C 코딩을 할때 도 마찬가지가 됩니다. 

어찌되었든 보드에 맞는 C 언어 함수들을 미리 준비하고 사용합니다. 

예를 들면 다음과 같이 함수 이름을 짓습니다. 

void led_init( void );
void led_on( void );
void led_off( void );

void uart_init( int baud );
void uart_write( char *data, int size );
int  uart_read( char *data, int size );

그리고 매번 이 소스를 다른 프로젝트에 포함하고 이를 컴파일하고 링크하여 보드에 심습니다.

혼자 작성하고 관리하면 이 방법이 그리 나쁜것은 아니죠...

하지만 이제 여러명이 작성하게 됩니다. 

보드는 웬만하면 설계를 바꾸지 않으므로 이전 보드는 공통된 소스가 있고 이를 여러명이 다양한 목적으로 개발합니다.

소스를 가지고 여러명이 관리하면 좋은 방법은 버전관리 프로그램을 사용하는 것이죠..

그. 런. 데..

펌웨어 개발자들에게 그런 행위를 요구하면 한국에서는 욕먹습니다. 

뭐 이미 체계가 갖추어진 곳이야 별 문제 없지만...

옛분들.. 버전 관리 프로그램을 접해 보신적도 없고 배우려고도 하지 않습니다. 

문제는 소스를 가지고 매번 컴파일 하면 조금 불편해 집니다. 

이러다가 처음 시도하는 것이 라이브러리를 만드는 것입니다. 

그렇지만 이 방법도 여전히 링크라는 과정을 거칩니다. 

이미 검증된 부분을 매번 컴파일 하는 것도 그렇고...

만약 소스라도 없으면 귀찮아 집니다. 

또 처음 부팅해서 보드를 초기화 하는 부분과 기능을 수행하는 부분을 별도로 작성하고 싶은 욕심이 생깁니다. 

그래서 프로그램을 부팅하는 부분과 실제로 필요한 기능을 담당하는 부분을 별도의 프로그램처럼 만들고 싶은데

적은 저장 공간을 가지고 있는 펌웨어 특성상 동일한 기능을 각각의 프로그램에 링크해서 사용 메모리를 낭비하는 것은

무척 싫은 거죠.. 펌웨어 프로그래머를 만나서 보면 

그 양반들이 코딩할 때 집착하는 것이 두 가지가 있습니다.

나이가 많으신 개발자일수록 심한데..

그것은 프로그램 크기와 처리 속도입니다. 

항상 열악한 상황에서 코딩을 하는 버릇이 되셔서 

가급적 작게 프로그램을 만들려고 하고 

처리 속도를 늘리기 위해서 정말 어려운 코드를 만들어 갑니다. 

반대로 PC 프로그래머들은 편한 생각을 합니다. 

뭐 용량이 부족해? 그럼 하드 늘리고 메모리 늘려..

뭐 속도가 안나와? 그럼 CPU 좋은거 써...

쩝... 부럽죠.,..

이 분들이 이런 고민을 해결하기 위해서 

지금은 흔히 사용하는 동적 링크 개념을 도입합니다. 

물론 동적 링크 개념은 사실 펌웨어 개발자들이 만든 꼼수를 정형화 한거죠...

그런 방식중 하나를 들라면 이런 것이 있습니다. 

C 언어의 구조체와 함수 포인터를 이용하는 겁니다. 

C의 구조체로 다음과 같이 하나 만듭니다 .

   struct lib_funcs
   {
    void (*led_init)( void );
    void (*led_on)( void );
    void (*led_off)( void );
   
    void (*uart_init)( int baud );
    void (*uart_write)( char *data, int size );
    int   (*uart_read)( char *data, int size );
   };
   
   #define LIB_FUNCS_ADDRESS   0xC0000000


이 함수군을 제공할 프로그램 쪽에서는 다음과 같은 형식으로 

해당 공유 주소에 구조체 변수를 이용하여 함수 벡터 테이블을 만듭니다. 

    void lib_export( void )
    {
    struct lib_funcs *lib_funcs;
    
    lib_funcs = (struct lib_funcs *) LIB_FUNCS_ADDRESS;
   
    lib_funcs->led_init    = led_init;
    lib_funcs->led_on      = led_on;
    lib_funcs->led_off     = led_off;
    
    lib_funcs->uart_init   = uart_init;
    lib_funcs->uart_write  = uart_write;
    lib_funcs->uart_read   = uart_read;
   
    }

이 함수를 호출해 주면 0xC0000000 번지에는 함수 벡터 번지가 형성되는 거지요..

이번에는 다른 프로그램은 동일한 구조체를 쓰고 이를 가져 오는 것이죠...


예를 들면 

    struct lib_funcs *lib_funcs;
    
    void lib_init( void )
    {
    lib_funcs = (struct lib_funcs *) LIB_FUNCS_ADDRESS;
    }
   
그리고 이것을 

lib_funcs->led_init(); 
와 같은 형식으로 호출해도 되고 
#define _led_int   lib_funcs->led_init


이런식으로 또는 매크로 함수 선언과 같은 다양한 기교를 사용해서 호출하도록 하면되는 것이죠..

헥헥...

오늘도 체력 고갈.. 

누가 필코 키보드가 죽이는 키감이라고 한거야...

비싸기만 하구 손목 아프네. ㅠㅠ

----------------------------------------------------------------------------------------

독자   : 아 그러니깐 언제 시스템 콜이 나오냐구요..

         난 C 언어 공부하고 펌웨어 프로그램 공부하려고 이글 읽는 것이 아니라니깐..
        
유영창 : 딴 책 읽으슈...

         좋은 책 많수다...
         
----------------------------------------------------------------------------------------