· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Linux Device Driver Self Study

1. 목적

이 문서는 유영창님의 리눅스 디바이스 드라이버란 책을 토대로 제가 공부한 결과를 정리한 문서입니다. 책의 요약보다는 각 장별 제가 배우고 느낀점을 정리하는 데 초점을 둘 것입니다.

2. Self Study Memo

2.1. 2006-03-18

  • 커널소스 준비
  • 테스트 킷 제작

2.2. 2006-03-23

2.2.1. 2장. 리눅스 커널과 디바이스 드라이버

2.2.1.1. 커널에 대한 개괄적인 설명

  • 커널 소스의 구조
    • Documentation/
    • arch/ : 아키텍처와 밀접한 소스들 위치
    • include/asm : 심볼릭 링크, 아키텍처 특성에 맞는
    • include/linux : 표준 리눅스 커널과 관련된 헤더파일

  • 커널 소스 탐색방법
    • http/lxr.linux.no 커널 소스를 웹브라우저로 보여준다
    • grep사용
      • 사용법: grep ... 패턴 ...
      • greb readb * -r : 하부 디렉토리를 모두 담색하여 readb라는 문자열 검색
      • greb readb * -r | greb include : readb 검색결과에서 include가 포함된 것들만.
      • 위와 같이 greb을 여러개 겹쳐쓰면 유용

2.2.1.2. 디바이스 드라이버

  • 커널에 자원 처리 요청 - 시스템 호출(시스템콜, 소프트웨어 인터럽트), 디바이스 드라이버(파일 입출력 개념)
  • 모귤과 디바이스 파일
    • 모듈 : 다아비스 드라이버를 커널 소스에서 분리시킨 것?
    • 디바이스 파일 : 시스템에 있는 모든 자원을 파일 형식으로 표현
      • /dev/에 위치
      • 시스템 또는 하드웨어 정보를 제공
        • 디바이스 타입 : 문자 or 블록
        • 주 번호(Major) : 응용 프로그램과 디바이스 드라이버 사이의 연결 고리
        • 부 번호(Minor) : 실질적인 디바이스 <<== 주 번호와 부 번호의 차이가 무엇인가?
  • 디바이스 드라이버의 종류
    • 문자 : 임의의 길이를 갖는 문자열, 응용 프로그램에서, 버퍼없는
    • 블럭 : 일정 크기의 버퍼를 통해 데이터를 처리, 커널 내부의 파일 시스템에서 관리
    • 네트워크 : 네트워크 층과 연결된 디바이스 드라이버
  • 통합형 디바이스 드라이버(커널 2.6부터)
    • device : 실제 존재하는 하드웨어
    • driver : 디바이스를 제어하는 소프트웨어
    • bus : 하드웨어가 연결된 버스를 의미 <<==버스도 디바이스 라고 할 수 있나?
    • class : 하드웨어의 종류
    • interface : 논리적인 인터페이스
  • Kconfig <-- 집중 탐구 필요
    • 이 설정파일 안에 디바이스가 설정되어 있으면 커널의 컴파일 옵션에 해당 디바이스가 나타나게 된다???


2.2.2. 3장. 디바이스 파일과 저수준 파일 입출력


2.2.2.1. 디바이스 파일과 파일 입출력 함수

  • mknod 바이스 파일명 바이스 파일형 번호 번호
  • 디바이스 파일에는 fopen과 같은 스트림 파일 입출력 함수를 사용하면 안된다.

2.2.2.2. 저수준 파일 입출력 함수



2.2.2.3. 디바이스 파일 관련 함수

  • mknod
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>

    int mknod(const char* pathname, mode_t mode, dev_t dev);
     
    • S_IFCHR(문자), S_IFBLK(블록)
    • S_IRWXU : 사용자에 읽기, 쓰기
    • S_IRWXG : 그룹에 읽기, 쓰기
    • dev : 주번호와 부번호 지정 --> (240<<8) | 1
  • perror() : 실패 이유에 대한 error 번호를 해석하여 문자열로 표현


2.2.2.4. 저수준 파일 입출력 함수의 사용 예제

2.2.2.4.1. port.c
/*
 * Test /dev/port device file.
 * Output data to print port that is located 0x378
 * It connected 8-LED.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{
  int fd;       // File Descripter
  int lp;       // Loop Counter

  unsigned char buff[1];                        // Buffer for write

  fd = open("/dev/port", O_RDWR);       // open /dev/port in read & write mode
  if(fd < 0)                                            // open error, fd must be unsigned.
  {
        perror("/dev/port open error");
  }

  while(1)      // Infinite loop. It must be terminated manually.
  {
        for(lp = 0; lp < 8; lp++)       // loop 8th times.
        {
          lseek(fd, 0x378, SEEK_SET);   // Define file pointer is 0x378 that is parallel port.
          buff[0] = (0x01 << lp);               // shift lp times. LED will be shifted.
          write(fd, buff, 1);                   // Out to print port.

          sleep(1);             // delay some time. Maybe 1 second?
        }
  }

  close(fd);

  return 0;
}

2.2.2.4.2. lp.c
/*
 * test ioctl() by using /dev/lp0.
 * /dev/lp0 can control printer port. It is used to check printer status.
 * 13 Pin -> LP_PSELECD
 * 12 Pin -> LP_POUTPA
 * 10 Pin -> LP_PACK
 * If button is pushed, they are connected to ground.
 * So logic value is change to zero.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <linux/lp.h>

int main(int argc, char **argv)
{
        int fd;                 // file descripter.
        int prnstate;   // save state of print port.
        int lp;                 // loop counter.

        fd = open("/dev/lp0", O_RDWR | O_NDELAY);       // open read & write & no_delay.
        if(fd < 0)
        {
                perror("open error");
                exit(1);
        }

        while(1)
        {
                ioctl(fd, LPGETSTATUS, &prnstate);              // load print status to prnstate.
                /* To get status of printer,
                 * We define parameter of ioctl() is LPGETSTATUS. */

                if((prnstate & LP_PSELECD) == 0)                // LP_PSELECD is 13 Pin. It is connected GND.
                        printf("LP_PSELECK\n");

                else if((prnstate & LP_POUTPA) == 0)
                        printf("LP_POUTPA\n");

                else if((prnstate & LP_PACK) == 0)
                        printf("LP_PACK\n");

                else
                        printf("ON\n");
                usleep(50000);
        }

        close(fd);

        return 0;
}
2.2.2.4.3. mknod 명령과 저수준 파일 입출력 함수 정리

2.2.3. 4장. 간단한 모듈 테스트

2.2.3.1. 모듈

  • 어떤 기능을 구현하는 커널 부분을 의미하며 ELF 형식의 객체 파일
  • Hello world프로그램 문제 발생.. 책에서는 커널 버전이 2.6.4인데 비해 지금 사용중인 커널은 2.6.14.7이라 버전이 틀려서 인가 했지만 결국. 오타로 밝혀짐.
  • Makefile에서 ogj-m := test.c라고 해놨었음.. --;;;
  • 모듈 동적 링크 개념
    • 심볼 테이블에 외부 참조가 가능한 함수의 심볼과 주소가 등록되어있음.
    • 모듈이 커널에 적재될때 커널내의 심볼 테이블을 참조하고 주소를 얻는다.
    • 모듈에 외부 참조된 심볼 선언은 객체와 테이블을 이용해 관리 <== 무슨 말
    • 모듈의 외부 참조가 선언된 심볼들을 커널 내의 심볼 테이블에 등록
      • 모듈의 외부 참조 심볼이 모지?
    • 모듈 코드에서 선언되지 않았던 주소를 모두 선언하면 커널에 등록된다.
  • 커널에서 사용하는 심볼 테이블은 cat /proc/ksyms
  • 모듈 유틸리티
    • insmod
    • rmmod
    • lsmod
    • depmod
    • modprobe
  • 모듈적재 과정 -- 이해가 안감. 제거 과정도 마찬가지'''''

2.2.3.2. 모듈 소스 형식

  • 헤더파일
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
  • 모듈 관련 함수 표기법
module_init(hello_init); // 사용자가 임의로 지정할 수 있다.
module_exit(hello_exit);

2.2.3.3. 모듈 매개변수 지정

  • 디바이스 드라이버와 같은 초기값 중에서 외부에서 변경할 수 있는 것을 모듈 매개변수
  • 설정값을 그때 그때 다르게 해 줄때 사용할까나..
#define <linux/moduelparam.h>

static int onevalue = 1;
static char *twostring = NULL;

module_param(onevalue, int, 0);
module_param(twosrting, charp, 0);
  • module_param(변수명, 변수 타입, 접근 속성)

2.2.3.4. 커널 메시지 출력

  • printk(로그레벨, "메지시");
  • 디폴트는 4
  • 커널 메시지는 바로 콘솔로 출력되지 않고 원형 큐형태로 로그버퍼에 저장된다.
  • dmesg를 사용하면 커널 메시지를 볼 수 있다.
  • cat /proc/kmsg 는 메시지 바로바로 출력가능하게.

2.3. 2006-03-25

  • FA Linux 시미나 참석했었습니다. 음... 제가 초보라 세미나 내용이 잘 이해는 되지 않았지만.. 그래도 참 신기한 경험이었네요. 유영창님 역시 제 생각대로 굉장힌 포스를 풍기시던데요..

2.4. 2006-04-06

  • 에휴~ 이렇게 날짜가 뚝뚝 떨어지는걸 보니 제가 생각해도 참 한심하네요.. 빨리 열심히 해야 하는데.

2.4.1. 5장. 메모리 할당과 해제

2.4.1.1. 변수

  • 전역 & 지역 변수 : 가급적 지역변수를 사용하고 디바이스 드라이버가 커널에 적재되고 해제되는 시점까지 유지할려면 전역변수로 지정
  • static 키워드를 사용하자 : 해당 소스에서만 참조
  • int 문제 : cpu 마다 다르니 확인, 커널에서 제공되는 것 사용?
  • 구조체는 packed를 사용 실제 크기로 선언
  • include/linux/byteorder, #include <asm/byteorder.h>
  • volatile
    • 하드웨어 셋팅시 컴파일러가 자동으로 최적화를 하게 하면 안된다
    • 반드시 메모리에 접근하여 처리하여야.
    • 예제작성 해볼것

2.4.1.2. 동적 메모리

  • kmalloc(), kfree()
  • vmalloc(), vfree()
  • __get_free_pages(), free_pages()

2.4.1.3. 메모리 풀

  • 사전에 예측되는 최소한의 메모리를 미리 할당하고, 관리하는 방식?
  • 절대 할당이 실패하지 않음.

2.4.2. 6장. 디바이스의 등록과 해제

2.4.2.1. 디바이스 드라이버 제어 방식

  • 응용 프로그램이 하드웨어를 제어하기 위해서는 저수준 파일 입출력 함수를 사용 디바이스 파일에 접근하면 그에 해당되는 디바이스 드라이버에 제어 함수들이 호출이 되어서 하드웨어를 다루게 된다.
  • 문자, 블록, 네트워크
  • 문자 디바이스 드라이버는 저수준 파일 입출력에 대응하는 하드웨어를 다루는 커널 함수의 집합


2.4.2.2. struct file_operations

  • 파일 입출력 함수에 해당하는 함수들을 등록시키는 구조체

2.4.2.3. 문자 디바이스 드라이버의 등록과 해제 및 구성


2.4.3. 7장. 디바이스 드라이버의 초기화와 종료

2.4.3.1. 초기화와 종료처리

  • 디바이스 드라이버에 모듈 초기화 시점, 디바이스를 여는 시점, 모듈 제거 시점, 디바이스를 닫는 시점
    • insmod 명령 : module_init - 모듈 적재 과정
    • open()함수 : file_operations.open - 디바이스 파일을 여는 과정
    • close()함수 : file_operations.release - 디바이스 파일을 닫는 과정
    • rmmod 명령 : module_exit - 모듈 제거 과정

2.4.3.2. 모듈 사용 횟수 관리

2.4.3.3. I/O 영역의 경쟁 처리 함수

2.4.4. Check Point

2.4.4.1. Makefile

obj-m   := test.o

KDIR    := /lib/modules/$(shell uname -r)/build
PWD     := $(shell pwd)

default:
    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

test:
    echo $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
  • 위의 Makefile은 기본적인 Makefile과는 달리 source, object, compiler등의 설정이 없다. 과연 이건 어떻게 동작을 하는 것일까?

  • uname -r : 현재 커널 버전이 추력된다.
  • /lib/modules/$(shell uname -r)/build : kernel source가 있다. 각 버전대의 kernel 소스에 대한 심볼릭 링크이다.
  • $(MAKE) : 그냥 make
  • -C : man make로 확인결과 작업디렉토리를 바꾸는 옵션이다. 즉 -C /path 하면 해당 path에 있는 Makefile을 실행시키는데 여기서는 커널 소스를 작업디렉토리로 설정한다. SUBDIRS은 현재 컴파일할 소스가 있는 디렉토리를 커널소스에 있는 Makefile에 넘겨주는 역활을 한다.
  • 결국 커널소스내의 Makefile을 사용해서 현재 디렉토리에 위치한 소스를 make module을 하는 것이다.
  • 물론 이렇게 간단한 구조는 아닌것 같지만 일단 현재로서는 여기정도까지 파악을 하였다.
  • make module시 SUBDIRS가 설정이 되어 있으면 일반 커널 컴파일시 수행되는 make modules가 아닌 현재 사용자가 작성한 소스(SUBDIRS에 위치한)만 모듈로 컴파일을 수행하는 것 같다.
  • 역시 리눅스는 심오. 이런식으로 연동이 되나니 대단~~

2.4.4.2. kmalloc() test

  • 과연 kmalloc()으로 메모리 할당 받으면 /proc/meminfo에 메모리 정보가 그 만큼 변할 것인가?
  • meminfo에 어느 부분이 변하는 것인지 찾자!!
  • 시스템 부트시 커널은 커널 루틴들이 필요로 할 만큼을 예상하여 미리 free_page_list로 초기화시켜 두고 커널 루틴이 kmalloc()을 호출하면 kmalloc()루틴은 free_page_list에서 요구한 만큼을 할당해 주는 것이다. 미리 할당해 두는 크기는 시스템의 물리메로리에 비례한다.
  • /usr/src/linux/include/linux/mm.h에서 확인!
  • 요구된 양을 그래도 할당하는 것이 아니라 단편화를 막기위해 N바이트를 요구하면 2^X바이트를 할당 : 1000바이트를 요구하면 1025바이트 이런식
  • PAGE_SIZE*32크기씩 100개 할당해한 결과 = 총 13107200Byte, 약 13MB, 이정도의 양이 변한부분은?
할당전 할당후
Total 256264 256264
MemFree 120572 107648
Buffers 9040 9072
Cached 71152 71208
Active 77080 77144
Inactive 41608 41636
LowFree 120572 107648
Dirty 108 180
Slab 12920 25720
  • 위의 결과를 보면 MemFree, LowFree,Slab등이 13MB씩 변한 것을 볼수 있다. 각각의 의미를 더 정확하게 파악할 필요가 있을 것 같다.
  • '''''추가조사 필요


2.4.4.3. volatile test

  • volatile을 했을 때랑 안 했을 때랑 어셈블리 코드의 변화 확인!

2.4.4.4. read(), write(), llseek() 구현

  • 실제로 동작하는 것 처럼 보이는 예제 코드 작성해보기..





3. 게시판


captcha
Username:

ID
Password
Join
You have an ambitious nature and may make a name for yourself.


sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2006-04-28 13:54:19
Processing time 0.0140 sec