디바이스 드라이버를 위한 커널 서비스







1. 변수와 메모리 할당



1.1 디바이스 드라이버에서 변수의 사용

    • 지역 변수와 전역 변수 중에 지역 변수를 사용하는 것이 좋다.

      -> 디바이스 드라이버는 다중 프로세스 환경에서 동작.
          프로세스 간 변수 사용에 따른 경쟁 문제를 해결하기 위해 가급적 지역 변수를 사용하는 편이 좋다.

    • 변수 명 사용시 static을 주로 사용

      -> 커널 소스는 방대한 함수와 전역 변수가 있으므로 이것이 서로 중복되지 않도록 static으로 선언
          cf. 함수 안에 static을 쓰면 전역 변수처럼 되기 때문에 함수 안에 쓰지 않는다.




1.2 이식성과 데이터 형


    • 데이터 형


다른 플랫폼에서 문제없이 수행되려면 변수의 데이터 형을 명확히 해야한다.


특히, int 형의 경우, "CPU의 레지스터와 동일한 크기를 가지는 타입"이다.


이 말은 int의 실제 크기는 플랫폼마다 달라진다는 얘기이다.




따라서 커널에서 제공하는 데이터형을 사용하는 것이 바람직하다.




 #include <asm/types.h>에서 제공하는 데이터형

 부호 있는 정수

부호 없는 정수 

 __s8, s8

 8 bits (byte)

 __u8, u8

 8 bits (byte) 

 __s16, s16

 16 bits (word) 

 __u16, u16

 16 bits (word) 

 __s32, s32

 32 bits

 __u32, u32

 32 bits

 __s64, s64

 64 bits

 __u64, u64

 64 bits






    • 구조체의 실제 저장 

구조체는 대부분 int형의 배수로 선언이 된다.

구조체 안에 char, int 자료형 두 개가 있다고 가정했을 때, 실제 크기는 int형 4 byte + char형 1 byte라고 생각하기 쉽지만,
실제로는 4 byte + 4 byte로 구조체의 크기는 총 8 byte


정확한 데이터 교환을 위해 packed 라는 키워드를 사용해 실제 변수 크기로 선언되도록 하는 것이 좋다. 


typedef struct

{

       u16 index;

       u16 data;

       u8   data2;

}__attribute__ ((packed)) testctl_t;






    • 바이트 순서


      다른 플랫폼에 이식할 경우, 정수 바이트 순서에 주의해야 한다.

      프로세스에 따라 바이트 순서를 처리하는 헤더는 include/linux/byteorder/ 디렉토리에 있다.

      #include <asm/byteorder.h>의 헤더를 포함시켜 사용한다.

      리틀엔디언 방식인지, 빅엔디언 방식인지에 따라 __LITTLE_ENDIAN , __BIG_ENDIAN 이 정의된다.






    • 동적 데이터 접근


      소스 코드에 정의 된 데이터 구조는 아키텍처에 따라 다르게 컴파일될 수 있다.

      예를 들어, 소스 코드에 int char int 순서로 정의를 해놨다고 해도 컴파일하고 나면 아키텍처에 따라 순서가 바뀌어 char int int 혹은 int int char 로  정렬되어 컴파일 될 수 있다.

      크기가 정해져 있지 않은 데이터를 액세스해야하는 경우 예외 처리기에서 처리되어 정렬된 데이터에 접근해야 하기 때문에 성능이 크게 저하된다.

      따라서 크기가 정해져 있지 않은 데이터에 액세스해야하는 경우 다음 매크로를 사용해야 한다.


    •  #include <asm/unaligned.h>

       get_unaligned (ptr) 

       ptr 주소에서 값을 읽는다.

       put_unaligned (val, ptr)

       ptr 주소에 val 값을 쓴다. 








    • I/O 메모리 접근 변수 처리


      메모리 번지를 이용하는 I/O처리는 최적화 등에 의해 의도되지 않은 형태로 변할 수 있으므로 주의를 기울인다.


          (예시)
          0xE0000300 번지 (센서 입력)이 바뀌는 것을 대기하기 위해

    u32    *ptr = (u32 *) 0xE0000300;

     while (*ptr = *ptr);
     Led_on();



            위 코드는 컴파일러에 의한 최적화로 다음과 같이 변할 수 있다.

    u32    *ptr = (u32 *) 0xE0000300;

     while(1);



            이 때는 voltaile를 이용하여 최적화되지 않도록 한다.


    volatile u32    *ptr = (u32 *) 0xE0000300;








    1.3 커널에서의 동적 메모리 관리




    커널에서는 malloc()이나 free()를 이용해 동적 메모리를 사용할 수 없다. 디바이스 드라이버는 커널 메모리를 사용하므로 물리적 접근이 가능하며 MMU를 고려한 처리가 필요하다.


        * 가상 메모리 (Virtual Memory)
             - 프로그램이 실행될 때 반드시 프로그램 전체가 실제 메모리에 있을 필요는 없으므로 현재 실행되어야 하는 부분만이 실제 메모리에 옮겨 쓰는 것이 가상 메모리

             - 이 가상 메모리를 사용하기 위해 주소 변환을 하는 장치가 필요한 데 이 장치가 MMU











    커널에서의 동적메모리 할당 함수

    함수 

    특징 

     kmalloc(), kfree()

     malloc(), free()와 유사하며 디바이스 드라이버에서 많이 사용

     할당 속도가 빠른 것이 특징
     할당 가능한 크기: 32*PAGE_SIZE

     vmalloc(), vfree()

     할당하려는 크기 외에는 매개변수를 주지 않음

     크기 제한이 없음

     가상 메모리 관리 루틴이 수행되기 때문에 kmalloc보다 속도가 느림 

     인터럽트 서비스 함수 안에서는 사용 불가

     __get_free_pages(), free_pages()

     페이지 단위 할당 함수







출처

https://ko.wikipedia.org/wiki/Volatile_변수

https://dojang.io/mod/page/view.php?id=432

http://www.tipssoft.com/bulletin/board.php?bo_table=FAQ&wr_id=968

http://l2men.tistory.com/21

http://www.xml.com/ldd/chapter/book/ch10.html

http://young3276.tistory.com/4


    



+ Recent posts