Presentation is loading. Please wait.

Presentation is loading. Please wait.

박상원 (swpark@hufs.ac.kr) 한국외국어대학교 정보통신공학과 데이터베이스 응용 특강 박상원 (swpark@hufs.ac.kr) 한국외국어대학교 정보통신공학과.

Similar presentations


Presentation on theme: "박상원 (swpark@hufs.ac.kr) 한국외국어대학교 정보통신공학과 데이터베이스 응용 특강 박상원 (swpark@hufs.ac.kr) 한국외국어대학교 정보통신공학과."— Presentation transcript:

1 박상원 (swpark@hufs.ac.kr) 한국외국어대학교 정보통신공학과
데이터베이스 응용 특강 박상원 한국외국어대학교 정보통신공학과

2 Contents Linux device driver Flash translation layer MTD

3 Linux Device Driver

4 1. 실습 환경 설정 및 준비

5 Linux의 특징 공짜 모든 구성 요소를 완전히 재구성할 수 있다 값싼 하드웨어에서도 동작 강력하다
소스 코드 품질을 위해 표준을 따른다 커널은 매우 작고 아담하게 만들 수도 있다 여러 대중적인 운영체제와 잘 호환된다 지원이 잘 된다

6 Linux Kernel의 특징 Monolithic kernel Module Kernel thread
microkernel Module Kernel thread Multithread 응용 프로그램 지원 Preemptive vs. Nonpreemptive Multiprocessor File system ext2fs, ext3fs (journaling file system)

7 Linux Linux version 2.4.x 2.5.x 2.6.x cat /proc/version Kernel

8 실습 기자재 준비 프린터 포트 총 25핀, 17개의 신호선과 8개의 접지선
신호선 : 4개의 제어선, 5개의 상태선, 8개의 데이터선 각 레지스터는 PC의 I/O 주소 공간에 매핑 0x378, 0x379, 0x37A 프린터 포트의 인터럽트는 7번 사용

9

10

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

12 리눅스의 역사 Linux GNU Project Linus-B. Torvalds에 의해 개발 Minix 1991년 0.02 버전
1994년 1.0 버전 현재 2.6 버전 GNU Project FSF(Free Software Foundation) 리차드 스톨만 GCC, Emacs 등의 다양한 유틸리티 GPL(General Public License)

13 리눅스 커널의 특징 Monolithic kernel 비선점형(커널 2.4)과 선점형(커널 2.6) 가상 메모리 시스템(VM)
MMU 없는 시스템도 지원 가상 파일 시스템(VFS) 모듈을 이용한 커널 확장 커널 스레드 멀티스레드 지원 멀티 프로세서 지원 강력한 네트워크 지원 GPL 라이센스

14 Device Driver 시스템이 지원하는 하드웨어를 응용 프로그램에서 사용할 수 있도록 커널에서 제공하는 라이브러리
시스템이 지원하는 하드웨어를 응용 프로그램에서 사용할 수 있도록 커널에서 제공하는 라이브러리 하드웨어는 커널의 입장에서는 자원 응용 프로그램이 커널에게 자원 처리를 요청하는 방법 System call 파일 입출력 형식을 이용한 device driver

15 시스템 호출 방식 소프트웨어 인터럽트 서비스 이용 각 기능별로 번호를 할당
이 번호에 해당하는 제어 루틴을 커널 내부에 정의 응용 프로그램은 원하는 기능 번호를 레지스터에 저장한 후 소프트웨어 인터럽트 서비스를 호출하여 커널에 제어 를 넘김 처음 운영체제가 설계될 당시에는 시스템 호출 방 식만 존재 하드웨어 증가에 따른 한계

16 파일 형식의 디바이스 드라이버 파일 입출력 함수로 하드웨어 제어 하드웨어를 표현하는 디바이스 파일
응용 프로그램이 입출력을 시도 커널 내의 해당 디바이스 파일에 연결된 디바이스 드라이 버의 서비스 루틴 호출

17 모듈 초기에는 디바이스 드리이버를 커널 소스에 포함시켜 야
긴 커널 컴파일 시간 수정할 때마다 시스템 재부팅 커널이 부팅되어 동작중인 상태에서 디바이스 드라이 버를 동적으로 추가하거나 제거 디바이스 드라이버 개발 시간 단축 필요없는 기능을 커널에서 제거하여 커널 자원의 효율적 사용 MMU가 있는 시스템에서만 지원, 커널 버전이 동일해 야

18 디바이스 파일 시스템의 모든 자원을 파일 형식으로 표현 디바이스 파일 정보 램, 프로세스, 태스크 등
예) /dev/mouse, /dev/console 디바이스 파일 정보 문자/블록 주번호, 부번호 mknod로 생성

19 디바이스 드라이버의 종류 문자 디바이스 드라이버 블록 디바이스 드라이버 네트워크 디바이스 드라이버
임의의 길이를 갖는 문자열을 다루는 디바이스 드라이버 응용 프로그램에서 직접적으로 호출 버퍼없음 블록 디바이스 드라이버 일정 크기의 버퍼를 통해 데이터를 처리하는 디바이스 커널 내부의 파일 시스템에서 관리 내부적인 버퍼가 있음 네트워크 디바이스 드라이버 네트워크 층과 연결되는 디바이스 드라이버

20 문자 디바이스 드라이버 일반 파일과 가장 유사한 방식으로 처리 파일 포인터를 이용하여 특정 처리 위치 지정
open, close, read, write 함수 이용 파일 포인터를 이용하여 특정 처리 위치 지정 응용 프로그램의 호출과 1:1로 대응 대부분의 하드웨어는 문자 디바이스 드라이버로 구현 가능

21 블록 디바이스 드라이버 파일 시스템을 지원하는 구조이므로 응용 프로그 램은 파일 시스템을 통해 접근
효율적인 처리를 위해 커널 내부 버퍼 이용 블록 처리도 가능하지만 스트림 처리도 가능 open, close, read, write 함수 이용하여 접근 가능

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

23 디바이스 파일 ls /dev/ mknod [디바이스 파일명][디바이스 파일형][주번 호][부번호]
hdj nst28a sdam sdcf8 sddz9 sdft sdhm1 sdt10 ttySR xdb54 hdj nst28l sdam sdcf9 sde sdft1 sdhm10 sdt11 ttySR xdb55 hdj nst28m sdam sdcg sde1 sdft10 sdhm11 sdt12 ttySR xdb56 hdj nst sdan sdcg1 sde10 sdft11 sdhm12 sdt13 ttySR xdb57 hdj nst29a sdan sdcg10 sde11 sdft12 sdhm13 sdt14 ttySR xdb58 hdj nst29l sdan sdcg11 sde12 sdft13 sdhm14 sdt15 ttySR xdb59 hdj nst29m sdan sdcg12 sde13 sdft14 sdhm15 sdt2 ttySR xdb6 hdk nst2a sdan sdcg13 sde14 sdft15 sdhm2 sdt3 ttySR xdb60 hdk nst2l sdan sdcg14 sde15 sdft2 sdhm3 sdt4 ttySR xdb61 mknod [디바이스 파일명][디바이스 파일형][주번 호][부번호] mknod /dev/devfile c 240 1

24 파일 입출력 함수 파일 입출력 함수 기능 fopen, open 파일을 연다 fread, read 파일을 읽는다
fwrite, write 파일에 데이터를 쓴다 fclose, close 파일을 닫는다

25 저수준 파일 입출력 함수 시스템 콜을 라이브러리 함수로 만든 것 저수준 파일 입출력 함수 기능 open( )
파일이나 장치를 연다 close( ) 열린 파일을 닫는다 read( ) 파일에서 데이터를 읽어온다 write( ) 파일에 데이터를 쓴다 lseek( ) 파일의 쓰기나 읽기 위치를 변경한다 ioctl( ) read( ), write( )로 다루지 않는 특수한 제어를 한다 fsync( ) 파일에 쓴 데이터와 실제 하드웨어의 동기를 맞춘다

26 파일 열고 닫다: open( ), close( )
int fd = -1; fd = open(“/dev/mem”, O_RDWR | O_NONBLOCK); if (fd < 0) { // 에러 처리 } close(fd); O_NONBLOCK, O_NDELAY 읽기나 쓰기가 완료되지 않더라도 저수준 파일 입출력 함 수가 즉시 종료되도록

27 파일 읽고 쓰기: read( ), write( )
방법 ret_num = read(fd, buff, 10); ret_num = write(fd, buff, 10); if (ret_num < 0) { // 에러 처리 } if (ret_num != 10) { // 요구된 것과 다를 때 처리

28 파일 포인터 처리: lseek( ) Options SEEK_CUR SEEK_SET SEEK_END off_t ret_pos;
ret_pos = lseek(fd, 1234, SEEK_CUR); Options SEEK_CUR SEEK_SET SEEK_END

29 디바이스 제어: ioctl( ) 모든 제어를 read, write 만으로는 곤란 디바이스 파일에만 적용되는 연산
10장. 디바이스의 제어

30 동기 처리: fsync( ) 디바이스의 구현 방식에 따라 쓰는 방식 다를 수 있음 주의점 내부에 버퍼를 두는 경우
디바이스의 구현 방식에 따라 쓰는 방식 다를 수 있음 내부에 버퍼를 두는 경우 int ret; ret = fsync(fd); 주의점 실행 시간이 길어지거나 함수가 종료되지 않을 수 있음

31 디바이스 파일 생성: mknod( ) int mknod(const char *pathname, mode_t mode, dev_t dev); 다음 헤더를 포함해야 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> 디바이스 파일 종류 S_IFCHR : 문자 디바이스 S_IFBLK : 블록 디바이스 S_IRWXU : 사용자는 읽기 쓰기 권한이 있다 S_IRWXG : 그룹은 읽기 쓰기 권한이 있다 [ ] # mknod /dev/test c 240 1 mknod(“/dev/test”, S_IRWXU|S_IRWXG|S_IFCHR, (240<<8)|1);

32 에러 처리: perror( ) 프로그램의 디버깅에 사용 전역변수 참조 에러 번호를 해석하여 문자열로 표현
fd = open(“/dev/ram”, O_RDONLY); if (fd < 0) { perror(“open”); } 에러 번호를 해석하여 문자열로 표현 include/asm/errno.h

33 기자재 준비

34 /dev/port 디바이스 파일 함수 lseek : 접근할 I/O 주소를 지정 read : 지정된 I/O에서 데이터를 읽어온다
write : 지정된 I/O에 데이터를 쓴다 dev]# ls -al /dev/port crw-r root kmem 1, 4 1월 /dev/port [ ] # mknod /dev/port c 1 4

35 예제 int main( int argc, char **argv ) { int fd; int lp; unsigned char buff[128]; fd = open( "/dev/port", O_RDWR ); if( fd < 0 ) perror( "/dev/port open error" ); exit(1); }

36 for( lp = 0; lp < 10; lp++ ) { lseek( fd, 0x378, SEEK_SET ); buff[0] = 0xFF; write( fd, buff, 1 ); sleep( 1 ); buff[0] = 0x00; } close( fd ); return 0;

37 ioctl( ) 함수의 사용 예 int main( int argc, char **argv ) { int fd; int prnstate; int lp; unsigned char buff[128]; fd = open( "/dev/lp0", O_RDWR | O_NDELAY ); if( fd < 0 ) perror( "open error" ); exit(1); }

38 while( 1 ) { ioctl( fd, LPGETSTATUS, &prnstate ); // 13 Pin <--> GND Pin if( prnstate & LP_PSELECD ) printf( "ON\n" ); else printf( "OFF\n" ); usleep( ); } close( fd ); return 0;

39 4. 간단한 모듈 테스트

40 모듈 개념

41 hello world 모듈 (커널 2.4) #define MODULE #include <linux/module.h> #include <linux/kernel.h> int init_module(void) { printk("Hello, world\n"); return 0; } void cleanup_module(void) printk("Goodbye world\n");

42 Makefile (커널 2.4) KERNELDIR = /lib/modules/$(shell uname -r)/build CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O all: test.o clean: rm -rf *.o

43 실행 방법 (커널 2.4) [ ] # make [ ] # insmod test.o [ ] # dmesg [ ] # rmmod test

44 hello world 모듈 (커널 2.6) #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> static int hello_init(void) { printk("Hello, world \n"); return 0; } static void hello_exit(void) printk("Goodbye, world\n"); module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("Dual BSD/GPL");

45 Makefile (커널 2.6) obj-m := test.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o

46 실행 방법 (커널 2.6) [ ] # make [ ] # insmod test.ko [ ] # dmesg [ ] # rmmod test.ko

47 모듈의 구현 원리

48 커널에서 제공하는 심볼 테이블 [ ] # cat /proc/ksyms … #c0134a50 register_chardev_R1a5f156e #c0134ae0 unregister_chrdev_Rc192d491

49 모듈 유틸리티 유틸리티 기능 insmod 모듈을 커널에 적재한다. rmmod 커널에서 모듈을 제거한다. lsmod
커널에 적재된 모듈 목록을 보여준다. depmod 모듈간 의존성 정보를 생성한다. modprobe 모듈을 커널에 적재하거나 제거한다.

50 모듈 초기화 함수와 제거 함수 커널 2.4 커널 2.6 int init_module(void);
module_init(hello_init); void cleanup_module(void); module_exit(hello_exit);

51 커널 2.6의 라이선스 등록 MODULE_LICENSE(“Dual BSD/GPL”);
생략하거나 “Proprietary”를 사용하면 커널 내부의 몇몇 API에 접근할 수 없다 라이센스 풀어쓰기 GPL GNU Public License v2 or later GPL v2 GNU Public License v2 GPL and additional rights GNU Public License v2 rights and more Dual BSD/GPL GNU Public License v2 or BSD license choice Dual MPL/GPL GNU Public License v2 or Mozilla license choice Proprietary Non free products

52 모듈 매개변수 지정 모듈 매개변수 초기값 중에서 외부에서 변경할 수 있는 것 I/O 포트, IRQ 번호 등

53 모듈 매개변수 예제 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/moduleparam.h> static int onevalue = 1; static char *twostring = NULL; module_param(onevalue, int, 0); module_param(twostring, charp, 0);

54 static int hello_init(void) { printk("Hello, world [onevalue=%d:twostring=%s]\n", onevalue, twostring ); return 0; } static void hello_exit(void) printk("Goodbye, world\n"); module_init(hello_init); module_exit(hello_exit); MODULE_AUTHOR("You Young-chang MODULE_DESCRIPTION("Module Parameter Test Module"); MODULE_LICENSE("Dual BSD/GPL");

55 module_param module_param(변수명, 변수 타입, 접근 속성) 변수 타입 C 타입 short ushort
unsigned short int uint unsigned int long ulong unsigned long charp char * bool invbool intarray int *

56 실행 방법 [ ] # insmod test.ko onevalue=0x27 twostring=“Oh my godrmmod test” [ ] # rmmod test.ko [ ] # dmesg . Hello, world [ onevalue=38:twostring=Oh my godrmmod test] Goodbye, world

57 커널 메시지 출력: printk( ) 레벨 레벨을 표시하지 않을 경우 다음은 동일한 의미
printk(KERN_INFO “system ok\n”); printk(“<6>” “system ok\n”); printk(“<6> system ok\n”); 레벨을 표시하지 않을 경우 다음은 동일한 의미 printk(KERN_WARNING “system ok\n”); printk(“<4>” “system ok\n”); printk(“<4> system ok\n”); printk(“system ok\n”); 반드시 \n을 포함해야 출력 수행시간이 많이 걸리는 함수

58 사용가능한 레벨 상수 선언문 의미 KERN_EMERG “<0>” /* 시스템이 동작하지 않는다 */
KERN_ALERT “<1>” /* 항상 출력된다. */ KERN_CRIT “<2>” /* 치명적인 정보 */ KERN_ERR “<3>” /* 오류 정보 */ KERN_WARNING “<4>” /* 경고 정보 */ KERN_NOTICE “<5>” /* 정상적인 정보 */ KERN_INFO “<6>” /* 시스템 정보 */ KERN_DEBUG “<7>” /* 디버깅 정보 */

59 5. 메모리 할당과 해제

60 지역 변수와 전역 변수의 선택 지역 변수, 전역 변수 모두 커널 메모리 공간에 배 치 지역 변수 전역 변수
가급적 지역 변수를 사용하는 것이 좋다. 전역 변수 컴파일 시점에 크기와 주소가 정의 디바이스 드라이버가 커널에 적재되고 해제되는 시점까 지 유지해야 하는 정보

61 중복 함수명과 변수명 방지 변수 명이나 함수 명이 중복 정의되는 경우 static 으로 중복 방지
static int checkout = 0; // 변수 선언 예 static int dev_app_check(void) // 함수 선언 예 { }

62 이식성과 데이터형 32비트에서 64비트로 전환되는 시점 플랫폼간 호환성 사용하는 변수의 데이터 형을 명확히 기술

63 변수의 데이터형 가장 문제가 되는 타입 : int 크기는 프로세스에 의존적 16 비트 : 2 바이트 32 비트 : 32 비트
64 비트 : 64 비트 #include <asm/types.h> 부호 있는 정수 부호 없는 정수 __s8, s8 8비트 __u8, u8 __s16, s16 16비트(워드) __u16, u16 __s32, s32 32비트 __u32, u32 __s64, s64 64비트 __u64, u64

64 구조체 Alignment 무시한 구조체 필요 packed typedef struct { u16 index; u16 data;
} __attribute__ ((packed)) testctl_t;

65 바이트 순서 Little endian vs. Big endian #include <asm/byteorder.h>
#define __LITTLE_ENDIAN #define __BIG_ENDIAN

66 I/O 메모리 접근 변수 처리 메모리 번지를 이용하는 I/O 접근 처리 컴파일러 입장에서는 최적화 시도
u32 *ptr = (u32 *) 0xE ; *ptr = 0x1234; *ptr = *ptr & 0xFF; 컴파일러 입장에서는 최적화 시도 *ptr = 0x1245 & 0xFF; 항상 만족하게 하려면 최적화 방지 volatile u32 *ptr = (volatile u32 *) 0xE ;

67 동적 메모리 일반적인 C에서 #include <stdlib.h> char *buff;
buff = malloc(1024); if (buff == null) exit(1); . . . free(buf);

68 커널의 특수성 프로세스 사용자 메모리 공간과 다른 커널 메모리 공간 PAGE_SIZE 단위로 할당하는 특성
보통 4KB 시스템에 가용 메모리가 없는 상황 가상 메모리 기법에 의해 접근하고자 하는 메모리가 보 조 장치에 존재하는 경우 swapping DMA 같은 디바이스가 사용하는 연속된 물리 메모리 주 소가 필요한 경우 인터럽트 상태에서 메모리를 할당하는 경우 커널 내에 메모리가 부족하여 sleep 하면 안됨

69 메모리 할당 함수 kmalloc( ), kfree( ) __get_free_pages( ), free_pages( )
vmalloc( ), vfree( )

70 kmalloc( ), kfree( ) 할당 가능한 크기 : 32 x PAGE_SIZE
일반적으로 PAGE_SIZE는 4K이므로 bytes 이상 은 곤란 #include <linux/slab.h> char * buff; buff = kmalloc(1024, GFP_KERNEL); if (buff != NULL) kfree(buff);

71 kmalloc의 Options GFP_KERNEL GFP_ATOMIC GFP_DMA 동적 메모리 할당이 항상 성공하도록 요구
충분한 메모리가 없을 때 sleep 인터럽트 서비스에 사용할 때는 사용하면 안됨 GFP_ATOMIC 커널에 할당 가능한 메모리가 있으면 무조건 할당 없으면 즉시 NULL 반환 sleep 하는 경우가 없음 GFP_DMA 연속된 물리 메모리를 할당방을 때 사용 디바이스 드라이버가 동작하는 메모리 공간은 물리적인 메모리가 아닌 가상 주소 메모리  실제 물리적 공간은 분할되어 있을 수 있음 DMA 콘트롤러를 사용할 때

72 vmalloc( ), vfree( ) 가상 공간이 허용하는 한 크기 제한 없이 할당
가상 주소 공간이므로 할당할 메모리가 디스크에 있을 수 있음 커다란 연속 공간을 할당하기 위해 가상 메모리 관리 루틴이 실행 되므로 kmalloc 보다 느리다 인터럽트 서비스 함수 안에서 사용 불가 #include <linux/vmalloc.h> char *buff; buff = vmalloc(1024); . . . vfree(buff)

73 __get_free_pages( ), free_pages( )
페이지의 2의 승수 크기로 할당 MAX_ORDER 11 보통은 5 이하 (32 x PAGE_SIZE) char *buff; int order; order = get_order(8192); buff = __get_free_pages(GFP_KERNEL, order); . . . free_pages(buff, order);

74 동적 메모리 예제 void kmalloc_test( void ) { char *buff; printk( "kmalloc test\n" ); buff = kmalloc( 1204, GFP_KERNEL ); if( buff != NULL ) sprintf( buff, "test memory\n" ); printk( buff ); kfree( buff ); } buff = kmalloc( 32 * PAGE_SIZE, GFP_KERNEL ); printk( "Big Memory Ok\n" );

75 void vmalloc_test( void ) { char
void vmalloc_test( void ) { char *buff; printk( "vmalloc test\n" ); buff = vmalloc( 33 * PAGE_SIZE ); if( buff != NULL ) sprintf( buff, "vmalloc test ok\n" ); printk( buff ); vfree( buff ); }

76 void get_free_pages_test( void ) { char
void get_free_pages_test( void ) { char *buff; int order; printk( "get_free_pages test\n" ); order = get_order(8192*10); buff = __get_free_pages( GFP_KERNEL, order ); if( buff != NULL) sprintf( buff, "__get_free_pages test ok [%d]\n", order ); printk( buff ); free_pages(buff, order); }

77 int memtest_init(void) { char
int memtest_init(void) { char *data; printk("Module Memory Test\n" ); kmalloc_test(); vmalloc_test(); get_free_pages_test(); return 0; } void memtest_exit(void) printk("Module Memory Test End\n"); module_init(memtest_init); module_exit(memtest_exit); MODULE_LICENSE("Dual BSD/GPL");

78 메모리 풀(Memory Pool) 커널 2.6 엔터프라이즈 환경에 적합하도록 개선 시도
메모리가 부족해지면 가상 파일시스템이 동작하지만 느림 사전에 예측되는 최소한의 메모리를 미리 할당하고, 관리 관리자 생성과 소멸 API mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data); void mempool_destroy(mempool_t *pool); typedef void * (mempool_alloc_t)(int gfp_mask, void *pool_data); typedef void (mempool_free_t)(void *element, void *pool_data); 할당과 해제 API void *mempool_alloc(mempool_t *pool, int gfp_mask); void mempool_free(void *element, mempool_t *pool)

79 메모리 풀 mempool_alloc 함수로 할당 실패시 사전에 미리 할당받았던 메모리 반환
그래도 실패시는 gfp_mask 옵션에 따라 실패 혹 은 slepp

80 메모리 풀 예제 #define MIN_ELEMENT 4 #define TEST_ELEMENT 4 typedef struct { int number; char string[128]; } TMemElement; int elementcount = 0; void *mempool_alloc_test(int gfp_mask, void *pool_data) { TMemElement *data; printk( "----> mempool_alloc_test\n" ); data = kmalloc( sizeof( TMemElement ), gfp_mask ); if( data != NULL ) data->number = elementcount++; return data; }

81 void mempool_free_test(void. element, void
void mempool_free_test(void *element, void *pool_data) { printk( "----> call mempool_free_test\n" ); if( element != NULL ) kfree( element ); } int mempool_init(void) mempool_t *mp; TMemElement *element[TEST_ELEMENT]; int lp; printk("Module MEMPOOL Test\n" ); memset( element, 0, sizeof( element ) ); printk( "call mempool_create\n" ); mp = mempool_create( MIN_ELEMENT, mempool_alloc_test, mempool_free_test, NULL );

82 printk( "mempool allocate\n" ); for( lp=0; lp < TEST_ELEMENT; lp++ ) { element[lp] = mempool_alloc(mp, GFP_KERNEL ); if( element[lp] == NULL ) printk( "allocte fail\n" ); else { sprintf( element[lp]->string, "alloc data %d\n", element[lp]->number ); printk( element[lp]->string ); } printk( "mempool free\n" ); if( element[lp] != NULL ) mempool_free( element[lp], mp ); printk( "call mempool_destroy\n" ); mempool_destroy( mp ); return 0; void mempool_exit(void) { printk("Module MEMPOOL Test End\n"); module_init(mempool_init); module_exit(mempool_exit);

83 메모리 풀 결과 Module MEMPOOL Test call mempool_create ----> mempool_alloc_test mempool allocate alloc data 4 alloc data 5 alloc data 6 alloc data 7 mempool free ----> call mempool_free_test call mempool_destroy Module MEMPOOL Test End

84 6. 디바이스 등록과 해제

85 디바이스 드라이버 제어 방식 응용 프로그램 디바이스 파일 디바이스 드라이버 사용자 공간에서 프로세스로 동 작
하드웨어에 직접 접근 못함 디바이스 파일 디바이스 드라이버 문자 디바이스 드라이버의 경우 응용 프로그램에서 해당 디바이 스 드라이버와 연결된 디바이스 파일을 통해 호출 블록 디바이스 드라이버나 네트 워크 디바이스 드라이버는 커널 에서 직접 호출

86 디바이스 드라이버의 종류

87 문자 디바이스 드라이버 동작 시리얼 입출력 시작과 끝이 없음 처리 용량, 보존 여부 불명확

88 문자 디바이스 드라이버 호출 구조

89 디바이스 드라이버 호출 커널은 디바이스 파일에 기록된 디바이스 타입과 주번호를 이용해
커널 내에 등록된 디바이스 드라이버 함수를 연결 struct char_device_struct chrdevs[MAX_PROBE_HASH]; 여기에 struct file_operations *fops 필드 관리

90 struct file_operations
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); };

91

92 open 반환값 : 정상 (0), 오류값

93 close 반환값 : 성공 (0), 오류값

94 read xxx_read에 전달된 버퍼의 주소값은 프로세스 메모리 공간의 주소이므로 직접 사용할 수 없음
파일 포인터 f_pos 관리 반환값 : 처리된 데이터 수나 오류 값

95 write xxx_write에 전달된 버퍼의 주소값은 프로세스 메모리 공간의 주소이므로 직접 사용할 수 없음
파일 포인터 f_pos 관리 반환값 : 처리된 데이터 수 혹은 오류값

96 lseek

97 ioctl

98 문자 디바이스 드라이버의 등록과 해제 및 구성 file_operations 구조체 커널에 등록
register_chrdev : 디바이스 등록 unregister_chrdev : 등록된 디바이스 제거 int register_chrdev(unsigned int major, const char *name, struct file_operations *fops) major : 주번호, 응용 프로그램에서 디바이스 파일을 이용해 디바 이스 드라이버를 찾을 때 사용 name : 디바이스 드라이버 명 fops : 함수 포인터 int unregister_chrdev(unsigned int major, const char *name)

99 문자 디바이스 드라이버의 구성 int xxx_open(struct inode *inode, struct file *filp) { } // close 처리 int xxx_release(struct inode *inode, struct file *filp) struct file_operations xxx_fops = { .owner = THIS_MODULE, .open = xxx_open, .close = xxx_release, }; int xxx_init(void) register_chrdev( 240, “char_dev”, &xxx_fops ); void xxx_exit(void) unregister_chrdev( 240, “char_dev” ); module_init(xxx_init); module_exit(xxx_exit);

100 문자 디바이스 드라이버의 등록과 해제

101 문자 디바이스 드라이버 작성 예제 #define CALL_DEV_NAME "calldev" #define CALL_DEV_MAJOR 240 int call_open (struct inode *inode, struct file *filp) { int num = MINOR(inode->i_rdev); printk( "call open -> minor : %d\n", num ); return 0; } loff_t call_llseek (struct file *filp, loff_t off, int whence ) printk( "call llseek -> off : %08X, whenec : %08X\n", off, whence ); return 0x23;

102 ssize_t call_read(struct file. filp, char. buf, size_t count, loff_t
ssize_t call_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { printk( "call read -> buf : %08X, count : %08X \n", buf, count ); return 0x33; } ssize_t call_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) printk( "call write -> buf : %08X, count : %08X \n", buf, count ); return 0x43; int call_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) printk( "call ioctl -> cmd : %08X, arg : %08X \n", cmd, arg ); return 0x53; int call_release (struct inode *inode, struct file *filp) printk( "call release \n" ); return 0;

103 struct file_operations call_fops = {. owner = THIS_MODULE,
struct file_operations call_fops = { .owner = THIS_MODULE, .llseek = call_llseek, .read = call_read, .write = call_write, .ioctl = call_ioctl, .open = call_open, .release = call_release, }; int call_init(void) int result; printk( "call call_init \n" ); result = register_chrdev( CALL_DEV_MAJOR, CALL_DEV_NAME, &call_fops); if (result < 0) return result; return 0; } void call_exit(void) printk( "call call_exit \n" ); unregister_chrdev( CALL_DEV_MAJOR, CALL_DEV_NAME ); module_init(call_init); module_exit(call_exit);

104 응용 프로그램 #define DEVICE_FILENAME "/dev/calldev" int main() { int dev; char buff[128]; int ret; printf( "1) device file open\n"); dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY ); if( dev >= 0 ) printf( "2) seek function call\n"); ret = lseek( dev, 0x20, SEEK_SET ); printf( "ret = %08X\n", ret ); printf( "3) read function call\n"); ret = read(dev,0x30, 0x31 );

105 printf( "4) write function call\n");
ret = write(dev,0x40,0x41 ); printf( "ret = %08X\n", ret ); printf( "5) ioctl function call\n"); ret = ioctl(dev, 0x51, 0x52 ); printf( "6) device file close\n"); ret = close(dev); } return 0;

106 결과 [ ] # mknod /dev/calldev c [ ] # insmod call_dev.ko [ ] # lsmod Module Size Used by call_dev autofs [ ] # gcc –o call_app call_app.c [ ] # ./call_app 1) device file open 2) seek function call ret = ) read function call ret = ) write function call ret = ) ioctl function call ret = ) device file close

107 결과 [ ] # dmesg call call_init call open -> minor : 32 call llseek -> off : , whenec : call read -> buf : , count : call write -> buf : , count : call ioctl -> cmd : , arg : call release [ ] # rmmod call_dev

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

109 초기화와 종료 처리 디바이스 드라이버의 등록과 해제 디바이스 드라이버의 내부 구조체의 메모리 할당과 해제
여러 프로세스가 하나의 디바이스에 접근할 때 필요한 사전 처리 및 종료 시 처리 하드웨어 검출 처리 및 에러 처리 하드웨어 초기화와 제거 가능한 하드웨어의 제거 처리 응용 프로그램에서 디바이스 드라이버를 사용하는 경우 의 초기 처리 및 사용 종료 처리 부 번호에 관련된 프로세스별 처리 프로세스별 메모리 할당과 해제 사용하는 모듈 수의 관리

110 초기화와 종료 시점 모듈 적재와 커널 부팅 처리 과정 또는 제거 과정 응용 프로그램이 디바이스 파일을 여는 과정과 닫는 과 정
insmod 명령 : module_init – 모듈 적재 과정 rmmod 명령 : module_exit – 모듈 제거 과정 응용 프로그램이 디바이스 파일을 여는 과정과 닫는 과 정 open( ) 함수 : file_operations.open – 디바이스 파일을 여는 과 정 close( ) 함수 : file.operations.release – 디바이스 파일을 닫는 과정

111 module_init의 초기화 처리 디바이스 드라이버의 등록 디바이스 드라이버에 내부 구조체의 메모리 할당
전역 변수 등의 할당받은 메모리 초기화 여러 프로세스가 하나의 디바이스에 접근하는 경우에 필 요한 사전 처리 20장. 다중 프로세스 환경의 디바이스 드라이버 주 번호에 종속된 부 번호를 관리하기 위한 사전 처리 9장. 주 번호와 부 번호의 처리 하드웨어 검출 처리 및 에러 처리 하드웨어가 검출되지 않거나 수행이 곤란한 에러가 발생할 시 모 듈이 커널에 적재되는 시점에 거부하는 것이 좋음 하드웨어 초기화

112 module_exit의 종료 처리 디바이스 드라이버의 해제 디바이스 드라이버에 할당된 모든 메모리 해제
커널 자원을 점유하고 있기 때문에 반드시 해제 디바이스 드라이버에 할당된 모든 메모리 해제 적절히 제거하지 않으면 가용 메모리가 부족해 짐 하드웨어 제거에 따른 처리

113 처리 순서 static xxx_info *info = NULL; int xxx_init(void) { xxx_probe(... // 하드웨어 검출 처리 및 에러 처리 xxx_setup(... // 하드웨어 초기화 register_chrdev(... // 디바이스 드라이버의 등록 info = kmalloc(... // 디바이스 드라이버 동작에 필요한 메모리 할당 xxx_setupinfo(... // 여러 프로세스가 디바이스 하나에 접근하는 경우 사전 처리 xxx_setupminor(... // 주 번호에 종속된 부 번호를 관리하기 위한 사전 처리 } void xxx_exit(void) kfree(... // 디바이스 드라이버에 할당된 모든 메모리 해제 unregister_chrdev(... // 디바이스 드라이버 해제 xxx_shutdown(... // 하드웨어 제거에 따른 처리 module_init(xxx_init); module_exit(xxx_exit);

114 디바이스 드라이버의 open( ) 함수 int fd fd = open(DEVICE_FILENAME, O_RDWR|O_NDELAY); if (fd < 0) { printf(“error number %d”, error); exit(1); } struct file_operations call_fops = { .open = xxx_open, ... }; int xxx_open(struct inode *inode, struct file *filp) { int err = 0; // open 시 처리 내용들 return err;

115 open( ) 함수 호출 시 초기화 처리 디바이스 드라이버가 처음 열렸을 때 하드웨어 초 기화
디바이스 드라이버의 동작에 필요한 에러 체크 부 번호에 대한 처리가 필요한 경우 파일 오퍼레이 션 구조체 갱신 프로세스별 메모리 할당과 초기화 모듈의 사용 횟수 증가 (커널 2.4)

116 디바이스 드라이버의 release( ) 함수
if (fd >= 0) close(fd); struct file_operations call_fops = { .release = xxx_release, ... }; int xxx_release(struct inode *inode, struct file *filp) { // close 시 처리 내용 return 0; }

117 release( ) 함수 호출 시 종료 처리 프로세스별 할당 메모리 해제 모듈 사용 횟수 감소 (커널 2.4)

118 8. 디바이스 드라이버의 읽기와 쓰기

119 디지털 입출력을 처리하는 디바이스파일 “/dev/dio”에 데이터를 써넣거나 읽기
read() 함수의 구현 (1) int fd; char buff[128]; fd = open(“/dev/dio”, O_RDWR|O_NDElAY); read(fd, buff, 16); // 하드웨어 데이터 읽기 write(fd, buff, 32); // 하드웨어 데이터 쓰기 close(fd); 디바이스파일의 주 번호에 의해 연결된 커널 내에 디바이스 드라이버 file_operations 구조체 함수 필드 중 read 필드와 write필드가 각각 호출됨 디지털 입출력을 처리하는 디바이스파일 “/dev/dio”에 데이터를 써넣거나 읽기

120 read() 함수의 구현 (2) ssize_t xxx_read(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { // 하드웨어에서 데이터 읽기 } ssize_t write(struct file *filp, const char *buf, size_t count, loff_t *fpos) { // 하드웨어에 데이터 쓰기 struct file_operations xxx_fops = { .read = xxx_read, .write = xxx_write, }; 사용자 메모리 공간과 커널 메모리 공간 사이의 데이터 이동 처리 조건이 되지 않을 때의 블록 처리 하드웨어 제어 함수 여러 프로세스가 동시에 접근했을 때의 경쟁 처리 인터럽트 서비스 함수와의 경쟁 처리 디바이스 드라이버의 형식

121 read() 함수의 구현 (3) 하 드 웨 어 read write 디바이스 드라이버 읽기 쓰기의 구조
read(fd,buff,size) I/O 제어함수 inb, inw, inl .. readb .. read 메모리 복사 함수 put_user, copy_to_user Device file file_operation interrupt service buffer write(fd,buff,size) 메모리 복사 함수 get_user, copy_from_user write I/O 제어함수 outb, outw, outl .. writeb .. 디바이스 드라이버 읽기 쓰기의 구조

122 read() 함수의 구현 (4) 데이터 전달 함수 copy_to_user(to, from, n) put_user(x, ptr)
fd = open( const char *pathname, int flags); ret = read(int fd, void *buf, size_t count); 응용프로그램에서 전달한 버퍼의 주소 ssize_t xxx_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { return ret; } 응용프로그램에서 요청한 데이터의 크기 read() 함수 매개변수의 의미

123 read() 함수의 구현 (5) 응용 프로그램으로의 데이터 전달
시간적으로 굳이 동기화가 필요없는 간단한 하드웨어 처리 장치라면 직접 하드웨어에서 읽어서 전달하는 구조로 작성 시간적으로 민감한 경우라면 인터럽트를 이용한 버퍼구조 로 작성 개발자의 설계방식에 따라 선택적 하드웨어를 다룬다면 다음 함수 이용 inb, inw, inl, insb, insw, insl, outb, outw, outl, outsb, outsw, outsl readb, readw, readl, writeb, writew, writel, memset_io, memcpy_fromio, memcpy_toio 응용프로그램으로의 데이터 전달

124 read() 함수의 구현 (6) 디바이스 드라이버의 read() 함수가 반환하는 값은 0 이상인 경우와 0 미만인 경우
이 값은 디바이스 드라이버의 read()함수가 응용프로그램에 전달한 데이터의 개수(count)가 됨 Read() 함수가 수행되는 시점에 하드웨어에서 발생한 데이터 의 개수가 count 보다 적을 경우 count 값이 만족될 때까지 기다릴것인지 아니면 발생된 데이 터만 처리하고 종료할 것인지는 응용프로그램에서 디바이스 파일을 열었을 때 어떤 옵션을 주었는가에 따라 달라짐. read()함수의 매개변수중에서 struct file *filp를 참조하여 판 단 read() 함수의 반환하는 값

125 하드웨어에서 발생하는 데이터의 개수가 count 보다 적을 경우
read() 함수의 구현 (7) ssize_t xxx_read(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { If(filp->f_flags & O_NONBLOCK) { // 즉시 처리한다. } else // 블록 처리한다. 응용프로그램이 O_NONBLOCK이나 O_NDELAY를 지정한 상태로 디바이스 파일을 열었다면 현재 발생된 데이터만 버퍼에 써넣고 함수 종료. 그렇지 않으면 count 값이 만족될 때까지 기다려야 함. 하드웨어에서 발생하는 데이터의 개수가 count 보다 적을 경우

126 하드웨어에서 발생하는 데이터의 개수가 count 보다 적을 경우
read() 함수의 구현 (8) 반환값이 요구된 값과 항상 일치하지 않음 O_NONBLOCK이나 O_NDELAY 옵션을 주고 파일을 열었 을 때 read() 함수의 반환값이 음수값일 때 EAGAIN : O_NONBLOCK으로 열렸지만 즉시 읽을 수 있 는 데이터가 없슴. EIO : I/O 에러가 발생 EFAULT : buf는 접근할 수 없는 주소 공간을 가리킴 디바이스 드라이버를 읽기 / 쓰기 위치를 관리하는 형태로 작성해야 한다면 f_pos 매개변수를 처리해야 함. 하드웨어에서 발생하는 데이터의 개수가 count 보다 적을 경우

127 일반적인 디바이스 드라이버의 read()함수의 처리구조
ssize_t xxx_read(strcuct file *filp, const char *buf, size_t count, loff_t *f_pos) { if(!(준비된 데이터가 있는가?)) { if(!(filp->f_flags & O_NOBLOCK)) { // 블록 모드로 열렸다면 // 프로세스를 재운다. } // 하드웨어에서 데이터를 읽는다. // inb(), outb(),…., read(), write() 함수사용 // 또는 버퍼를 읽는다. // 사용자 공간에 데이터를 전달한다. // copy_to_user, put_user return 처리된 데이터 개수; ssize_t rdwr_read(strcuct file *filp, char *buf, size_t count, loff_t *f_pos) { unsigned char status; int loop; // 하드웨어에서 데이터를 읽는다. for(loop = 0; loop < count; loop++) { status = inb(RDWR_READ_ADDR); // 사용자 공간에 데이터를 전달한다. put_user(status, (char *) &buf[loop]; } // 처리된 데이터 개수 return count; 일반적인 디바이스 드라이버의 read()함수의 처리구조

128 write() 함수의 구현 (1) 데이터 전달 함수 copy_from_user(to, from, n)
get_user(x, ptr) fd = open( const char *pathname, int flags); ret = write(int fd, void *buf, size_t count); 응용프로그램에서 전달한 버퍼의 주소 ssize_t xxx_write(struct file *filp, char *buf, size_t count, loff_t *f_pos) { return ret; } 응용프로그램에서 요청한 데이터의 크기 write() 함수 매개변수의 의미

129 write() 함수의 구현 (2) 디바이스 드라이버의 write() 함수가 반환하는 값은 0 이상인 경우와 0 미만인 경우
이 값은 디바이스 드라이버의 write()함수가 응용프로그램에 전달한 데이터의 개수(count)가 됨 write() 함수가 수행되는 시점에 하드웨어에서 발생한 데이터 의 개수가 count 보다 적을 경우 count 값이 만족될 때까지 기다릴것인지 아니면 발생된 데이 터만 처리하고 종료할 것인지는 응용프로그램에서 디바이스 파일을 열었을 때 어떤 옵션을 주었는가에 따라 달라짐. write()함수의 매개변수중에서 struct file *filp를 참조하여 판단 write() 함수의 반환하는 값

130 하드웨어에서 발생하는 데이터의 개수가 count 보다 적을 경우
write() 함수의 구현 (3) 반환값이 요구된 값과 항상 일치하지 않음 O_NONBLOCK이나 O_NDELAY 옵션을 주고 파일을 열었 을 때 write() 함수의 반환값이 음수값일 때 EAGAIN : O_NONBLOCK으로 열렸지만 즉시 읽을 수 있 는 데이터가 없슴. EIO : I/O 에러가 발생 EFAULT : buf는 접근할 수 없는 주고 공간을 가리킴 ENOSPC : 데이터를 위한 공간이 없다. 디바이스 드라이버를 읽기 / 쓰기 위치를 관리하는 형태로 작성해야 한다면 f_pos 매개변수를 처리해야 함. 하드웨어에서 발생하는 데이터의 개수가 count 보다 적을 경우

131 filp 구조체 필드 중 디바이스 드라이버에서 자주 다루는 필드
struct file *filp struct file { unsigned int f_flags; loff_t f_pos; void *private_data; struct file_operation *f_op; }; 응용 프로그램에서 디바이스 파일을 open함수로 열었을 때 flags에 설정된 값. 현재의 읽기/쓰기 위치를 담음 프로세스가 함수간에 메모리를 공유할 목적이라면 적극 활용 부번호에 따라 다르게 동작하는 디바이스 드라이버를 작성할 수 있슴. filp 구조체 필드 중 디바이스 드라이버에서 자주 다루는 필드

132 I/O 처리 I/O mapped I/O memory mapped I/O 하드웨어에서 데이터를 읽음.
inb(), inw(), inl(), insb(), insw(), insl() 하드웨어에서 데이터를 씀. outb(), outw(), outl(), outb(), outw(), outsl() 사용하려면 #include <asm/io.h>를 소 스에 포함(대부분 매크로 함수) b(8bit), w(16bit), l(32bit)로 처리하는 시스템의 I/O 버스폭과 관련 스트림 I/O 명령들은 뒤에 s가 붙어 있 는것을 사용 하드웨어에서 데이터를 읽음. readb(), readw(), readl(), memcpy_fromio() 하드웨어에서 데이터를 씀. writeb(), writew(), writel(), memcpy_toio() 사용하려면 #include <asm/io.h>를 소스에 포함(대부분 매크로 함수)

133 사용자 메모리 공간과 커널 메모리 공간 사용자 메모리 공간 커널 메모리 공간 일반적인 동작 상태 의미
프로세스 #1 (사용자 모드) 프로세스 #1 (커널 모드) 일반적인 동작 상태 의미 시스템 전체를 처리하는 상태 의미 시스템 콜 인터럽트 복귀 프로세스 #2 (사용자 모드) 프로세스 #2 (커널 모드) 저수준 파일 입출력함수(소프트웨어 인터럽트를 이용) 커널 모드에서 사용자 메모리 공간 접근 verify_area(type, addr, size) copy_to_user(to, from, n) copy_from_user(to, from, n) get_user(x, ptr) put_user(x, ptr) 시스템 콜 인터럽트 복귀 프로세스 #3 (사용자 모드) 프로세스 #3 (커널 모드) 시스템 콜 인터럽트 복귀

134 rdwr_dev.c #define RDWR_DEV_NAME "rdwrdev" #define RDWR_DEV_MAJOR 240 #define RDWR_WRITE_ADDR 0x0378 #define RDWR_READ_ADDR 0x0379 int rdwr_open (struct inode *inode, struct file *filp) { return 0; } ssize_t rdwr_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) unsigned char status; int loop; for( loop = 0; loop < count; loop++ ) status = inb( RDWR_READ_ADDR ); put_user( status, (char *) &buf[loop] ); return count;

135 ssize_t rdwr_write (struct file. filp, const char
ssize_t rdwr_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) { unsigned char status; int loop; for( loop = 0; loop < count; loop++ ) get_user( status, (char *) buf ); outb( status , RDWR_WRITE_ADDR ); } return count; int rdwr_release (struct inode *inode, struct file *filp) return 0; struct file_operations rdwr_fops = .owner = THIS_MODULE, .read = rdwr_read, .write = rdwr_write, .open = rdwr_open, .release = rdwr_release, };

136 int rdwr_init(void) { int result; result = register_chrdev( RDWR_DEV_MAJOR, RDWR_DEV_NAME, &rdwr_fops); if (result < 0) return result; return 0; } void rdwr_exit(void) unregister_chrdev( RDWR_DEV_MAJOR, RDWR_DEV_NAME ); module_init(rdwr_init); module_exit(rdwr_exit); MODULE_LICENSE("Dual BSD/GPL");

137 int main() { int dev; char buff[128]; int loop; dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY ); if( dev >= 0 ) printf( "wait... input\n" ); while(1) { if( read(dev,buff,1 ) == 1 ) { printf( "read data [%02X]\n", buff[0] & 0xFF ); if( !(buff[0] & 0x10) ) break; } printf( "input ok...\n"); printf( "led flashing...\n"); for( loop=0; loop<5; loop++ ) { buff[0] = 0xFF; write(dev,buff,1 ); sleep(1); buff[0] = 0x00; close(dev); return 0;

138 9. 주 번호와 부 번호의 처리

139 1. 주 번호, 부 번호, 디바이스타입 (1/5) 주 번호 리눅스 파일 디렉토리 구조로 알려진 레이블의 계층구조에 의해서 구성
레이블에 의해 참조되는 파일 정규파일(regular file) 디렉토리 파일(directory file) 특수파일(special file) 커널 내부의 함수를 호출할 수 있는 정보 제공 디바이스 파일(타입정보, 주 번호, 부 번호) mknod /dev/xxx c M m 디바이스 드라이버 함수는 응용프로그램에서 직접 호출하여 사용되지 않는다. 이것은 멀티 태스킹을 지원하기 때문이다. 두개 이상의 서로 다른 응용프로그램에서 사용가능하기 때문에 직접 제어를 하면 다른 응용프로그램에 영향을 줄 수 있다. 따라서 커널이 중간자적인 입장에서 응용프로그램이 커널에게 요청하면 디바이스 제어함수를 호출하도록 한다. 그림 이때 커널이 하드웨어 제어하거나 커널내부에서 특정기능을 수행하는 함수를 사용하기 위해서 디바이스 파일 개념이 제공된다. 리눅스 파일 시스템을 조금 알아야 한다. 리눅스 파일은 보통 디렉토리 구조로 알려진 레이블의 계층 구조에 의해서 구성된다. 이 레이블에 의해 참조되는 파일은 세가지 종류중에 하나이다. 정규화일(regular file) – 일반적으로 데이터 또는 코드에 해당하는 일련의 바이트를 포함한다. 표준 입출력 시스템 호출을 통해 참조된다. 디렉토리화일(directory file) – 특별한 형식으로 디스크에 저장되면 파일 시스템의 중추를 이룬다. 이 파일은 디렉토리 명시적 시스템 호출을 통해서만 참조된다. 특수화일(special file) –주변장치나 파이프와 소켓 같은 프로세스 간 상호 통신 메커니즘에 해당된다. 표준 입출력 시스템 호출을 통해 참조된다. 디바이스 파일은 특수화일 분류에 속한다. 이때 다양한 정보가 있지만 지금 필요한 정보는 디바이스 드라이버 타입정보, 주 번호, 부 번호 세가지 이다. 응용프로그램이 커널에 디바이스제어요청을 하면 커널은 타입정보와 주 번호를 통해 대항 디바이스드라이버를 호출한다. Open() 함수 디바이스 파일 열기하면 -> 파일 입출력 함수 정보, 디바이스 드라이버 타입정보, 주 번호 -> read() -> xxx_read()를 찾기위해 디바이스 드라이버 타입 정보를 확인한다. Chrdevs[]. Blkdevs[]인지 알기위해서말이다.이 배열의 인덱스로 주번호가 사용된다.-> xxx_read() 함수 실행 주 번호는 제어하려는 디바이스를 구분하기 위한 디바이스의 ID로 생각하면 될것이다. 그림9-1] 리눅스에서의 디바이스 제어 구조

140 1. 주 번호, 부 번호, 디바이스타입 (2/5)

141 1. 주 번호, 부 번호, 디바이스타입(3/5) Inode(/include/linux/fs.h) label inode #
파일의 유형:정규파일, 디렉토리, 특수 등 허가권 소유권과 그룹id 하드링크 계수 마지막 수정과 마지막 접근 시간 정규/디렉토리 파일 : 디스크 블록의 위치 특수 파일 : 주/보조 장치 번호 심볼릭 링크 : 심볼릭 링크의 값 Inode(/include/linux/fs.h) struct inode { unsigned long i_ino; atomic_t i_count; umode_t i_mode; unsigned int i_nlink; uid_t i_uid; gid_t i_gid; dev_t i_rdev; loff_t i_size; struct timespec i_atime; struct timespec i_mtime; struct timespec i_ctime; unsigned int i_blkbits; unsigned long i_blksize; unsigned long i_version; unsigned long i_blocks; unsigned short i_bytes; unsigned char i_sock; }; bin usr /2 bin usr4 ls5 cp test.c7 label inode #

142 1. 주 번호, 부 번호, 디바이스타입 (4/5) 부 번호 디바이스의 구분 COM1, COM2 구분
1. 주 번호, 부 번호, 디바이스타입 (4/5) 부 번호 디바이스의 구분 용도에 따른 디바이스의 구분(misc 계열 디바이스) 블록 디바이스의 파티션 구분 COM1, COM2 구분 misc_register(&xxx_miscdev) misc_deregister(&xxx_miscdev) struct miscdevice{ int minor; const char *name; struct file_operation *fops; struct miscdevice *next, *prev; devfs_handle_t devfs_handle; } 주 번호는 커널에 의해 관리되고, 부 번호는 각 디바이스 드라이버에 의해 용도가 결정된다. 세가지 용도가 있다.1,2,3 용도 결정은 개발자의 몫이다. 디바이스 구분은 시리얼을 보면 다음과 같다. Com1, Com2 등의 같은 디바이스이지만 물리적으로 구분할때 주번호는 같고 부번호는 다르다. 주번호가 256개밖에 안되는데 계속적인 주변장치 증가로 확장개념으로 부번호를 사용한다. Misc 계열 디바이스 드라이버를 들 수 있다. Misc 계열만의 디바이스 특성을 커널에 반영하기 위해 문자디바이스와는 다른 등록함수를 사용한다.

143 1. 주 번호, 부 번호, 디바이스타입 (5/5) kdev_t (2.4) dev_t (2.6) 디바이스 타입
1. 주 번호, 부 번호, 디바이스타입 (5/5) 디바이스 타입 kdev_t (2.4) “include/linux/kdev_t.h” dev_t (2.6) “include/linux/coda.h” typedef unsigned short kdev_t; #define MINORBITS 8 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define HASHDEV(dev) ((unsigned int) (dev)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) typedef unsigned long dev_t; #define MINORBITS 20 typedef unsigned short kdev_t; #define MINORBITS 8 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define HASHDEV(dev) ((unsigned int) (dev)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) typedef unsigned long dev_t; #define MINORBITS 20

144 2. 부 번호에 의한 파일 처리 방법 struct file_operations minor0_fops = { // 부 번호가 1일 경우 처리하는 파일 오퍼레이션 …. }; struct file_operations minor1_fops = { // 부 번호가 2일 경우 처리하는 파일 오퍼레이션 ….}; Int minor_open(struct inode *inode, struct file *filp) { switch(MINOR(inode->i_rdev)) case 1: filp->f_op = &minor0_fops; break; case 2: filp->f_op = &minor1_fops; break; default : return –ENXIO; } if(filp->f_op && filp->f_op->open) return filp->f_op->open(inode, filp); retun 0; struct file_operations master_fops= { .open = minor_open, }; Int xxx_init(void) int result; result = register_chrdev(MINOR_DEV_MAJOR, MINOR_DEV_NAME, &master_fops); 부번호는 디바이스 드라이버와 관련된 것으로 커널에서는 따로 처리하지 않는다. 부번호에 따라 다르게 동작하는 디바이스 드라이버를 작성하는 방법에 대해 알아본다. 응용프로그램에서 open()하면 디바이스 드라이버 등록했을때 파일 오퍼레이션 필드의 open() 필드 에 정의된 함수 호출한다. 부 번호에 의한 파일 오퍼레이션의 재지정 처리는 이 xxx_open()함수에 처리한다.

145 3. 부 번호에 의한 파일 처리 예제 (1/2) 프린터 포트 출력과 입력 예제 소개한다. #include <>
3. 부 번호에 의한 파일 처리 예제 (1/2) #include <> #define READ_DEVICE_FILENAME “/dev/minor_read” #define WRITE_DEVICE_FILENAME “/dev/minor_write” Int main() { … read_dev = open(READ_DEVICE_FILENAME, O_RDWR|O_NDELAY); write_dev = open(WRITE_DEVICE_FILENAME, O_RDWR|O_NDELAY); while(1){ if(read(read_dev,buff,1) == 1) // 클립 접촉여부 확인 { printf(“ read data…”); if(!(buff[0] & 0x10)) break; } for(loop=0;loop<5;loop++) { // LED ON wirte(write_dev,buff,1); close(read_dev); close(write_dev); return 0; #include <> Int minor0_open(){…} Ssize_t minor0_write(){…} Int minor0_release(){…} Int minor1_open(){…} Ssize_t minor1_read(){…} Int minor1_release(){…} Struct file_operations minor0_fops = {…} Struct file_operations minor1_fops = {…} Int minor_open() { switch(MINOR(inode->i_rdev)) case 1: filp->f_op = &minor0_fops; break; case 2: filp->f_op = &minor1_fops; break; default : return –ENXIO; } if(filp->f_op && filp->f_op->open) return filp->f_op->open(inode, filp); retun 0; Int init_module() Int cleanup_module() 프린터 포트 출력과 입력 예제 소개한다.

146 3. 부 번호에 의한 파일 처리 예제 (2/2) 실행방법 mknod /dev/minor_write c 240 1 mknod /dev/minor_write c 240 2 make 2.4 insmod minor_dev.o 2.6 insmod minor_dev.ko 참고) lsmod cat /proc/ksyms | grep <모듈이름> ./minor.app .. READ DATA [7F] READ DATA [6F] Input ok… led flashing… rmmod minor_dev dmesg 프린터 포트 출력과 입력 예제 소개한다.

147 4. 예약된 주 번호 Document/devices.txt 테스트나 특정 플랫폼용으로 할당된 주 번호와 부 번호 주 번호
테스트나 특정 플랫폼용으로 할당된 주 번호와 부 번호 주 번호 60~63 120~127 240~254 부 번호 주 번호 10번의 부 번호 240~255

148 #define MINOR_DEV_NAME "minordev" #define MINOR_DEV_MAJOR 240 #define MINOR_WRITE_ADDR 0x0378 #define MINOR_READ_ADDR 0x0379 int minor0_open (struct inode *inode, struct file *filp) { printk( "call minor0_open\n" ); return 0; } ssize_t minor0_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) unsigned char status; int loop; for( loop = 0; loop < count; loop++ ) get_user( status, (char *) buf ); outb( status , MINOR_WRITE_ADDR ); return count; int minor0_release (struct inode *inode, struct file *filp) printk( "call minor0_release\n" ); int minor1_open (struct inode *inode, struct file *filp) { printk( "call minor1_open\n" ); return 0; } ssize_t minor1_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) unsigned char status; int loop; for( loop = 0; loop < count; loop++ ) { status = inb( MINOR_READ_ADDR ); put_user( status, (char *) &buf[loop] ); return count; int minor1_release (struct inode *inode, struct file *filp) printk( "call minor1_release\n" ); struct file_operations minor0_fops = { .owner = THIS_MODULE, .write = minor0_write, .open = minor0_open, .release = minor0_release, };

149 struct file_operations minor1_fops = {. owner = THIS_MODULE,
struct file_operations minor1_fops = { .owner = THIS_MODULE, .read = minor1_read, .open = minor1_open, .release = minor1_release, }; int minor_open (struct inode *inode, struct file *filp) printk( "call minor_open\n" ); switch (MINOR(inode->i_rdev)) case 1: filp->f_op = &minor0_fops; break; case 2: filp->f_op = &minor1_fops; break; default : return -ENXIO; } if (filp->f_op && filp->f_op->open) return filp->f_op->open(inode,filp); return 0; struct file_operations minor_fops = .open = minor_open, int minor_init(void) { int result; result = register_chrdev( MINOR_DEV_MAJOR, MINOR_DEV_NAME, &minor_fops); if (result < 0) return result; return 0; } void minor_exit(void) unregister_chrdev( MINOR_DEV_MAJOR, MINOR_DEV_NAME ); module_init(minor_init); module_exit(minor_exit); MODULE_LICENSE("Dual BSD/GPL");

150 #define READ_DEVICE_FILENAME "/dev/minor_read" #define WRITE_DEVICE_FILENAME "/dev/minor_write" int main() { int read_dev; int write_dev; char buff[128]; int loop; read_dev = open( READ_DEVICE_FILENAME, O_RDWR|O_NDELAY ); if( read_dev < 0 ) printf( READ_DEVICE_FILENAME "open error\n" ); exit(1); } write_dev = open( WRITE_DEVICE_FILENAME, O_RDWR|O_NDELAY ); if( write_dev < 0 ) printf( WRITE_DEVICE_FILENAME "open error\n" ); close( read_dev ); printf( "wait... input\n" ); while(1) { if( read(read_dev,buff,1 ) == 1 ) printf( "read data [%02X]\n", buff[0] & 0xFF ); if( !(buff[0] & 0x10) ) break; } printf( "input ok...\n"); printf( "led flashing...\n"); for( loop=0; loop<5; loop++ ) buff[0] = 0xFF; write(write_dev,buff,1 ); sleep(1); buff[0] = 0x00; close(read_dev); close(write_dev); return 0;

151 10. 디바이스의 제어

152 10장 디바이스의 제어 01. 디바이스 제어 02. ioctl() 함수 사용 예 디바이스 제어 ioctl()함수의 역할
cmd관련 MACRO함수들 02. ioctl() 함수 사용 예

153 01. 디바이스 제어 디바이스 제어 ioctl() 함수의 역할 Input / Output Control
device driver와 application간에 범용적인 대화 통로 할당 받은 인터럽트 변경 점유하고 있는 물리주소 변경 그외 여러가지…

154 01. 디바이스 제어

155 01. 디바이스 제어 ioctl() 함수의 매개변수
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); linux/fs.h struct file_operations inode : file inode structure file : device file 자신 cmd = ioctl 명령 번호 arg = arguement

156 01. 디바이스 제어 cmd관련 MACRO 함수 32bits cmd의 구성
구분번호(ordinal / sequential number) : 2 bits 명령을 구분하는 명령어의 순차번호 매직번호(type) : 8 bits 다른 디바이스 드라이버의 ioctl명령과 구분하기 위한 값 데이터크기(size) : 14 bits 매개변수 arg를 통해 전달되는 메모리의 크기 읽기쓰기구분(direction) : 8 bits 일기, 쓰기를 위한 요구 명령을 구분하는 속성 2 14 8 15 31 direction size type number

157 01. 디바이스 제어 asm/ioctl.h /* asm/ioctl.h */
#define _IOC_NRBITS /* Number field */ #define _IOC_TYPEBITS 8 /* Type field */ #define _IOC_SIZEBITS 14 /* Size field */ #define _IOC_DIRBITS 2 /* Direction field */

158 01. 디바이스 제어 asm/ioctl.h /* Direction bits.*/
#define _IOC_NONE U /* Nothing */ #define _IOC_WRITE U /* Writing */ #define _IOC_READ /* Reading */ /* 각 field들을 완전한 32 bits인 하나의 cmd 로 만듬*/ #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT))

159 01. 디바이스 제어 asm/ioctl.h (명령어 생성 매크로) /* used to create numbers */
#define _IO(type,nr) /* 의미 없는 cmd 로 … */ _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size) /* 읽기용 cmd 로 …*/ _IOC(_IOC_READ,(type),(nr),sizeof(size)) #define _IOW(type,nr,size) /* 쓰기용 cmd 로 … */ _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) #define _IOWR(type,nr,size) /* 읽기/쓰기용 cmd 로 */ _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

160 01. 디바이스 제어 asm/ioctl.h (명령어 디코드 매크로)
/* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) /* Direction field 값 얻기 */ (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) /* Type field 값 얻기 */ (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) /* Number field 값 얻기 */ (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) /* Size field 값 얻기 */ (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

161 ioctl_test.h #ifndef _IOCTLTEST_H_ #define _IOCTLTEST_H_ #define IOCTLTEST_MAGIC 't' typedef struct { unsigned long size; unsigned char buff[128]; } __attribute__ ((packed)) ioctl_test_info; #define IOCTLTEST_LEDOFF _IO( IOCTLTEST_MAGIC, 0 ) #define IOCTLTEST_LEDON _IO( IOCTLTEST_MAGIC, 1 ) #define IOCTLTEST_GETSTATE _IO( IOCTLTEST_MAGIC, 2 ) #define IOCTLTEST_READ _IOR( IOCTLTEST_MAGIC, 3 , ioctl_test_info ) #define IOCTLTEST_WRITE _IOW( IOCTLTEST_MAGIC, 4 , ioctl_test_info ) #define IOCTLTEST_WRITE_READ _IOWR( IOCTLTEST_MAGIC, 5 , ioctl_test_info ) #define IOCTLTEST_MAXNR 6 #endif // IOCTLTEST_H_

162 ioctl_dev.c #include "ioctl_test.h" #define IOCTLTEST_DEV_NAME "ioctldev" #define IOCTLTEST_DEV_MAJOR 240 #define IOCTLTEST_WRITE_ADDR 0x0378 #define IOCTLTEST_READ_ADDR 0x0379 int ioctltest_open (struct inode *inode, struct file *filp) { return 0; } int ioctltest_release (struct inode *inode, struct file *filp) int ioctltest_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ioctl_test_info ctrl_info; int err, size; int loop; if( _IOC_TYPE( cmd ) != IOCTLTEST_MAGIC ) return -EINVAL; if( _IOC_NR( cmd ) >= IOCTLTEST_MAXNR ) return -EINVAL; size = _IOC_SIZE( cmd );

163 if( size ) { err = 0; if( _IOC_DIR( cmd ) & _IOC_READ ) err = verify_area( VERIFY_WRITE, (void *) arg, size ); else if( _IOC_DIR( cmd ) & _IOC_WRITE ) err = verify_area( VERIFY_READ , (void *) arg, size ); if( err ) return err; } switch( cmd ) { case IOCTLTEST_LEDOFF : outb( 0x00 , IOCTLTEST_WRITE_ADDR ); break; case IOCTLTEST_LEDON : outb( 0xFF , IOCTLTEST_WRITE_ADDR ); case IOCTLTEST_GETSTATE : return inb( IOCTLTEST_READ_ADDR ); case IOCTLTEST_READ : ctrl_info.buff[0] = inb( IOCTLTEST_READ_ADDR ); ctrl_info.size = 1; copy_to_user ( (void *) arg, (const void *) &ctrl_info, (unsigned long ) size ); case IOCTLTEST_WRITE : copy_from_user ( (void *)&ctrl_info, (const void *) arg, size ); for( loop = 0; loop < ctrl_info.size; loop++ ) outb( ctrl_info.buff[loop] , IOCTLTEST_WRITE_ADDR ); case IOCTLTEST_WRITE_READ : copy_from_user ( (void *)&ctrl_info, (const void *) arg, size ); ctrl_info.buff[0] = inb( IOCTLTEST_READ_ADDR ); return 0;

164 struct file_operations ioctltest_fops = {. owner = THIS_MODULE,
struct file_operations ioctltest_fops = { .owner = THIS_MODULE, .ioctl = ioctltest_ioctl, .open = ioctltest_open, .release = ioctltest_release, }; int ioctltest_init(void) int result; result = register_chrdev( IOCTLTEST_DEV_MAJOR, IOCTLTEST_DEV_NAME, &ioctltest_fops); if (result < 0) return result; return 0; } void ioctltest_exit(void) unregister_chrdev( IOCTLTEST_DEV_MAJOR, IOCTLTEST_DEV_NAME ); module_init(ioctltest_init); module_exit(ioctltest_exit); MODULE_LICENSE("Dual BSD/GPL");

165 ioctl_app.c int main() { ioctl_test_info info; int dev; int state; int cnt; dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY ); if( dev >= 0 ) printf( "wait... input\n" ); ioctl(dev, IOCTLTEST_LEDON ); while(1) state = ioctl(dev, IOCTLTEST_GETSTATE ); if( !(state & 0x10) ) break; } sleep(1); ioctl(dev, IOCTLTEST_LEDOFF );

166 printf( "wait. input\n" ); while(1) { info
printf( "wait... input\n" ); while(1) { info.size = 0; ioctl(dev, IOCTLTEST_READ, &info ); if( info.size > 0 ) if( !(info.buff[0] & 0x10) ) break; } info.size = 1; info.buff[0] = 0xFF; for( cnt=0; cnt<10; cnt++ ) ioctl(dev, IOCTLTEST_WRITE, &info ); info.buff[0] = ~info.buff[0]; usleep( ); cnt = 0; state = 0xFF;

167 while(1) { info. size = 1; info
while(1) { info.size = 1; info.buff[0] = state; ioctl(dev, IOCTLTEST_WRITE_READ, &info ); if( info.size > 0 ) if( !(info.buff[0] & 0x10) ) break; } cnt++; if( cnt >= 2 ) cnt = 0; state = ~state; usleep( ); ioctl(dev, IOCTLTEST_LEDOFF ); close(dev); return 0;

168 실행 # mknod /dev/ioctldev c # insmod ioctl_dev.ko # ./ioctl_app wait… input # rmmod ioctl_dev

169 11. 시간 처리와 커널 타이머

170 시간처리 하드웨어 (x86) RTC(Real Time Clock) TSC(Time Stamp Counter)
PIT(Programmable Interval Timer) – 8254 호환칩 APIC(Advanced Programmable Interrupt Controller) 내의 타이머

171 시간 처리 관련 전역변수, 함수 HZ : 1초당 발생하는 인터럽트 횟수 USER_HZ : HZ 값을 보정하는 수 *
jiffies: 2.4에서 tick마다 증가하는 전역 변수 wall_jiffies : 가장 최근의 wall time 갱신 때의 jiffies jiffies_64: 2.6에서 tick 마다 증가하는 전역 변수 * get_jiffies_64() : jiffies_64 값을 참조하기 위한 함수 * * : kernel 2.6

172 타이머 인터럽트 do_IRQ(0) timer_interrupt do_timer_interrupt
do_timer_interrupt_hook do_timer jiffies_64++ update_times()

173 시간 계산법 2.4 #define DIFF_TIME(3*HZ/10) u32 aftertime;
aftertime = jiffies + DIFF_TIME; #define DIFF_TIME (30) 2.6 u64 aftertime; aftertime = get_jiffies_64() + DIFF_TIME*(HZ/USER_HZ); #define DIFF_TIME (3*HZ/10) aftertime = get_jiffies_64() + DIFF_TIME;

174 시간 지연 짧은 지연 mdelay() – 밀리초단위(5 ms 이내) udelay() – 마이크로초 단위 지연
ndelay() – 나노초 단위 지연. 시스템 클럭이 1GHz 이상일 때 가능 긴 지연 2.4 #define DELAY_TIME_MSEC (3*HZ/10) unsigned long endtime = jiffies + DELAY_TIME_MSEC; while(jiffies < endtime) schedule(); 2.6 u64 endtime = get_jiffies_64() + DELAY_TIME_MSEC; while(jiffies<endtime);

175 시스템 시간 설정 struct timeval { time_t tv_Sec; // sec
suseconds tv_usec; // microsec }; struct timespec { time_t tv_sec; // sec long tv_nsec; // nano sec do_gettimeofday() : 시스템 시간을 초로 얻어온다 do_settimeofday() : 초로 환산된 시스템 시간을 설정한다 mktime() : 날짜와 시간을 초로 바꾼다

176 커널 타이머 커널 타이머 목록 : 수행할 함수와 함수가 수행되어야 하는 시간에 대한 정보를 담 고 있는 연결리스트
struct timer_list : 커널 타이머 구조체 struct timer_list { struct list_head list; unsigned long expires; // 만료 시간 unsigned long data; // 참조될 데이타의 주소 void (*function)(unsigned long); // 수행 함수 }; init_timer() : 커널 타이머 구조체를 초기화한다 add_timer() : 커널 타이머에 수행될 함수를 등록한다 del_timer() : 커널 타이머 목록에서 등록된 것을 제거한다

177 커널 타이머 (continue) - Example
struct timer_list timer 초기화 init_timer(&timer); timer.expires = get_jiffies_64()+(3*HZ/10); timer.data = (unsigned long)&mng_data[0]; timer.function = handler_function; 등록 void add_timer(&timer); 제거 int del_timer(&timer);

178 커널 타이머 동작 (2.6) 타이머 장치 1/Hz 간격으로 인터럽트 발생 1) 초기화 2) 등록 3) 호출 4) 제거 커널
4)자동제거 디바이스 드라이버 struct timer_list timer data 타이머 인터럽트 처리 init_timer(&timer); timer.expires = jiffies_64 + 호출시간; timer.data = (unsigned long)data addr; timer.function = handler; add_timer(&timer); __run_timers timer_list 등록된 timer_list 에서 timer.expires >= jiffies_64 검사 timer.function 호출 후 제거 void handler (unsigned long arg) { } del_timer(&timer);

179 example #define KERNELTIMER_WRITE_ADDR 0x0378
#define TIME_STEP (2*HZ/10) // 0.2 sec typedef struct { struct timer_list timer; unsigned long led; }__attribute__ ((packed)) KERNEL_TIMER_MANAGER; static KERNEL_TIMER_MANAGER *ptrmng = NULL; void kerneltimer_timeover(unsigned long arg);

180 void kerneltimer_registertimer(KERNEL_TIMER_MANAGER
void kerneltimer_registertimer(KERNEL_TIMER_MANAGER *pdata, unsigned long timeover) { init_timer(&(*pdata->timer)); // 구조체 초기화 pdata->timer.expires = get_jiffies_64()+timeover; // 실행 시간 설정 pdata->timer.data = (unsigned long) pdata; // 전달인자 주소값 pdata->timer.function = kerneltimer_timeover; // 수행될 함수 add_timer(&(pdata->timer)); // 타이머 구조체 등록 } void kerneltimer_timeover(unsigned long arg) { KERNEL_TIMER_MANAGER *pdata = NULL; if(arg) { pdata = (KERNEL_TIMER_MANAGER*)arg; outb((unsigned char) (pdata->led & 0xFF), KERNELTIMER_WRITE_ADDR); pdata->led = ~(pdata->led); kerneltimer_registertimer(pdata, TIME_STEP);

181 int kerneltimer_init(void) {
ptrmng = kmalloc(sizeof(KERNEL_TIMER_MANAGER), GFP_KERNEL); if(ptrmng == NULL) return -ENOMEM; memset(ptrmng, 0, sizeof(KERNEL_TIMER_MANAGER)); ptrmng->led = 0; kerneltimer_registertimer(ptrmng, TIME_STEP); return 0; } void kerneltimer_exit(void) { if(ptrmng != NULL) { del_timer(&(ptrmng->timer)); kfree(ptrmng); outb(0x00, KERNELTIMER_WRITE_ADDR); module_init(kerneltimer_init); module_exit(kerneltimer_exit); MODULE_LISENCE("Dual BSD/GPL");

182 12. 인터럽트 처리

183 What is the Interrupt? 어떤 프로세스가 수행되는 도중에 다른 서비스 처리 루틴이 끼어들 어 프로세스의 수행을 방해하는 것. 인터럽트가 발생하면 수행 중인 프로세스의 상태를 저장하고 수행을 중단한 다음 ISR (Interrupt Service Routine)을 수행. ISR처리가 끝나면 중단된 프로세스를 다시 수행. 인터럽트의 종류 오류 인터럽트와 같은 내부 인터럽트 외부 인터럽트 (IRQ요청 사용) IRQ – Interrupt Request Line

184 Interrupt vs. Exception
Interrupts : Asynchronous H/W device는 cpu clock에 비동기적 으로 interrupt를 발생시키 므로 kernel은 언제든지 interrupt에 의해 방해 받을 수 있음 Exception : Synchronous CPU clock에 동기화되어 발생 CPU가 명령을 실행하는 도중이나 프로그래밍 에러등에 의해 발생 Interrupt나 exception에 의한 code는 process에 의한 실행code가 아님

185 IRQ Interrupt 처리 과정 Architecture에 따라 처리하는 방법이 다르므로 Linux Kernel에서는 do_IRQ()함수를 통해 IRQ Interrupt를 처리. Interrupt가 발생한 IRQ 번호에 대해 등록된 서비스 함수가 없는 경우 해당 Interrupt는 무시된다.

186 인터럽트 서비스 함수의 형식

187 ISR내의 메모리 할당 Memory 할당 시 kmalloc() / kfree() 사용.
vmalloc() / vfree() / ioremap() 은 process휴면 가능성이 존재하기 때문 에 사용하기 곤란 kmalloc()도 GFP_ATOMIC인자를 사용해 process휴면 가능성을 제거 해야 함 ISR이전에 할당한 Memory는 제약없이 사용가능. ISR은 request_irq()를 이용해 kernel에 등록된 후 사용 가능하다.

188 인터럽트 함수의 등록 request_irq()는 sleep할 수 있으므로 interrupt context나 중단돼서는 안 되는 상황에서 호출할 수 없다. 등록이 성공하면 /proc/irq에 해당 interrupt항목 생성. Flags SA_INTERRUPT : 다른 interrupt를 허가하지 않음 SA_SHIRQ : interrupt 번호를 공유 SA_SAMPLE_RANDOM : Random 값 처리에 영향을 줌 Return value Success : 0

189 인터럽트 등록과 해제 Arguments 1.5 인터럽트 함수 해제
void free_irq( unsigned int irq, void *dev_id ) 반드시 process context에서 호출

190 인터럽트 함수와 Device Driver간의 데이터 공유
Global Variable 사용 Interrupt 하나에 interrupt service 함수 하나가 동작하는 경우 하나의 interrupt service함수로 여러 device를 제어하는 경 우에는 사용할 수 없음

191 dev_id 사용 Device의 정보를 전달하는 가장 보편적인 방법 ( 다중 프로 세스 환경에 적합 )

192 인터럽트 서비스 등록과 해제 시점 PC와 같은 범용 System 특정 목적의 System
open() / close() 에서 등록 / 해제 특정 목적의 System 모듈의 등록 / 해제 시점에 맞추어 등록 / 해제

193 인터럽트의 공유 request_irq() 함수를 사용할 때 flags와 dev_id를 변경
flags : SA_SHIRQ가 포함되어야 함. dev_id : 0이 아닌 값을 사용 dev_id값을 사용해 인터럽트를 공유하는 device들을 구 분한다. Kernel2.6에서는 같은 인터럽트에 대해 여러 ISR이 동작 하는 것을 방지하기 위해 ISR의 return value를 확인하 여 처리.

194 인터럽트의 금지와 해제 ISR이 동작 중에 다른 interrupt가 발생하지 못하도록 함.
ISR을 수행하는 도중에 상위 interrupt가 발생해 현재 ISR이 중단되면 안 되는 경우 일반함수 수행 중 데이터 처리를 보호하기 위해 interrupt 차단 Interrupt와 관련된 data처리나 data입력을 Queue / Linked List로 하 는 경우 ISR를 등록하는 request_irq() flag변수에 SA_INTERRUPT를 포함시 키면 ISR도중에 다른 interrupt를 disable한다. Interrupt 두 개가 동시에 발생한 경우 flag에 SA_INTERRUPT를 포 함한 ISR을 먼저 수행

195 특정 처리 구간에서 interrupt를 disable하는 경우
void disable_irq ( int irq ) : interrupt disable void enable_irq ( int irq ) : interrupt enable asm/irq.h header를 포함해야 함 Processor전체 interrupt en/disable ( asm/system.h ) Kernel 2.4 cli(void) sti(void) save_flag(unsigned long frags) restore_flags(unsigned long frags) Kernel 2.6 local_irq_disable(void) local_irq_enable(void) local_save_flags(unsigned long frags) local_irq_resotre(unsigned long frags)

196 seqlock_t 구조체 Device driver가 사용하는 변수와 ISR이 사용하는 변수가 전역변수로 서 로 같으면 동기를 맞추기 위해 interrupt en/disable을 사용 – interrupt disable period가 길거나 빠른 처리를 요구할 경우 interrupt 처리에 문 제가 생길 수 있음. Kernal 2.6에서는 보다 가벼운 seqlock_t를 사용해서 변수간 동기를 맞 춘다.

197 인터럽트와 난수 발생 처리 인터럽트 발생 횟수 확인 예측 불가능한 값 ( 난수 )가 필요한 경우 srand()함수를 사용
/proc/sys/kernel/random/uuid을 매개 변수로 사용 Kernel interrupt 함수와 random device driver에 의해 생 성 일반적인 Device driver도 난수 발생에 영향을 줄 수 있 음 request_irq()에 SA_SAMPLE_RANDOM을 포함 인터럽트 발생 횟수 확인 /proc/interrupt

198 file_operation structure
read -> int_read write -> int_write open -> int_open release -> int_release owner -> THIS_MODULE (v2.6) Interrupt registeration Request_irq(PRINT_IRQ, int_interrupt, SA_INTERRUPT, INT_DEV_NAME, NULL)

199 int_dev.c #define INT_DEV_NAME "intdev" #define INT_DEV_MAJOR 240 #define INT_WRITE_ADDR 0x0378 #define INT_READ_ADDR 0x0379 #define INT_CTRL_ADDR 0x037A #define PRINT_IRQ 7 #define PRINT_IRQ_ENABLE_MASK 0x10 #define INT_BUFF_MAX 64 typedef struct { unsigned long time; } __attribute__ ((packed)) R_INT_INFO; R_INT_INFO intbuffer[INT_BUFF_MAX]; int intcount = 0;

200 void int_clear( void ) { int lp; for( lp = 0; lp < INT_BUFF_MAX; lp++ ) intbuffer[lp].time = 0; } intcount = 0; irqreturn_t int_interrupt(int irq, void *dev_id, struct pt_regs *regs) if( intcount < INT_BUFF_MAX ) intbuffer[intcount].time = get_jiffies_64(); intcount++; return IRQ_HANDLED; int int_open (struct inode *inode, struct file *filp) if( !request_irq( PRINT_IRQ , int_interrupt, SA_INTERRUPT, INT_DEV_NAME, NULL) ) outb( PRINT_IRQ_ENABLE_MASK, INT_CTRL_ADDR ); int_clear(); return 0;

201 ssize_t int_read(struct file. filp, char. buf, size_t count, loff_t
ssize_t int_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { int readcount; char *ptrdata; int loop; readcount = count / sizeof( R_INT_INFO ); if( readcount > intcount ) readcount = intcount; ptrdata = (char * ) &intbuffer[0]; for( loop = 0; loop < readcount * sizeof(R_INT_INFO); loop++ ) put_user( ptrdata[loop], (char *) &buf[loop] ); } return readcount * sizeof( R_INT_INFO ); ssize_t int_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) unsigned char status; int loop; int_clear(); for( loop = 0; loop < count; loop++ ) get_user( status, (char *) buf ); outb( status , INT_WRITE_ADDR ); return count;

202 int int_release (struct inode. inode, struct file
int int_release (struct inode *inode, struct file *filp) { outb( 0x00, INT_CTRL_ADDR ); free_irq( PRINT_IRQ , NULL ); return 0; } struct file_operations int_fops = .owner = THIS_MODULE, .read = int_read, .write = int_write, .open = int_open, .release = int_release, }; int int_init(void) int result; result = register_chrdev( INT_DEV_MAJOR, INT_DEV_NAME, &int_fops); if (result < 0) return result; void int_exit(void) unregister_chrdev( INT_DEV_MAJOR, INT_DEV_NAME ); module_init(int_init); module_exit(int_exit); MODULE_LICENSE("Dual BSD/GPL");

203 int_app.c #define DEVICE_FILENAME "/dev/intdev" typedef struct { unsigned long time; } __attribute__ ((packed)) R_INT_INFO; #define INT_BUFF_MAX 64 int main() int dev; R_INT_INFO intbuffer[INT_BUFF_MAX]; int intcount; char buff[128]; int loop; dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY ); if( dev >= 0 ) printf( "start...\n" ); buff[0] = 0xFF; write(dev,buff,1 );

204 printf( "wait... input\n" ); while(1) { memset( intbuffer, 0, sizeof( intbuffer ) ); intcount = read(dev,(char *) &intbuffer[0],sizeof(R_INT_INFO) ) / sizeof(R_INT_INFO) ; if( intcount ) break; } printf( "input ok...\n"); sleep(1); printf( "read interrupt times\n" ); intcount = read(dev,(char *) intbuffer,sizeof(intbuffer) ) / sizeof(R_INT_INFO) ; for( loop =0; loop < intcount; loop++ ) printf( "index = %d time = %ld\n", loop, intbuffer[loop].time ); printf( "led flashing...\n"); for( loop=0; loop<5; loop++ ) buff[0] = 0xFF; write(dev,buff,1 ); buff[0] = 0x00; close(dev); return 0;

205 13장 블록킹 I/O

206 프로세스의 사용 효율성과 잠들기 구조 다중 프로세스와 시분할 처리 프로세스의 Sleep과 시스템의 효율성 프로세스가 잠든 상태
사건과 프로세스 스케줄링

207 블록킹 I/O의 개념 프로세스가 하드웨어의 외부 입력상태 변화를 기다리기위 해 Sleep 하게 되는 것
Example: Serial App int dev; char buff[128]; int readsize; dev = open(“/dev/ttyS0”, O_RDWR); readsize = read(dev, buff, 16);

208 블록킹 모드와 프로세스 처리 과정 프로세스 #1 처리 처리 프로세스 #2 처리 처리 처리 read 호출
Interrupt Handler 실제로 수행됨 프로세스 #1 처리 처리 할당된 시간 프로세스 #2 처리 처리 처리 스케줄 전환 스케줄 전환 스케줄 전환 스케줄 전환

209 Sleeping And Waking Up TASK_RUNNING TASK_INTERRUPTIBLE
__add_wait_queue() adds task to a wait queue, sets the task’s state to TASK_INTERRUPTIBLE, and calls schedule(). schedule() calls deactivate_task() which removes the task from the runqueue (task is runnable) (task is not runnable) TASK_RUNNING TASK_INTERRUPTIBLE Event occurs, and try_to_wake_up() sets the task to TASK_RUNNING, calls activate_task() to add the task to a runqueue, and calls schedule(). __remove_wait_queue() removes the task from the wait queue

210 블록킹 I/O의 구현 헤더파일: include/linux/wait.h 변수 선언 및 초기화 프로세스 관련
wait_queue_head_t WaitQueue; init_waitqueue_head(&WaitQueue); DECLARE_WAIT_QUEUE_HEAD(WaitQueue); 프로세스 관련 interruptible_sleep_on(&WaitQueue) interruptible_sleep_on_timeout(&WaitQueue, 10) wake_up_interruptible(&WaitQueue) wait_event(wq, condition) wait_event_interruptible(wq, condition) wait_event_interruptible_timeout(wq, condition, timeout)

211 블록킹 I/O의 구현 블록킹 I/O mode로 열기 wait_queue_head_t 구조체
open() flag 옵션으로 O_NDELAY를 주지 않으면 됨 wait_queue_head_t 구조체 프로세스를 관리하기 위한 대기큐 필요 blocking 조건마다 각각 대기큐 필요 대기큐 변수 생성 방법 wait_queue_head_t WaitQueue; init_waitqueue_head(&WaitQueue); DECLARE_WAIT_QUEUE_HEAD(WaitQueue);

212 프로세스 재우기와 깨우기 재우기 깨우기 DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Read); …
if(IsWait()) { if(!(filp->f_flags & O_NONBLOCK) interruptible_sleep_on(&WaitQueue_Read); // or // interruptible_sleep_on_timeout(&WaitQueue_Read, HZ); else return –EAGAIN; } 깨우기 wake_up_interruptible(&WaitQueue_Read)

213 wait_event_interruptible
헤더 파일 2.4 : linux/sched.h 2.6 : linux/wait.h type : wait_event_interruptible(wq, condition) example if(!intcount) { if(!(filp->f_flags & O_NONBLOCK)) interruptible_sleep_on(&WaitQ) } else return –EAGAIN; int return; if((!intcount) && (filp->f_flags & O_NONBLOCK)) return –EAGAIN; ret = wait_event_interruptible(WaitQ, intcount); if(ret) return ret;

214 blockio_app.c typedef struct { unsigned long time; } __attribute__ ((packed)) R_INT_INFO; #define BLOCKIO_BUFF_MAX 64 int main() int dev; R_INT_INFO intbuffer[BLOCKIO_BUFF_MAX]; int intcount; char buff[128]; int loop; dev = open( DEVICE_FILENAME, O_RDWR ); if( dev >= 0 ) printf( "start...\n" ); buff[0] = 0xFF; write(dev,buff,1 );

215 printf( "wait. input\n" ); intcount = read(dev,(char
printf( "wait... input\n" ); intcount = read(dev,(char *) &intbuffer[0],sizeof(R_INT_INFO) ); printf( "input ok...\n"); sleep(1); memset( intbuffer, 0, sizeof( intbuffer ) ); printf( "read interrupt times\n" ); intcount = read(dev,(char *) intbuffer,sizeof(intbuffer) ) / sizeof(R_INT_INFO) ; for( loop =0; loop < intcount; loop++ ) { printf( "index = %d time = %ld\n", loop, intbuffer[loop].time ); } printf( "led flashing...\n"); for( loop=0; loop<5; loop++ ) buff[0] = 0xFF; write(dev,buff,1 ); buff[0] = 0x00; close(dev); return 0;

216 blockio_dev.c #define BLOCKIO_DEV_NAME "blockiodev" #define BLOCKIO_DEV_MAJOR 240 #define BLOCKIO_WRITE_ADDR 0x0378 #define BLOCKIO_READ_ADDR 0x0379 #define BLOCKIO_CTRL_ADDR 0x037A #define BLOCKIO_IRQ 7 #define BLOCKIO_IRQ_ENABLE_MASK 0x10 #define BLOCKIO_BUFF_MAX 64 typedef struct { unsigned long time; } __attribute__ ((packed)) R_BLOCKIO_INFO; R_BLOCKIO_INFO intbuffer[BLOCKIO_BUFF_MAX]; int intcount = 0; DECLARE_WAIT_QUEUE_HEAD( WaitQueue_Read ); // 읽기에 대한 블럭 모드 구현을 위한 대기 큐 변수

217 void blockio_clear( void ) { int lp; for( lp = 0; lp < BLOCKIO_BUFF_MAX; lp++ ) intbuffer[lp].time = 0; intcount = 0; } void blockio_interrupt(int irq, void *dev_id, struct pt_regs *regs) if( intcount < BLOCKIO_BUFF_MAX ) { intbuffer[intcount].time = jiffies; intcount++; wake_up_interruptible( &WaitQueue_Read ); int blockio_open (struct inode *inode, struct file *filp) if( !request_irq( BLOCKIO_IRQ , blockio_interrupt, SA_INTERRUPT, BLOCKIO_DEV_NAME, NULL) ) outb( BLOCKIO_IRQ_ENABLE_MASK, BLOCKIO_CTRL_ADDR ); blockio_clear(); return 0;

218 ssize_t blockio_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { int readcount; char *ptrdata; int loop; if( !intcount ) if( !(filp->f_flags & O_NONBLOCK) ) interruptible_sleep_on( &WaitQueue_Read ); } else return -EAGAIN; readcount = count / sizeof( R_BLOCKIO_INFO ); if( readcount > intcount ) readcount = intcount; ptrdata = (char * ) &intbuffer[0]; for( loop = 0; loop < readcount * sizeof(R_BLOCKIO_INFO); loop++ ) put_user( ptrdata[loop], (char *) &buf[loop] ); return readcount * sizeof( R_BLOCKIO_INFO );

219 ssize_t blockio_write (struct file. filp, const char
ssize_t blockio_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) { unsigned char status; int loop; blockio_clear(); for( loop = 0; loop < count; loop++ ) get_user( status, (char *) buf ); outb( status , BLOCKIO_WRITE_ADDR ); } return count; int blockio_release (struct inode *inode, struct file *filp) outb( 0x00, BLOCKIO_CTRL_ADDR ); free_irq( BLOCKIO_IRQ , NULL ); return 0;

220 struct file_operations blockio_fops = { read : blockio_read, write : blockio_write, open : blockio_open, release : blockio_release, }; int init_module(void) int result; result = register_chrdev( BLOCKIO_DEV_MAJOR, BLOCKIO_DEV_NAME, &blockio_fops); if (result < 0) return result; return 0; } void cleanup_module(void) unregister_chrdev( BLOCKIO_DEV_MAJOR, BLOCKIO_DEV_NAME );

221 CH 14 입출력 다중화

222 목차 입출력 다중화 개요 입출력 다중화 system call 입출력 다중화 내부 구현 – driver – 예제 (프린터 포트)
Select Poll 입출력 다중화 내부 구현 – driver – 예제 (프린터 포트)

223 입출력 다중화 개요 특정 장치 및 파일과 같은 resource들에 대한 입출 력 처리를 event 중심으로 비 동기적으로 처리
관련 user system call Select Poll

224 입출력 다중화 system call

225 Select (1/2) 암수 원형 입력 패러미터 리턴값 설명 기타 FD_ZERO : fd_set 구조체 변수 초기화
n : readfds, writefds, exceptfds 의 파일 디스크립터의 최대값 +1 Readfds : read event 를 listening할 fd 저장한 fd_set Write fds : write event를 listening 할 fd 저장한 fd_set Exceptfds : error event를 listening 할 fd 저장한 fd_set Timeout : 이벤트가 발생하지 않았을 시 select함수가 리턴 할 timeout 리턴값 -1 : error, 0 : timeout, 양의 정수 : 이벤트와 관련한 fd개수 설명 Readfds, writefds, exceptfds에 각각 관심있는 event에 등록된 장치의 해당 event 수신 시 리턴한다. 기타 FD_ZERO : fd_set 구조체 변수 초기화 FD_SET : fd_set에 fd등록 FD_ISSET: fd_set내의 특정 fd의 이벤트 발생 여부 확인하는 매크로 #include <sys/select.h> int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *time-out);

226 Select 예제 #include <string.h> #include <fcntl.h>
#include <termios.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int main( int argc, char **argv ) { int sfd1, sfd2, sfd3; fd_set rfds; fd_set errorfds; struct timeval tv; int retval; char buff[256]; int readcnt; sfd1 = open( "/dev/ttyS1", O_RDWR | O_NOCTTY ); sfd2 = open( "/dev/ttyS2", O_RDWR | O_NOCTTY ); sfd3 = open( "/dev/ttyS3", O_RDWR | O_NOCTTY ); : 각각의 시리얼 환경 설정 루틴들 while(1) // 읽기 이벤트 대상이 되는 것들 FD_ZERO(&rfds); FD_SET(sfd1, &rfds); FD_SET(sfd2, &rfds); FD_SET(sfd3, &rfds); // 에러 이벤트 대상이 되는 것들 FD_ZERO(&errorfds); FD_SET(sfd1, &errorfds); FD_SET(sfd2, &errorfds); FD_SET(sfd3, &errorfds); tv.tv_sec = 5; // 5초에 대한 시간 tv.tv_usec = 0; // 사건이 생기기 전에 대기 한다. retval = select(FD_SETSIZE, &rfds, NULL, &errorfds, &tv); if( retval < 0 ) { perror ("select"); exit (EXIT_FAILURE); } if( retval == 0 ) printf("5 초안에 아무 데이타도 없었다.\n"); // 수신된 데이타가 있는가를 검사한다. for (i = 0; i < FD_SETSIZE; ++i) if (FD_ISSET (i, &read_fd_set)) readcnt = read( i, buff, 256 ); write( i, buff, readcnt ); // 에러에 대한 검사를 수행한다. if (FD_ISSET (i, &errorfds)) printf("장치 에러 발생.\n"); close( sfd1 ); close( sfd2 ); close( sfd3 );

227 Poll (1/3) 암수 원형 입력 패러미터 ufds : 대상 fd와 관심있는 event 그리고 return된 event를 담은 자료형 nfds : 등록된 event 개수 Timeout : 이벤트 발생이 안되어 poll함수 리턴 할 timeout (ms) 설명 Select와 같이 특정 fd에 대해 등록된 event 발생 시 return하여 관련 event에 대한 처리를 할 수 있도록 한다. #include <sys/poll.h> int poll(struct pollfd *ufds, unsigned int nfds, int timeout); struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ };

228 Poll (2/3) Event 확인 Event macro
Poll() 리턴 시, 등록한 pollfd 자료형의 revents 필드를 event macro상수 와 AND 연산으로 확인 Event macro POLLIN fd 로부터 데이터 읽기 가능한 상태 POLLPRI (network 수신) fd로부터 긴급한 데이터 읽기 가능한 상태 POLLOUT fd 로부터 데이터 쓰기 가능한 상태 POLLERR POLLHUP POLLNVAL fd가 유효하지 않은 값이 된 상태 예) socket fd의 무효화 여 부

229 Poll (3/3) #include <stdio.h> #include <string.h>
#include <fcntl.h> #include <termios.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <sys/poll.h> int main( int argc, char **argv ) { int sfd1, sfd2, sfd3; struct pollfd Events[3]; int retval; char buff[256]; int readcnt; sfd1 = open( "/dev/ttyS1", O_RDWR | O_NOCTTY ); sfd2 = open( "/dev/ttyS2", O_RDWR | O_NOCTTY ); sfd3 = open( "/dev/ttyS3", O_RDWR | O_NOCTTY ); : 각각의 시리얼 환경 설정 루틴들 memset( Events, 0, sizeof( Events ) ); Events[0].fd = sfd1; Events[0].events = POLLIN // 수신 이벤트 | POLLERR; // 에러 이벤트 Events[1].fd = sfd2; Events[1].events = POLLIN // 수신 이벤트 Events[2].fd = sfd3; Events[2].events = POLLIN // 수신 이벤트 while(1) { // 사건이 생기기 전에 대기 한다. retval = poll( (struct pollfd *)&Events, 3, 5000 ); < 0 ) if( retval < 0 ) perror("poll error : " ); exit (EXIT_FAILURE); } if( retval == 0 ) printf("5 초안에 아무 데이타도 없었다.\n"); continue; for (i = 0; i < 3; i++) // 에러에 대한 검사를 수행한다. if( Events[i].revents & POLLERR ) printf("장치 에러 발생.\n"); // 수신된 데이타가 있는가를 검사한다. if( Events[i].revents & POLLIN ) readcnt = read( i, buff, 256 ); write( i, buff, readcnt ); close( sfd1 ); close( sfd2 ); close( sfd3 );

230 입출력 다중화 내부 구현 – driver – 드라이버의 blocking I/O를 구현하기 위해 file operation 구조체의 poll필드 구현 해야 함 file operation 구조체의 poll필드 함수 원형 unsigned int xxx_poll( struct file *file, poll_table *wait) Poll 함수 기능 커널 poll_table에 polling 대상이 되는 사건에 대한 wait queue등록 Select/poll함수를 통해 입출력 다중화를 할 수 있도록 관련 mask값 반환 Poll 함수의 전형적인 구현 예 /* read와 write에 대한 wait queue 선언 및 초기화 */ DECLARE_WAIT_QUEUE_HEAD(waitq_read); Unsigned int xxx_poll( struct file *file, poll_table *wait){ Int mask = 0; /* read/write wait queue를 커널에서 관리하는 poll_table에 등록 */ poll_wait(file, &waitq_read, wait); poll_wait(file, &waitq_write, wait); /* 반환값 처리 */ If (처리가능한 데이터 있음) mask |= (POLLIN | POLLRDNORM); If(출력 가능)mask |= (POLLOUT | POLLWRNORM); Return mask;

231 poll_app.c int main() { int dev; struct pollfd Events[1]; int retval; char buff[128]; int readcnt; int loop; int flashing; printf( "poll Program Start\n" ); dev = open("/dev/polldev", O_RDWR ); if( dev < 0 ) printf( "[/dev/polldev] Open fail\n"); exit(-1); } flashing = 0; printf( "wait poll \n" ); buff[0] = 0xff; write(dev,buff,1 );

232 while(1) { memset( Events, 0, sizeof( Events ) ); Events[0]
while(1) { memset( Events, 0, sizeof( Events ) ); Events[0].fd = dev; Events[0].events = POLLIN; retval = poll( (struct pollfd *)&Events, 1, 1000 ); if( retval < 0 ) perror("poll error : " ); exit (EXIT_FAILURE); } if( retval == 0 ) flashing = !flashing; if( flashing ) buff[0] = 0x00; else buff[0] = 0xff; write(dev,buff,1 ); continue;

233 if( Events[0].revents & POLLERR ) { printf("Device Error\n"); exit(EXIT_FAILURE); } if( Events[0].revents & POLLIN ) readcnt = read( dev, buff, sizeof( buff ) ); printf( "READ DATA COUNT [%d]\n", readcnt ); for( loop = 0; loop < readcnt; loop++ ) printf( "READ DATA [%02X]\n", buff[loop] ); buff[0] = 0x00; write(dev,buff,1 ); close(dev); return 0;

234 poll_dev.c #define POLL_DEV_NAME "polldev" #define POLL_DEV_MAJOR 240 #define POLL_WRITE_ADDR 0x0378 #define POLL_READ_ADDR 0x0379 #define POLL_CTRL_ADDR 0x037A #define POLL_IRQ 7 #define POLL_IRQ_ENABLE_MASK 0x10 #define poll_BUFF_MAX 64 DECLARE_WAIT_QUEUE_HEAD( WaitQueue_Read ); #define MAX_QUEUE_CNT 128 static unsigned char ReadQ[MAX_QUEUE_CNT]; static unsigned long ReadQCount = 0; static unsigned long ReadQHead = 0; static unsigned long ReadQTail = 0;

235 void poll_interrupt(int irq, void. dev_id, struct pt_regs
void poll_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned long flags; save_flags(flags); cli(); if( ReadQCount < MAX_QUEUE_CNT ) ReadQ[ReadQHead] = (unsigned long) inb( POLL_READ_ADDR ); ReadQHead = ( ReadQHead + 1 ) % MAX_QUEUE_CNT; ReadQCount++; } restore_flags(flags); wake_up_interruptible( &WaitQueue_Read ); int poll_open (struct inode *inode, struct file *filp) if( !request_irq( POLL_IRQ , poll_interrupt, SA_INTERRUPT, POLL_DEV_NAME, NULL) ) outb( POLL_IRQ_ENABLE_MASK, POLL_CTRL_ADDR ); return 0;

236 ssize_t poll_read(struct file. filp, char. buf, size_t count, loff_t
ssize_t poll_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { unsigned long flags; int realmax; int loop; int retstate; if( (!ReadQCount) && (filp->f_flags & O_NONBLOCK) ) return -EAGAIN; retstate = wait_event_interruptible( WaitQueue_Read, ReadQCount ); if( retstate ) return retstate; save_flags(flags); cli(); realmax = 0; if( ReadQCount > 0 ) if( ReadQCount <= count ) realmax = ReadQCount; else realmax = count; for( loop = 0; loop < realmax; loop++ ) put_user( ReadQ[ReadQTail], (char *) &buf[loop] ); ReadQTail = ( ReadQTail + 1 ) % MAX_QUEUE_CNT; ReadQCount--; } restore_flags(flags); return realmax;

237 ssize_t poll_write (struct file. filp, const char
ssize_t poll_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) { unsigned char status; int loop; for( loop = 0; loop < count; loop++ ) get_user( status, (char *) buf ); outb( status , POLL_WRITE_ADDR ); } return count; unsigned int poll_poll( struct file *filp, poll_table *wait ) unsigned int mask = 0; poll_wait( filp, &WaitQueue_Read, wait ); if( ReadQCount > 0 ) mask |= POLLIN | POLLRDNORM; return mask;

238 int poll_release (struct inode. inode, struct file
int poll_release (struct inode *inode, struct file *filp) { outb( 0x00, POLL_CTRL_ADDR ); free_irq( POLL_IRQ , NULL ); return 0; } struct file_operations poll_fops = read : poll_read, write : poll_write, poll : poll_poll, open : poll_open, release : poll_release, }; int init_module(void) int result; result = register_chrdev( POLL_DEV_MAJOR, POLL_DEV_NAME, &poll_fops); if (result < 0) return result; void cleanup_module(void) unregister_chrdev( POLL_DEV_MAJOR, POLL_DEV_NAME );

239 15장 태스크 큐와 워크 큐

240 -목차- 01. 태스크 큐와 워크 큐의 필요성 02. 태스크 큐 03. 워크 큐 04. 선점형 커널

241 01. 태스크 큐와 워크 큐의 필요성 특정 I/O에 대한 지속적인 감시가 필요한 경우
타이머 인터럽트 이용 => 여분의 타이머 인터럽트 필요, overhead 큼 커널 타이머 이용 => 정확한 주기로 동작, 1/HZ 이하의 주기에 선 사용 불가능 태스크 큐와 워크 큐 이용 => 동작 시점 예측 힘듬, 커널 타이머 보다 자주 혹은 빠르게 호출될 가 능성이 있음, 워크큐의 경우 유연 한 처리 가능 bottomhalf 이용(ver 2.4 이하) => 제한적인 방법

242 01. 태스크 큐와 워크 큐의 필요성 인터럽트 핸들러 함수에서의 신속한 처리가 힘든 경우
Tophalf/Bottomhalf 모델에서 Bottomhalf로 쓰임 ex) 이더넷 디바이스, 사운드 디바이스 인터럽트 서비스 함수(ISR)가 가지는 제약 이외의 처리를 해 야할 경우 인터럽트 서비스 함수(ISR)는 제약이 많음 ISR내의 긴 지연은 시스템 전체 성능 저하를 유발함 vmalloc이나 파일 입출력과 같이 sleep 상태를 유발할 수 있는 함 수는 사용이 금지됨

243 02. 태스크 큐 태스크 큐의 초기 설계 목표 인터럽트를 허용한 실행(Bottomhalf)
사용자 프로세스와 같은 태스크 특성(sleep 가능 등) 보유 (구현 실패) 워크 큐로 대치됨

244 02. 태스크 큐 tq_struct struct struct tq_struct { }
struct list_head list; unsigned long sync; void (*routine)(void *); void *data } routine : 나중에 커널이 수행해야할 함수 data : routine 함수가 사용할 data

245 02. 태스크 큐 태스크 큐 작업의 등록 int queue_task(struct tq_struct *bh_pointer,
task_queue *bh_list); bh_list : 태스크 큐 관리용 list tq_timer : 타이머 인터럽트와 연동 tq_immediate : 소프트웨어 인터럽트와 연동, Bottomhalf와 관련 소프트웨어 인터럽트는 system call에 의해 발생되거나 kernel이 강제로 발생(ex. mark_bh, softirqd)시킨다. tq_disk : 블록형 디바이스 드라이버에서 사용 bh_pointer : 등록될 tq_struct struct

246 02. 태스크 큐 태스크 큐 작업의 실행 태스크 큐 작업의 종료 다른 인터럽트를 허용함
느린 인터럽트 핸들러와 유사 => sleep 해선 안됨 태스크 큐 작업의 종료 실행된 태스크 큐는 태스크 큐 관리 list에서 자동 제거됨 태스크 큐 struct는 디바이스 드라이버 내에서 수동으로 제 거해야 함

247 02. 태스크 큐 태스크 큐의 특별한 사용법 전용의 태스크 큐 사용 가능
void run_task_queue(task_queue *list); 전용의 태스크 큐 작업 강제 실행 tq_timer, tq_immediate 를 대상으로 호출하면 안됨

248 03. 워크 큐 워크 큐의 특징 태스크 큐와 기본 설계 이념 동일 태스크 큐의 문제점 보완
우선 순위가 높은 전용의 kernel thread를 이용하여 태스크 특성 부여 (선점형 커널 도입으로 성능이 보장되었음) 2.6에서 태스크 큐를 대체함

249 03. 워크 큐 work_struct struct struct work_struct { } func : 수행할 함수
unsigned long pending; struct list_head entry; void (*func)(void *); void *data; void *wq_data; struct timer_list timer; } func : 수행할 함수 data : func 함수가 사용할 data

250 03. 워크 큐 스케줄 워크 큐 keventd 스레드가 관리하는 워크 큐
DECLARE_WORK(name, void (*function)(void *), void *data); 워크 큐에 등록될 작업 구조체를 선언하고 초기화하는 함수 INIT_WORK(struct work_struct *work, void (*function)(void *), void *data); 이미 선언된 워크 큐 작업 구조체를 초기화하는 함수 int schedule_work(struct work_struct *work); keventd가 소모하는 워크 큐에 워크 큐 작업을 등록하는 함수 int schedule_delayed_work(struct work_struct *work, unsigned long delay); delay(1/HZ 단위) 후, schedule_work과 같은 작업을 수행하는 함 수

251 03. 워크 큐 스케줄 워크 큐 void flush_scheduled_work(void);
keventd가 소모하는 워크 큐의 작업들이 모두 처리될 수 있게 스케 줄링 전환을 수행하며 대기하는 함수 디바이스 드라이버 제거시나 커널 종료시에만 호출되어야 함 keventd thread -10의 우선 순위 값으로 시작함 보통 땐 sleep함 schedule_work(), schedule_delayed_work()함수에 의해 깨 어남 모듈의 라이센스가 GPL이 아니어도 사용 가능

252 03. 워크 큐 디바이스 드라이버 고유의 워크 큐 다른 디바이스 드라이버가 스케줄 워크 큐를 통해 처리 시간이 긴 작 업을 수행해서 스케줄 워크 큐 사용이 힘들어 질 때 필요함 모듈의 라이센스가 GPL이나 BSD일 때 사용 가능 struct workqueue_struct * create_workqueue(const char * name); name을 이름으로 갖는 워크 큐 처리 thread와 워크 큐를 생성하는 함수 void destroy_workqueue(struct workqueue_struct *queue); 워크 큐에서 수행 중인 작업이나 대기 중인 작업의 완료를 기다린 후, 워크 큐를 안전하게 제거하고 스레드를 종료시키는 함수

253 03. 워크 큐 디바이스 드라이버 고유의 워크 큐 void flush_workqueue(struct workqueue_struct *queue); 스케줄 워크 큐의 flush_scheduled_work()과 유사 int queue_work(struct workqueue_struct *queue, struct work_struct *work); 스케줄 워크 큐의 schedule_work()과 유사 int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay); 스케줄 워크 큐의 schedule_delayed_work()과 유사

254 04. 선점형 커널 비선점형 커널 2.6 커널 실행 흐름이 커널 안에 있을 경우, 다른 태스크로 선점되는 것이 불가 능
커널 코드 종료나 정지 시에만 다른 태스크로 스케줄링 가능 2.6 커널 nested ISR에 schedule 함수 추가됨 커널 안에서 수행되는 태스크가 lock을 획득하고 있지 않다면 선점 가능(preempt_count로 관리)

255 04. 선점형 커널 커널 선점이 가능한 경우 인터럽트 핸들러로부터 커널 공간으로 리턴할 경우
커널 코드가 다시 선점 가능해질 경우 커널 안의 태스크가 명시적으로 schedule()을 호출하는 경우 커널 안의 태스크가 중단되는 경우

256 17. proc 파일 시스템

257 proc 파일 시스템 시스템 내부의 상황 proc 디렉토리 마운트 인터럽트 할당 PCI 사용정보 프로세스 동작 상황
메모리 상태 커널 내부 상태 값 (실시간 시스템 정보) etc .. (표 17-1 참조) proc 디렉토리 마운트 부팅 스크립트에서 다음 명령을 실행 mount –t proc proc /proc

258 proc 파일 시스템의 특성 디바이스 드라이버를 디버깅 하거나 셀에서 디바이스 드라이버의 상 태를 쉽게 관찰하거나 제어할 때 유용 시스템 정보를 파일처럼 볼 수 있게 해주는 램 파일 시스템 커널과 디바이스 드라이버에서 제공하는 정보를 응용 프로그램에서 읽거나 데이터를 써 넣을 수 있다. 응용 프로그램이 /proc 디렉토리 하부에 디렉토리나 파일을 새로 만 들 수 없고, 삭제나 이름 변경 불가 (커널이나 디바이스 드라이버에 서만 가능) 디바이스 드라이버를 위해 반드시 proc 파일 시스템을 제공할 필요 없음 (기본 인터페이스가 구현 되어있어서 따로 proc 파일 시스템을 만들 필요는 없다.) 파일이 특성은 일반 파일과 같다 (open(), close(), read(), write()등 을 쓸 수 있다.) <-> 파일을 새로 만들 수 없고 데이터는 보존 되지 않는다.

259 proc 파일 시스템 함수와 구조체 • struct proc_dir_entry :디렉토리 표현을 위한 변수 구조체
• proc_mkdir : 디렉토리 생성 함수 • create_proc_entry : 파일 생성 함수 • remove_proc_entry : 디렉토리 또는 파일 제거 함수 struct proc_dir_entry { unsigned short low_ino; unsigned short namelen; const char *name; mode_t mode; nlink_t nlink; uid_t uid; gid_t gid; unsigned long size; struct inode_operations * proc_iops; struct file_operations * proc_fops; get_info_t *get_info; struct module *owner; struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; write_proc_t *write_proc; atomic_t count; /* use count */ int deleted; /* delete flag */ kdev_t rdev; };

260 proc 파일 시스템 동작 방식(1) /proc 디렉토리에 하부 디렉토리를 만들때 proc 파일 시스템 만들기
struct proc_dir_entry *root_proc_dir = NULL; root_proc_dir = proc_mkdir(“testdir”,0); struct proc_dir_entry * root_proc_file = NULL; root_proc_file = create_proc_entry( “testfile", S_IFREG | S_IRWXU, root_proc_dir ); int read_proc_test( char *page, char **start, off_t off, int count, int *eof, void *data_unused ) { page에 써 넣는다. *eof = 1; /* 스트림에 해당하는데 일반적인 경우가 아님 */ return 써 넣어진 테이터 수; }

261 proc 파일 시스템 동작 방식(2) proc 파일 시스템 쓰기 proc 파일 시스템 삭제
int write_proc_test( struct file *file, const char *buffer, unsigned long count, void *data) { 사용자 공간의 메모리인 buffer의 내용을 커널 공간에 써 넣는다 . return 처리된 데이터 수 } remove_proc_entry( “testfile" , root_proc_dir ); remove_proc_entry( “testdir" , 0 );

262 가산기 예제 struct proc_dir_entry *sumproc_root_fp = NULL; struct proc_dir_entry *sumproc_val1_fp = NULL; struct proc_dir_entry *sumproc_val2_fp = NULL; struct proc_dir_entry *sumproc_result_fp = NULL; char sumproc_str1[PAGE_SIZE-80] = { 0, }; char sumproc_str2[PAGE_SIZE-80] = { 0, }; int read_sumproc_val( char *page, char **start, off_t off, int count, int *eof, void *data_unused ) { char *buf; char *realdata; realdata = (char *)data_unused; buf = page; buf += sprintf( buf, "Value = [%s]\n", realdata ); *eof = 1; return buf - page; }

263 int write_sumproc_val( struct file. file, const char
int write_sumproc_val( struct file *file, const char *buffer, unsigned long count, void *data) { int len; char *realdata = (char *)data; if (copy_from_user(realdata, buffer, count)) return -EFAULT; realdata[count] = '\0'; len = strlen(realdata); if (realdata[len-1] == '\n') realdata[--len] = 0; return count; } int read_sumproc_result( char *page, char **start, off_t off,int count,int *eof, void *data_unused ) char *buf = page; int a, b, sum; a = simple_strtoul( sumproc_str1, NULL, 10 ); b = simple_strtoul( sumproc_str2, NULL, 10 ); sum = a + b; buf += sprintf( buf, "Result [%d + %d = %d]\n", a,b, sum ); *eof = 1; return buf - page;

264 int init_module(void) { sumproc_root_fp = proc_mkdir( "sumproc", 0 ); sumproc_val1_fp = create_proc_entry("val1", S_IFREG|S_IRWXU, sumproc_root_fp); if( sumproc_val1_fp ) { sumproc_val1_fp->data = sumproc_str1; sumproc_val1_fp->read_proc = read_sumproc_val; sumproc_val1_fp->write_proc = write_sumproc_val; } sumproc_val2_fp = create_proc_entry("val2", S_IFREG|S_IRWXU , sumproc_root_fp); if( sumproc_val2_fp ) { sumproc_val2_fp->data = sumproc_str2; sumproc_val2_fp->read_proc = read_sumproc_val; sumproc_val2_fp->write_proc = write_sumproc_val; sumproc_result_fp = create_proc_entry("result",S_IFREG|S_IRUSR,sumproc_root_fp); if( sumproc_result_fp ) sumproc_result_fp->read_proc = read_sumproc_result; return 0;

265 void cleanup_module(void) { remove_proc_entry( "result" , sumproc_root_fp ); remove_proc_entry( "val2" , sumproc_root_fp ); remove_proc_entry( "val1" , sumproc_root_fp ); remove_proc_entry( "sumproc" , 0 ); }

266 18. 메모리 매핑

267 물리 주소 메모리 처리

268 MMUless system MMUless system의 메모리 공 간을 플랫 메모리 공간이라 함
프로세스 별로 주소 공간이 나 눠져야 함 컴파일시 프로세스가 수행될 위치를 미리 지정하거나 상대 적인 주소로 프로세스가 컴파 일 되어야 함 H/W적인 메모리 보호 장치 없 음 메모리 단편화 문제 발생

269 가상 주소와 MMU 플랫 메모리 공간의 문제점 해결 다중 프로세스 지원 메모리 공간 보호
물리적인 메모리 제약에서 벗어나 프로그래밍 가능 하드디스크와 같은 보조 기억 장치를 메모리처럼 사용할 수 있게 함(swap device)

270 페이지와 페이지 테이블 모든 가상 주소와 물리 주소를 1대1로 매핑 => 메모리 소모가 큼
=> 페이지 단위로 처리함 (리눅스에선 보통 4KB) 페이지 테이블은 주소 변환 정보 뿐만 아니라, 메모리 접근 속성도 관 리함 => 허가되지 않은 접근 => exception 발생(H/W) => exception 처리(커널)

271 페이지 단위로 처리하는 MMU

272 리눅스의 가상 메모리 관리 프로세스마다 독자적인 메모리 공간을 보유함
단, 커널 주소 공간은 공유함 1개의 커널 MMU 테이블과 다수의 프로세스 MMU 테이블 존재 mmap 등의 API를 이용하여 I/O device의 물리 주소를 프 로세스 주소 공간으로 동적 매핑 가능

273 리눅스 가상 메모리 관리

274 3단계 리눅스 페이지 테이블 3단계 페이징 모델 PGD : Page Directory
실제로 보통 2단계만 사용됨 PGD : Page Directory PMD : Page Mid level Directory PTE : Page Table Entry

275 물리 주소 공간을 커널 주소 공간으로 매핑 디바이스 드라이버에서 H/W를 제어 하기 위해 물리 주소를 커널 주소 공간의 가상 주소로 변경해야 함 void *ioremap (unsigned long offset, unsigned long size); offset : 매핑하길 원하는 물리 주소 공간의 시작 주소 size : 매핑하길 원하는 물리 주소 공간의 크기, MMU 관리 단위인 PAGE_SIZE 단위여야 함 반환값: 성공 시 매핑된 가상 주소값, 실패 시 NULL값 반환 void * ioremap_nocache (unsigned long offset, unsigned long size); ioremap과 유사하나, PCI device의 non-prefetchable 영역같은 non- cachable 영역에 사용 void iounmap (void *addr); 매핑 해제 함수

276 I/O 물리 주소와 가상 주소 간의 변환 함수 리눅스 커널은 부팅 단계에서 시스템을 제어하기 위한 모든 I/O 제어 물리 주소나 램 영역의 물리 주소를 MMU 테이블로 미리 작성함 => 고 정 매핑된 예약 영역 고정 매핑된 영역은 PAGE_OFFSET과 같은 특정 offset을 통해 물리 주 소와 가상 주소 간의 변환이 가능하다 unsigned long virt_to_phys (volatile void * address), void * phys_to_virt (unsigned long address) 램이나 비디오 램과 같은 메모리 관련 주소 공간에 사용 unsigned long virt_to_bus (volatile void * address), void * bus_to_virt (unsigned long address) DMA등과 관련된 주소 공간에 사용(호환성을 위해 권장함)

277 프로세스 메모리 매핑 mmap 대용량의 데이터를 처리하는 하드웨어 디바이스 드라이버를 작성할 때 효율적임
대용량의 데이터를 처리하는 하드웨어 디바이스 드라이버를 작성할 때 효율적임 파일이나 디바이스 드라이버가 제공하는 물리 주소 영역을 프로세스 가 직접 사용할 수 있게 해줌 메모리 복사 없이 access 가능 => 효율적임

278 비효율적인 H/W access

279 mmap과 ioremap 함수

280 응용 프로그램에서 호출하는 mmap() void * mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset); start : 프로세스 주소 공간에 매핑하기를 원하는 주소 => 의미 없음, 보통 0 사용 length : PAGE_SIZE 단위의 매핑 영역 크기 prot : PROT_READ, PROT_WRITE flags : MAP_SHARED => 동일한 장치를 여러 프로세스가 열 수 있음, MAP_PRIVATE => 하나의 프로세스만 해당 영역에 접근 허용 offset : 디바이스 드라이버에 따라 다르게 해석됨 ex) 시작 물리 주소, offset 주소 등 반환값 : 성공 시 매핑된 프로세스 주소 공간 주소, 실패 시 NULL값 int munmap (void *start, size_t length); => 매핑 해제 함수

281 디바이스 드라이버에서의 mmap() 구현(1)
int mmap (struct file *filp, struct vm_area_struct *vma); 응용 프로그램에서 mmap을 호출하면 커널은 적절한 vm_area_struct를 생 성 및 초기화하여 전달한다 struct vm_area_struct unsigned long vm_start : 매핑될 가상 주소의 선두 번지 unsigned long vm_end : 매핑될 가상 주소의 마지막 번지 unsigned long vm_flags : 응용 프로그램 mmap()의 flags 내용이 적용된 값, 디바이스 드라이버가 추가로 조작함 VM_READ : 읽기 허용 VM_WRITE : 쓰기 허용 VM_EXEC : 실행 허용 VM_SHARED : 프로세스간 공유 허용 VM_IO : 메모리 맵드 I/O 주소 공간 VM_RESERVED : 스왑 아웃되지 않도록 예약

282 디바이스 드라이버에서의 mmap() 구현(2)
unsigned long vm_pgoff : 응용 프로그램 mmap()의 offset값이 PAGE_SHIFT만큼 오른쪽으로 이동되어 전달됨 간단한 예제 int mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long physical = vma->vm_pgoff << PAGE_SHIFT; unsigned long size = vma->vm_end – vma->start; if(size > MAX_MAPPING_SIZE) return –EINVAL; vma->vm_flags |= (VM_IO | VM_RESERVED); if(remap_page_range(vma, vma->vm_start, physical, size, vma->vm_page_prot)) return –EAGAIN; return 0; }

283 디바이스 드라이버에서의 mmap() 구현(3)
vma->vm_flags |= (VM_IO | VM_RESERVED)의 의미 VM_IO : 매핑된 물리 주소 공간이 I/O 영역임을 표시, 해당 영역 접근 실패 시, 프로세서가 코어 덤프하며 죽 는 것을 방지한다 VM_RESERVED : 스왑 아웃 대상에서 제외 시킨다

284 mmap() 호출 관계

285 19 모듈간의 상호 참조

286 모듈간 참조와 EXPORT_SYMBOL 매크로
수행중 커널에 모듈 추가 -> 링크 과정(심볼릭에 주소 부여) -> 심볼릭 테이블 등록 -> 외부 참조 가능 커널 프로그램 외부참조심볼선언 모듈코드 외부참조심볼선언 5 1 심볼 디바이스 드라이버는 커널에 포함시킬 수 있고, 모듈로 커널 수행중에 적재 시킬수 있다 커널 포함은 안정적이지만 편의성이 떨어진다 그래서 모듈 적재 개념이 나왔다. 커널에 외부 참조 선언된 심볼 선언을 심볼 테이블에 등록한다. 모듈은 커널에 적재될때 커널내의 심볼테이블을 참고하여 참조 주소를 얻는다 모듈에 외부 참조된 심볼 선언은 객체와 테이블을 이용해 관리한다 모듈의 외부 참조 선언된 심볼들을 커널내의 심볼 테이블에 등록한다. 모듈 코드에서 선언되지 않았던 주소를 모두 선언하면 커널에 등록된다. 심볼 테이블 3 심볼 주소 2 /proc/ksyms 4

287 모듈간 참조와 EXPORT_SYMBOL 매크로
EXPORT_SYMBOL_GPL (심볼명) : 2.6추가 EXPORT_SYMBOL_NOVERS (심볼명) : 2.4 int out_data; EXPORT_SYMBOL(out_data); void check_test(int a){ } EXPORT_SYMBOL(check_test); 2.6 에서는 GPL 라이선스가 강화되었다, 모듈라이선스가 GPL이 아니면 참조할 수 없는 심볼이 몇가지 있다. 이런 경우 모듈의 심볼을 GPL라이선스가 있는 모듈에서만 사용가능하도록 한다.

288 커널 2.4의 /proc/ksyms와 커널 2.6의 /proc/kallsyms
커널내에 심볼이 위치한 주소 심볼의 타입정보 : 2.6 심볼명 커널버전과 CRC 합친 값 : 2.4 커널 동작후 모듈로 포함되었을때 해당 심볼을 보유한 모듈명 Ver 2.4 d48132f0 usb_epnum_to_ep_desc_Rc2f07138[usbcore] Ver 2.6 d2875c60 T parport_proc_unregister[parport] If lowercase, the symbol is local; if uppercase, the symbol is global (external). A The symbol's value is absolute, and will not be changed by further linking. B The symbol is in the uninitialized data section (known as BSS). C The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols may appear with the same name. If the symbol is defined anywhere, the common symbols are treated as undefined references. D The symbol is in the initialized data section. G The symbol is in an initialized data section for small objects. Some object file formats permit more efficient access to small data objects, such as a global int variable as opposed to a large global array. I The symbol is an indirect reference to another symbol. This is a GNU extension to the a.out object file format which is rarely used. N The symbol is a debugging symbol. R The symbol is in a read only data section. S The symbol is in an uninitialized data section for small objects. T The symbol is in the text (code) section. U The symbol is undefined. V The symbol is a weak object. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error. W The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error. - The symbol is a stabs symbol in an a.out object file. In this case, the next values printed are the stabs other field, the stabs desc field, and the stab type. Stabs symbols are used to hold debugging information. ? The symbol type is unknown, or object file format specific. * The symbol name. A absolute B bss segment symbol C common symbol D data segment symbol R read-only data segment symbol T text segment symbol U undefined I indirect reference (alias to other symbol) If lowercase, the symbol is local; if uppercase, the symbol is global (external).

289 func_dev.c int func_var1 = 0; int func_var2 = 0; int func_sum ( int var3 ) { printk( "func_var1 = %d\n", func_var1 ); printk( "func_var2 = %d\n", func_var2 ); printk( "var3 = %d\n", var3 ); return func_var1 + func_var2 + var3 ; } int init_module(void) return 0; void cleanup_module(void) { EXPORT_SYMBOL_NOVERS(func_var1); EXPORT_SYMBOL_NOVERS(func_var2); EXPORT_SYMBOL_NOVERS(func_sum);

290 call_dev.c extern int func_var1; extern int func_var2; extern int func_sum ( int sub3 ); int init_module(void) { int result; func_var1 = 3; func_var2 = 4; printk( "%d + %d + 5 = %d\n", func_var1, func_var2, func_sum(5) ); return 0; } void cleanup_module(void)


Download ppt "박상원 (swpark@hufs.ac.kr) 한국외국어대학교 정보통신공학과 데이터베이스 응용 특강 박상원 (swpark@hufs.ac.kr) 한국외국어대학교 정보통신공학과."

Similar presentations


Ads by Google