[[TableOfContents]] = 목적 = 이 문서는 유영창님의 리눅스 디바이스 드라이버란 책을 토대로 제가 공부한 결과를 정리한 문서입니다. 책의 요약보다는 각 장별 제가 배우고 느낀점을 정리하는 데 초점을 둘 것입니다. = Self Study Memo = == 2006-03-18 == * 커널소스 준비 * 테스트 킷 제작 == 2006-03-23 == === 2장. 리눅스 커널과 디바이스 드라이버 === ==== 커널에 대한 개괄적인 설명 ==== * 커널 소스의 구조 * Documentation/ * arch/ : 아키텍처와 밀접한 소스들 위치 * include/asm : 심볼릭 링크, 아키텍처 특성에 맞는 * include/linux : 표준 리눅스 커널과 관련된 헤더파일 * 커널 소스 탐색방법 * [http/lxr.linux.no 커널 소스를 웹브라우저로 보여준다] * grep사용 * 사용법: grep [옵션] ... 패턴 [파일] ... * greb readb * -r : 하부 디렉토리를 모두 담색하여 readb라는 문자열 검색 * greb readb * -r | greb include : readb 검색결과에서 include가 포함된 것들만. * 위와 같이 greb을 여러개 겹쳐쓰면 유용 ==== 디바이스 드라이버 ==== * 커널에 자원 처리 요청 - 시스템 호출(시스템콜, 소프트웨어 인터럽트), 디바이스 드라이버(파일 입출력 개념) * 모귤과 디바이스 파일 * 모듈 : 다아비스 드라이버를 커널 소스에서 분리시킨 것? * 디바이스 파일 : 시스템에 있는 모든 자원을 파일 형식으로 표현 * /dev/에 위치 * 시스템 또는 하드웨어 정보를 제공 * 디바이스 타입 : 문자 or 블록 * 주 번호(Major) : 응용 프로그램과 디바이스 드라이버 사이의 연결 고리 * 부 번호(Minor) : 실질적인 디바이스 '''''<<== 주 번호와 부 번호의 차이가 무엇인가?''''' * 디바이스 드라이버의 종류 * 문자 : 임의의 길이를 갖는 문자열, 응용 프로그램에서, 버퍼없는 * 블럭 : 일정 크기의 버퍼를 통해 데이터를 처리, 커널 내부의 파일 시스템에서 관리 * 네트워크 : 네트워크 층과 연결된 디바이스 드라이버 * 통합형 디바이스 드라이버(커널 2.6부터) * device : 실제 존재하는 하드웨어 * driver : 디바이스를 제어하는 소프트웨어 * bus : 하드웨어가 연결된 버스를 의미 '''''<<==버스도 디바이스 라고 할 수 있나?''''' * class : 하드웨어의 종류 * interface : 논리적인 인터페이스 * Kconfig '''''<-- 집중 탐구 필요''''' * 이 설정파일 안에 디바이스가 설정되어 있으면 커널의 컴파일 옵션에 해당 디바이스가 나타나게 된다??? === 3장. 디바이스 파일과 저수준 파일 입출력 === ==== 디바이스 파일과 파일 입출력 함수 ==== * mknod [디바이스 파일명] [디바이스 파일형] [주 번호] [부 번호] * 디바이스 파일에는 fopen과 같은 스트림 파일 입출력 함수를 사용하면 안된다. ==== 저수준 파일 입출력 함수 ==== ==== 디바이스 파일 관련 함수 ==== * mknod {{{#!vim c #include #include #include #include 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 번호를 해석하여 문자열로 표현 ==== 저수준 파일 입출력 함수의 사용 예제 ==== ===== port.c ===== {{{#!vim c /* * Test /dev/port device file. * Output data to print port that is located 0x378 * It connected 8-LED. */ #include #include #include #include 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; } }}} ===== lp.c ===== {{{#!vim 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 #include #include #include #include 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; } }}} ===== mknod 명령과 저수준 파일 입출력 함수 정리 ===== === 4장. 간단한 모듈 테스트 === ==== 모듈 ==== * 어떤 기능을 구현하는 커널 부분을 의미하며 ELF 형식의 객체 파일 * Hello world프로그램 문제 발생.. 책에서는 커널 버전이 2.6.4인데 비해 지금 사용중인 커널은 2.6.14.7이라 버전이 틀려서 인가 했지만 결국. 오타로 밝혀짐. * Makefile에서 ogj-m := test.c라고 해놨었음.. --;;; * 모듈 동적 링크 개념 * 심볼 테이블에 외부 참조가 가능한 함수의 심볼과 주소가 등록되어있음. * 모듈이 커널에 적재될때 커널내의 심볼 테이블을 참조하고 주소를 얻는다. * 모듈에 외부 참조된 심볼 선언은 객체와 테이블을 이용해 관리 <== '''''무슨 말''''' * 모듈의 외부 참조가 선언된 심볼들을 커널 내의 심볼 테이블에 등록 * '''''모듈의 외부 참조 심볼이 모지?''''' * 모듈 코드에서 선언되지 않았던 주소를 모두 선언하면 커널에 등록된다. * 커널에서 사용하는 심볼 테이블은 cat /proc/ksyms * 모듈 유틸리티 * insmod * rmmod * lsmod * depmod * modprobe * 모듈적재 과정 -- ''''이해가 안감. 제거 과정도 마찬가지''''' ==== 모듈 소스 형식 ==== * 헤더파일 {{{ #include #include #include }}} * 모듈 관련 함수 표기법 {{{ module_init(hello_init); // 사용자가 임의로 지정할 수 있다. module_exit(hello_exit); }}} ==== 모듈 매개변수 지정 ==== * 디바이스 드라이버와 같은 초기값 중에서 외부에서 변경할 수 있는 것을 '''모듈 매개변수''' * 설정값을 그때 그때 다르게 해 줄때 사용할까나.. {{{ #define static int onevalue = 1; static char *twostring = NULL; module_param(onevalue, int, 0); module_param(twosrting, charp, 0); }}} * module_param(변수명, 변수 타입, 접근 속성) ==== 커널 메시지 출력 ==== * printk(로그레벨, "메지시"); * 디폴트는 4 * 커널 메시지는 바로 콘솔로 출력되지 않고 원형 큐형태로 로그버퍼에 저장된다. * dmesg를 사용하면 커널 메시지를 볼 수 있다. * cat /proc/kmsg 는 메시지 바로바로 출력가능하게. == 2006-03-25 == * FA Linux 시미나 참석했었습니다. 음... 제가 초보라 세미나 내용이 잘 이해는 되지 않았지만.. 그래도 참 신기한 경험이었네요. 유영창님 역시 제 생각대로 굉장힌 포스를 풍기시던데요.. == 2006-04-06 == * 에휴~ 이렇게 날짜가 뚝뚝 떨어지는걸 보니 제가 생각해도 참 한심하네요.. 빨리 열심히 해야 하는데. === 5장. 메모리 할당과 해제 === ==== 변수 ==== * 전역 & 지역 변수 : 가급적 지역변수를 사용하고 디바이스 드라이버가 커널에 적재되고 해제되는 시점까지 유지할려면 전역변수로 지정 * static 키워드를 사용하자 : 해당 소스에서만 참조 * int 문제 : cpu 마다 다르니 확인, 커널에서 제공되는 것 사용? * 구조체는 packed를 사용 실제 크기로 선언 * include/linux/byteorder, #include * '''volatile''' * 하드웨어 셋팅시 컴파일러가 자동으로 최적화를 하게 하면 안된다 * 반드시 메모리에 접근하여 처리하여야. * '''''예제작성 해볼것''''' ==== 동적 메모리 ==== * kmalloc(), kfree() * vmalloc(), vfree() * __get_free_pages(), free_pages() ==== 메모리 풀 ==== * 사전에 예측되는 최소한의 메모리를 미리 할당하고, 관리하는 방식? * 절대 할당이 실패하지 않음. === 6장. 디바이스의 등록과 해제 === ==== 디바이스 드라이버 제어 방식 ==== * 응용 프로그램이 하드웨어를 제어하기 위해서는 저수준 파일 입출력 함수를 사용 디바이스 파일에 접근하면 그에 해당되는 디바이스 드라이버에 제어 함수들이 호출이 되어서 하드웨어를 다루게 된다. * 문자, 블록, 네트워크 * 문자 디바이스 드라이버는 저수준 파일 입출력에 대응하는 하드웨어를 다루는 커널 함수의 집합 ==== struct file_operations ==== * 파일 입출력 함수에 해당하는 함수들을 등록시키는 구조체 ==== 문자 디바이스 드라이버의 등록과 해제 및 구성 ==== === 7장. 디바이스 드라이버의 초기화와 종료 === ==== 초기화와 종료처리 ==== * 디바이스 드라이버에 모듈 초기화 시점, 디바이스를 여는 시점, 모듈 제거 시점, 디바이스를 닫는 시점 * insmod 명령 : module_init - 모듈 적재 과정 * open()함수 : file_operations.open - 디바이스 파일을 여는 과정 * close()함수 : file_operations.release - 디바이스 파일을 닫는 과정 * rmmod 명령 : module_exit - 모듈 제거 과정 ==== 모듈 사용 횟수 관리 ==== ==== I/O 영역의 경쟁 처리 함수 ==== === '''''Check Point''''' === ==== Makefile ==== {{{#!vim 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에 위치한)만 모듈로 컴파일을 수행하는 것 같다. * 역시 리눅스는 심오. 이런식으로 연동이 되나니 대단~~ ==== 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씩 변한 것을 볼수 있다. 각각의 의미를 더 정확하게 파악할 필요가 있을 것 같다. * '''''추가조사 필요'''''' ==== volatile test ==== * volatile을 했을 때랑 안 했을 때랑 어셈블리 코드의 변화 확인! ==== read(), write(), llseek() 구현 ==== * 실제로 동작하는 것 처럼 보이는 예제 코드 작성해보기.. = 게시판 = [[comment(@PAGE)]]