데이터베이스 응용 특강.v6

Report
박상원 ([email protected])
한국외국어대학교 정보통신공학과



Linux device driver
Flash translation layer
MTD
2








공짜
모든 구성 요소를 완전히 재구성할 수 있다
값싼 하드웨어에서도 동작
강력하다
소스 코드 품질을 위해 표준을 따른다
커널은 매우 작고 아담하게 만들 수도 있다
여러 대중적인 운영체제와 잘 호환된다
지원이 잘 된다
5

Monolithic kernel
◦ microkernel






Module
Kernel thread
Multithread 응용 프로그램 지원
Preemptive vs. Nonpreemptive
Multiprocessor
File system
◦ ext2fs, ext3fs (journaling file system)
6

Linux version
◦
◦
◦
◦

2.4.x
2.5.x
2.6.x
cat /proc/version
Kernel
7

프린터 포트
◦ 총 25핀, 17개의 신호선과 8개의 접지선
◦ 신호선 : 4개의 제어선, 5개의 상태선, 8개의 데이터선
◦ 각 레지스터는 PC의 I/O 주소 공간에 매핑
 0x378, 0x379, 0x37A
◦ 프린터 포트의 인터럽트는 7번 사용
8
9
10


Linux
◦
◦
◦
◦
◦
Linus-B. Torvalds에 의해 개발
Minix
1991년 0.02 버전
1994년 1.0 버전
현재 2.6 버전
◦
◦
◦
◦
FSF(Free Software Foundation)
리차드 스톨만
GCC, Emacs 등의 다양한 유틸리티
GPL(General Public License)
GNU Project
12











Monolithic kernel
비선점형(커널 2.4)과 선점형(커널 2.6)
가상 메모리 시스템(VM)
MMU 없는 시스템도 지원
가상 파일 시스템(VFS)
모듈을 이용한 커널 확장
커널 스레드
멀티스레드 지원
멀티 프로세서 지원
강력한 네트워크 지원
GPL 라이센스
13



시스템이 지원하는 하드웨어를 응용 프로그램에서
사용할 수 있도록 커널에서 제공하는 라이브러리
하드웨어는 커널의 입장에서는 자원
응용 프로그램이 커널에게 자원 처리를 요청하는
방법
◦ System call
◦ 파일 입출력 형식을 이용한 device driver
14


소프트웨어 인터럽트 서비스 이용
각 기능별로 번호를 할당
◦ 이 번호에 해당하는 제어 루틴을 커널 내부에 정의
◦ 응용 프로그램은 원하는 기능 번호를 레지스터에 저장한
후 소프트웨어 인터럽트 서비스를 호출하여 커널에 제어
를 넘김


처음 운영체제가 설계될 당시에는 시스템 호출 방
식만 존재
하드웨어 증가에 따른 한계
15


파일 입출력 함수로 하드웨어 제어
하드웨어를 표현하는 디바이스 파일
◦ 응용 프로그램이 입출력을 시도
◦ 커널 내의 해당 디바이스 파일에 연결된 디바이스 드라이
버의 서비스 루틴 호출
16

초기에는 디바이스 드리이버를 커널 소스에 포함시켜
야
◦ 긴 커널 컴파일 시간
◦ 수정할 때마다 시스템 재부팅

커널이 부팅되어 동작중인 상태에서 디바이스 드라이
버를 동적으로 추가하거나 제거
◦ 디바이스 드라이버 개발 시간 단축
◦ 필요없는 기능을 커널에서 제거하여 커널 자원의 효율적 사용

MMU가 있는 시스템에서만 지원, 커널 버전이 동일해
야
17

시스템의 모든 자원을 파일 형식으로 표현
◦ 램, 프로세스, 태스크 등
◦ 예) /dev/mouse, /dev/console

디바이스 파일 정보
◦ 문자/블록
◦ 주번호, 부번호
◦ mknod로 생성
18

문자 디바이스 드라이버
◦ 임의의 길이를 갖는 문자열을 다루는 디바이스 드라이버
◦ 응용 프로그램에서 직접적으로 호출
◦ 버퍼없음

블록 디바이스 드라이버
◦ 일정 크기의 버퍼를 통해 데이터를 처리하는 디바이스
◦ 커널 내부의 파일 시스템에서 관리
◦ 내부적인 버퍼가 있음

네트워크 디바이스 드라이버
◦ 네트워크 층과 연결되는 디바이스 드라이버
19

일반 파일과 가장 유사한 방식으로 처리
◦ open, close, read, write 함수 이용



파일 포인터를 이용하여 특정 처리 위치 지정
응용 프로그램의 호출과 1:1로 대응
대부분의 하드웨어는 문자 디바이스 드라이버로
구현 가능
20



파일 시스템을 지원하는 구조이므로 응용 프로그
램은 파일 시스템을 통해 접근
효율적인 처리를 위해 커널 내부 버퍼 이용
블록 처리도 가능하지만 스트림 처리도 가능
◦ open, close, read, write 함수 이용하여 접근 가능
21

ls /dev/
hdj32
hdj4
hdj5
hdj6
hdj7
hdj8
hdj9
hdk
hdk1

nst28a
nst28l
nst28m
nst29
nst29a
nst29l
nst29m
nst2a
nst2l
sdam7
sdcf8 sddz9 sdft sdhm1 sdt10 ttySR1
xdb54
sdam8
sdcf9 sde
sdft1 sdhm10 sdt11 ttySR10
xdb55
sdam9
sdcg sde1 sdft10 sdhm11 sdt12 ttySR11
xdb56
sdan
sdcg1 sde10 sdft11 sdhm12 sdt13 ttySR12
xdb57
sdan1
sdcg10 sde11 sdft12 sdhm13 sdt14 ttySR13
xdb58
sdan10
sdcg11 sde12 sdft13 sdhm14 sdt15 ttySR14
xdb59
sdan11
sdcg12 sde13 sdft14 sdhm15 sdt2 ttySR15
xdb6
sdan12
sdcg13 sde14 sdft15 sdhm2 sdt3 ttySR16
xdb60
sdan13
sdcg14 sde15 sdft2 sdhm3 sdt4 ttySR17
xdb61
mknod [디바이스 파일명][디바이스 파일형][주번
호][부번호]
◦ mknod /dev/devfile c 240 1
23
파일 입출력 함수
기능
fopen, open
파일을 연다
fread, read
파일을 읽는다
fwrite, write
파일에 데이터를 쓴다
fclose, close
파일을 닫는다
24

시스템 콜을 라이브러리 함수로 만든 것
저수준 파일 입출력 함수
기능
open( )
파일이나 장치를 연다
close( )
열린 파일을 닫는다
read( )
파일에서 데이터를 읽어온다
write( )
파일에 데이터를 쓴다
lseek( )
파일의 쓰기나 읽기 위치를 변경한다
ioctl( )
read( ), write( )로 다루지 않는 특수한 제어
를 한다
fsync( )
파일에 쓴 데이터와 실제 하드웨어의 동기를
맞춘다
25
int fd = -1;
fd = open(“/dev/mem”, O_RDWR | O_NONBLOCK);
if (fd < 0) {
// 에러 처리
}
…
close(fd);

O_NONBLOCK, O_NDELAY
◦ 읽기나 쓰기가 완료되지 않더라도 저수준 파일 입출력 함
수가 즉시 종료되도록
26

방법
◦ ret_num = read(fd, buff, 10);
◦ ret_num = write(fd, buff, 10);

예
ret_num = read(fd, buff, 10);
if (ret_num < 0) {
// 에러 처리
}
if (ret_num != 10) {
// 요구된 것과 다를 때 처리
}
27
off_t ret_pos;
…
ret_pos = lseek(fd, 1234, SEEK_CUR);

Options
◦ SEEK_CUR
◦ SEEK_SET
◦ SEEK_END
28



모든 제어를 read, write 만으로는 곤란
디바이스 파일에만 적용되는 연산
10장. 디바이스의 제어
29

디바이스의 구현 방식에 따라 쓰는 방식 다를 수
있음
◦ 내부에 버퍼를 두는 경우
int ret;
…
ret = fsync(fd);

주의점
◦ 실행 시간이 길어지거나 함수가 종료되지 않을 수 있음
30

int mknod(const char *pathname, mode_t mode, dev_t dev);

다음 헤더를 포함해야
#include
#include
#include
#include

디바이스 파일 종류
◦
◦
◦
◦

<sys/types.h>
<sys/stat.h>
<fcntl.h>
<unistd.h>
S_IFCHR : 문자 디바이스
S_IFBLK : 블록 디바이스
S_IRWXU : 사용자는 읽기 쓰기 권한이 있다
S_IRWXG : 그룹은 읽기 쓰기 권한이 있다
예
◦ [ root@ ] # mknod /dev/test c 240 1
◦ mknod(“/dev/test”, S_IRWXU|S_IRWXG|S_IFCHR, (240<<8)|1);
31


프로그램의 디버깅에 사용
전역변수 참조
fd = open(“/dev/ram”, O_RDONLY);
if (fd < 0) {
perror(“open”);
}

에러 번호를 해석하여 문자열로 표현
◦ include/asm/errno.h
32
33

함수
◦ lseek : 접근할 I/O 주소를 지정
◦ read : 지정된 I/O에서 데이터를 읽어온다
◦ write : 지정된 I/O에 데이터를 쓴다
[[email protected] dev]# ls -al /dev/port
crw-r----1 root
kmem
1, 4
[ root@ ] # mknod /dev/port c 1 4
1월 30
2003 /dev/port
34
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);
}
35
for( lp = 0; lp < 10; lp++ )
{
lseek( fd, 0x378, SEEK_SET );
buff[0] = 0xFF;
write( fd, buff, 1 );
sleep( 1 );
lseek( fd, 0x378, SEEK_SET );
buff[0] = 0x00;
write( fd, buff, 1 );
sleep( 1 );
}
close( fd );
return 0;
}
36
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);
}
37
while( 1 )
{
ioctl( fd, LPGETSTATUS, &prnstate );
// 13 Pin <--> GND Pin
if( prnstate & LP_PSELECD ) printf( "ON\n" );
else
printf( "OFF\n" );
usleep( 50000 );
}
close( fd );
return 0;
}
38
40
#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");
}
41
KERNELDIR = /lib/modules/$(shell uname -r)/build
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O
all: test.o
clean:
rm -rf *.o
42
[
[
[
[
root@
root@
root@
root@
]
]
]
]
#
#
#
#
make
insmod test.o
dmesg
rmmod test
43
#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");
44
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
45
[
[
[
[
root@
root@
root@
root@
]
]
]
]
#
#
#
#
make
insmod test.ko
dmesg
rmmod test.ko
46
47
[ root@ ] # cat /proc/ksyms
…
#c0134a50 register_chardev_R1a5f156e
#c0134ae0 unregister_chrdev_Rc192d491
48
유틸리티
기능
insmod
모듈을 커널에 적재한다.
rmmod
커널에서 모듈을 제거한다.
lsmod
커널에 적재된 모듈 목록을 보여준다.
depmod
모듈간 의존성 정보를 생성한다.
modprobe
모듈을 커널에 적재하거나 제거한다.
49
커널 2.4
커널 2.6
int init_module(void);
module_init(hello_init);
void cleanup_module(void);
module_exit(hello_exit);
50

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
51

모듈 매개변수
◦ 초기값 중에서 외부에서 변경할 수 있는 것
◦ I/O 포트, IRQ 번호 등
52
#include
#include
#include
#include
<linux/init.h>
<linux/module.h>
<linux/kernel.h>
<linux/moduleparam.h>
static int onevalue = 1;
static char *twostring = NULL;
module_param(onevalue, int, 0);
module_param(twostring, charp, 0);
53
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 [email protected]");
MODULE_DESCRIPTION("Module Parameter Test Module");
MODULE_LICENSE("Dual BSD/GPL");
54

module_param(변수명, 변수 타입, 접근 속성)
변수 타입
C 타입
short
short
ushort
unsigned short
int
int
uint
unsigned int
long
long
ulong
unsigned long
charp
char *
bool
int
invbool
int
intarray
int *
55
[ root@ ] # insmod test.ko onevalue=0x27 twostring=“Oh my godrmmod test”
[ root@ ] # rmmod test.ko
[ root@ ] # dmesg
.
.
.
Hello, world [ onevalue=38:twostring=Oh my godrmmod test]
Goodbye, world
56

레벨
◦ 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을 포함해야 출력
◦ 수행시간이 많이 걸리는 함수
57
상수 선언문
의미
KERN_EMERG
“<0>” /* 시스템이 동작하지 않는다 */
KERN_ALERT
“<1>” /* 항상 출력된다. */
KERN_CRIT
“<2>” /* 치명적인 정보 */
KERN_ERR
“<3>” /* 오류 정보 */
KERN_WARNING
“<4>” /* 경고 정보 */
KERN_NOTICE
“<5>” /* 정상적인 정보 */
KERN_INFO
“<6>” /* 시스템 정보 */
KERN_DEBUG
“<7>” /* 디버깅 정보 */
58


지역 변수, 전역 변수 모두 커널 메모리 공간에 배
치
지역 변수
◦ 가급적 지역 변수를 사용하는 것이 좋다.

전역 변수
◦ 컴파일 시점에 크기와 주소가 정의
◦ 디바이스 드라이버가 커널에 적재되고 해제되는 시점까
지 유지해야 하는 정보
60


변수 명이나 함수 명이 중복 정의되는 경우
static 으로 중복 방지
static int checkout = 0;
// 변수 선언 예
static int dev_app_check(void)
{
// 함수 선언 예
}
61



32비트에서 64비트로 전환되는 시점
플랫폼간 호환성
사용하는 변수의 데이터 형을 명확히 기술
62

가장 문제가 되는 타입 : 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
32비트
__s64, s64
64비트
__u64, u64
64비트
8비트
16비트(워드)
63

Alignment 무시한 구조체 필요
◦ packed
typedef struct {
u16 index;
u16 data;
u8 data2;
} __attribute__ ((packed)) testctl_t;
64

Little endian vs. Big endian

#include <asm/byteorder.h>
◦ #define __LITTLE_ENDIAN
◦ #define __BIG_ENDIAN
65

메모리 번지를 이용하는 I/O 접근 처리
◦ u32 *ptr = (u32 *) 0xE0000300;
◦ *ptr = 0x1234;
◦ *ptr = *ptr & 0xFF;

컴파일러 입장에서는 최적화 시도
◦ *ptr = 0x1245 & 0xFF;

항상 만족하게 하려면 최적화 방지
◦ volatile u32 *ptr = (volatile u32 *) 0xE0000300;
66

일반적인 C에서
#include <stdlib.h>
char
*buff;
buff = malloc(1024);
if (buff == null) exit(1);
. . .
free(buf);
67


프로세스 사용자 메모리 공간과 다른 커널 메모리 공간
PAGE_SIZE 단위로 할당하는 특성
◦ 보통 4KB


시스템에 가용 메모리가 없는 상황
가상 메모리 기법에 의해 접근하고자 하는 메모리가 보
조 장치에 존재하는 경우
◦ swapping


DMA 같은 디바이스가 사용하는 연속된 물리 메모리 주
소가 필요한 경우
인터럽트 상태에서 메모리를 할당하는 경우
◦ 커널 내에 메모리가 부족하여 sleep 하면 안됨
68



kmalloc( ), kfree( )
__get_free_pages( ), free_pages( )
vmalloc( ), vfree( )
69

할당 가능한 크기 : 32 x PAGE_SIZE
◦ 일반적으로 PAGE_SIZE는 4K이므로 131072 bytes 이상
은 곤란
#include <linux/slab.h>
char * buff;
buff = kmalloc(1024, GFP_KERNEL);
if (buff != NULL)
kfree(buff);
70

GFP_KERNEL
◦ 동적 메모리 할당이 항상 성공하도록 요구
◦ 충분한 메모리가 없을 때 sleep
◦ 인터럽트 서비스에 사용할 때는 사용하면 안됨

GFP_ATOMIC
◦ 커널에 할당 가능한 메모리가 있으면 무조건 할당
◦ 없으면 즉시 NULL 반환
◦ sleep 하는 경우가 없음

GFP_DMA
◦ 연속된 물리 메모리를 할당방을 때 사용
◦ 디바이스 드라이버가 동작하는 메모리 공간은 물리적인 메모리가 아닌
가상 주소 메모리  실제 물리적 공간은 분할되어 있을 수 있음
◦ DMA 콘트롤러를 사용할 때
71

가상 공간이 허용하는 한 크기 제한 없이 할당
◦ 가상 주소 공간이므로 할당할 메모리가 디스크에 있을 수 있음
◦ 커다란 연속 공간을 할당하기 위해 가상 메모리 관리 루틴이 실행
되므로 kmalloc 보다 느리다
◦ 인터럽트 서비스 함수 안에서 사용 불가
#include <linux/vmalloc.h>
char *buff;
buff = vmalloc(1024);
. . .
vfree(buff)
72

페이지의 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);
73
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 );
if( buff != NULL )
{
printk( "Big Memory Ok\n" );
kfree( buff );
}
}
74
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 );
}
}
75
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);
}
}
76
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");
77

커널 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)
78



mempool_alloc 함수로 할당
실패시 사전에 미리 할당받았던 메모리 반환
그래도 실패시는 gfp_mask 옵션에 따라 실패 혹
은 slepp
79
#define MIN_ELEMENT
#define TEST_ELEMENT
4
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;
}
80
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 );
81
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" );
for( lp=0; lp < TEST_ELEMENT; lp++ ) {
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);
82
Module MEMPOOL Test
call mempool_create
----> mempool_alloc_test
----> mempool_alloc_test
----> mempool_alloc_test
----> mempool_alloc_test
mempool allocate
----> mempool_alloc_test
alloc data 4
----> mempool_alloc_test
alloc data 5
----> mempool_alloc_test
alloc data 6
----> mempool_alloc_test
alloc data 7
mempool free
----> call mempool_free_test
----> call mempool_free_test
----> call mempool_free_test
----> call mempool_free_test
call mempool_destroy
----> call mempool_free_test
----> call mempool_free_test
----> call mempool_free_test
----> call mempool_free_test
Module MEMPOOL Test End
83

응용 프로그램
◦ 사용자 공간에서 프로세스로 동
작
◦ 하드웨어에 직접 접근 못함

디바이스 파일

디바이스 드라이버
◦ 문자 디바이스 드라이버의 경우
응용 프로그램에서 해당 디바이
스 드라이버와 연결된 디바이스
파일을 통해 호출
◦ 블록 디바이스 드라이버나 네트
워크 디바이스 드라이버는 커널
에서 직접 호출
85
86

시리얼 입출력
◦ 시작과 끝이 없음
◦ 처리 용량, 보존 여부 불명확
87
88

커널은 디바이스 파일에 기록된
◦ 디바이스 타입과 주번호를 이용해
◦ 커널 내에 등록된 디바이스 드라이버 함수를 연결
◦ struct char_device_struct chrdevs[MAX_PROBE_HASH];
 여기에 struct file_operations *fops 필드 관리
89
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
*);
};
90
91

반환값 : 정상 (0), 오류값
92

반환값 : 성공 (0), 오류값
93



xxx_read에 전달된 버퍼의 주소값은 프로세스 메모리
공간의 주소이므로 직접 사용할 수 없음
파일 포인터 f_pos 관리
반환값 : 처리된 데이터 수나 오류 값
94



xxx_write에 전달된 버퍼의 주소값은 프로세스 메모리
공간의 주소이므로 직접 사용할 수 없음
파일 포인터 f_pos 관리
반환값 : 처리된 데이터 수 혹은 오류값
95
96
97

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)
98
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);
99
10
0
#define
#define
CALL_DEV_NAME
CALL_DEV_MAJOR
"calldev"
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;
}
10
1
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;
}
10
2
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);
10
3
#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 );
printf( "ret = %08X\n", ret );
10
4
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( "ret = %08X\n", ret );
printf( "6) device file close\n");
ret = close(dev);
printf( "ret = %08X\n", ret );
}
return 0;
}
10
5
[ root@ ] # mknod /dev/calldev c 240 32
[ root@ ] # insmod call_dev.ko
[ root@ ] # lsmod
Module
Size
Used by
call_dev
2432
0
autofs
15438
0
[ root@ ] # gcc –o call_app call_app.c
[ root@ ] # ./call_app
1) device file open
2) seek function call
ret = 00000023
3) read function call
ret = 00000033
4) write function call
ret = 00000043
5) ioctl function call
ret = 00000053
6) device file close
10
6
[ root@ ] # dmesg
call call_init
call open -> minor : 32
call llseek -> off : 00000020, whenec : 00000000
call read -> buf : 00000030, count : 00000031
call write -> buf : 00000040, count : 00000041
call ioctl -> cmd : 00000051, arg : 00000052
call release
[ root@ ] # rmmod call_dev
10
7
10
8









디바이스 드라이버의 등록과 해제
디바이스 드라이버의 내부 구조체의 메모리 할당과 해제
여러 프로세스가 하나의 디바이스에 접근할 때 필요한
사전 처리 및 종료 시 처리
하드웨어 검출 처리 및 에러 처리
하드웨어 초기화와 제거 가능한 하드웨어의 제거 처리
응용 프로그램에서 디바이스 드라이버를 사용하는 경우
의 초기 처리 및 사용 종료 처리
부 번호에 관련된 프로세스별 처리
프로세스별 메모리 할당과 해제
사용하는 모듈 수의 관리
10
9

모듈 적재와 커널 부팅 처리 과정 또는 제거 과정
◦ insmod 명령 : module_init – 모듈 적재 과정
◦ rmmod 명령 : module_exit – 모듈 제거 과정

응용 프로그램이 디바이스 파일을 여는 과정과 닫는 과
정
◦ open( ) 함수 : file_operations.open – 디바이스 파일을 여는 과
정
◦ close( ) 함수 : file.operations.release – 디바이스 파일을 닫는
과정
11
0


디바이스 드라이버의 등록
디바이스 드라이버에 내부 구조체의 메모리 할당
◦ 전역 변수 등의 할당받은 메모리 초기화

여러 프로세스가 하나의 디바이스에 접근하는 경우에 필
요한 사전 처리
◦ 20장. 다중 프로세스 환경의 디바이스 드라이버

주 번호에 종속된 부 번호를 관리하기 위한 사전 처리
◦ 9장. 주 번호와 부 번호의 처리

하드웨어 검출 처리 및 에러 처리
◦ 하드웨어가 검출되지 않거나 수행이 곤란한 에러가 발생할 시 모
듈이 커널에 적재되는 시점에 거부하는 것이 좋음

하드웨어 초기화
11
1

디바이스 드라이버의 해제
◦ 커널 자원을 점유하고 있기 때문에 반드시 해제

디바이스 드라이버에 할당된 모든 메모리 해제
◦ 적절히 제거하지 않으면 가용 메모리가 부족해 짐

하드웨어 제거에 따른 처리
11
2
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);
11
3
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;
}
11
4





디바이스 드라이버가 처음 열렸을 때 하드웨어 초
기화
디바이스 드라이버의 동작에 필요한 에러 체크
부 번호에 대한 처리가 필요한 경우 파일 오퍼레이
션 구조체 갱신
프로세스별 메모리 할당과 초기화
모듈의 사용 횟수 증가 (커널 2.4)
11
5
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;
}
11
6


프로세스별 할당 메모리 해제
모듈 사용 횟수 감소 (커널 2.4)
11
7
11
8
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”에 데이터를 써넣거나 읽기
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,
•처리 조건이 되지 않을 때의 블록 처리
…
•하드웨어 제어 함수
};
•여러 프로세스가 동시에 접근했을 때의 경쟁 처리
•인터럽트 서비스 함수와의 경쟁 처리
디바이스 드라이버의 형식
read() 함수의 구현 (3)
I/O 제어함수
inb, inw, inl .. readb ..
read(fd,buff,size)
read
메모리 복사 함수
put_user, copy_to_user
Device
file
하
드
file_operation
buffer
interrupt service
웨
어
write(fd,buff,size)
메모리 복사 함수
get_user, copy_from_user
write
I/O 제어함수
outb, outw, outl .. writeb ..
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() 함수의 구현 (5)



응용 프로그램으로의 데이터 전달
하드웨어 정보를 읽어 들이거나 인터럽트 처리 루틴과 같이
read()이외에서 발생되어 저장된 버퍼 데이터를 이용
시간적으로 굳이 동기화가 필요없는 간단한 하드웨어 처리
장치라면 직접 하드웨어에서 읽어서 전달하는 구조로 작성
시간적으로 민감한 경우라면 인터럽트를 이용한 버퍼구조
로 작성
개발자의 설계방식에 따라 선택적
하드웨어를 다룬다면 다음 함수 이용
inb, inw, inl, insb, insw, insl,
outb, outw, outl, outsb, outsw, outsl
readb, readw, readl,
writeb, writew, writel,
memset_io, memcpy_fromio, memcpy_toio
read() 함수의 구현 (6)




디바이스 드라이버의 read() 함수가 반환하는 값은 0 이상인
경우와 0 미만인 경우
반환값은 응용프로그램에서 호출한 read()함수에 그대로 전
달되므로 응용 프로그램은 반환값을 보고 read()함수 결과처
리
0 이상은 read() 함수가 정상적으로 수행됨
이 값은 디바이스 드라이버의 read()함수가 응용프로그램에
전달한 데이터의 개수(count)가 됨
Read() 함수가 수행되는 시점에 하드웨어에서 발생한 데이터
의 개수가 count 보다 적을 경우
count 값이 만족될 때까지 기다릴것인지 아니면 발생된 데이
터만 처리하고 종료할 것인지는 응용프로그램에서 디바이스
파일을 열었을 때 어떤 옵션을 주었는가에 따라 달라짐.
read()함수의 매개변수중에서 struct file *filp를 참조하여 판
단
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 값이 만족될 때까지 기다려
read() 함수의 구현 (8)



반환값이 요구된 값과 항상 일치하지 않음
O_NONBLOCK이나 O_NDELAY 옵션을 주고 파일을 열었
을 때
read() 함수의 반환값이 음수값일 때
EAGAIN : O_NONBLOCK으로 열렸지만 즉시 읽을 수 있
는 데이터가 없슴.
EIO : I/O 에러가 발생
EFAULT : buf는 접근할 수 없는 주소 공간을 가리킴
디바이스 드라이버를 읽기 / 쓰기 위치를 관리하는 형태로
작성해야 한다면 f_pos 매개변수를 처리해야 함.
read() 함수의 구현 (9)
ssize_t xxx_read(strcuct file *filp, const
ssize_t rdwr_read(strcuct file *filp,
char *buf, size_t count, loff_t *f_pos) {
char *buf, size_t count, loff_t *f_pos) {
if(!(준비된 데이터가 있는가?)) {
unsigned char status;
if(!(filp->f_flags & O_NOBLOCK)) {
int loop;
// 블록 모드로 열렸다면
// 프로세스를 재운다.
// 하드웨어에서 데이터를 읽는다.
}
for(loop = 0; loop < count; loop++) {
}
status = inb(RDWR_READ_ADDR);
// 하드웨어에서 데이터를 읽는다.
// 사용자 공간에 데이터를 전달한다.
// inb(), outb(),…., read(), write() 함수사용
put_user(status, (char *) &buf[loop];
// 또는 버퍼를 읽는다.
}
// 처리된 데이터 개수
// 사용자 공간에 데이터를 전달한다.
return count;
// copy_to_user, put_user
}
return 처리된 데이터 개수;
}
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() 함수의 구현 (2)




디바이스 드라이버의 write() 함수가 반환하는 값은 0 이상인
경우와 0 미만인 경우
반환값은 응용프로그램에서 호출한 write()함수에 그대로 전
달되므로 응용 프로그램은 반환값을 보고 write()함수 결과처
리
0 이상은 write() 함수가 정상적으로 수행됨
이 값은 디바이스 드라이버의 write()함수가 응용프로그램에
전달한 데이터의 개수(count)가 됨
write() 함수가 수행되는 시점에 하드웨어에서 발생한 데이터
의 개수가 count 보다 적을 경우
count 값이 만족될 때까지 기다릴것인지 아니면 발생된 데이
터만 처리하고 종료할 것인지는 응용프로그램에서 디바이스
파일을 열었을 때 어떤 옵션을 주었는가에 따라 달라짐.
write()함수의 매개변수중에서 struct file *filp를 참조하여
판단
write() 함수의 구현 (3)



반환값이 요구된 값과 항상 일치하지 않음
O_NONBLOCK이나 O_NDELAY 옵션을 주고 파일을 열었
을 때
write() 함수의 반환값이 음수값일 때
EAGAIN : O_NONBLOCK으로 열렸지만 즉시 읽을 수 있
는 데이터가 없슴.
EIO : I/O 에러가 발생
EFAULT : buf는 접근할 수 없는 주고 공간을 가리킴
ENOSPC : 데이터를 위한 공간이 없다.
디바이스 드라이버를 읽기 / 쓰기 위치를 관리하는 형태로
작성해야 한다면 f_pos 매개변수를 처리해야 함.
struct file *filp
•응용
프로그램에서 디바이스
파일을 open함수로 열었을 때
flags에 설정된 값.
struct file {
…
unsigned int
f_flags;
•현재의 읽기/쓰기 위치를 담
loff_t
f_pos;
음
void
*private_data;
struct file_operation *f_op;
…
};
•프로세스가
함수간에 메모리
를 공유할 목적이라면 적극 활
용
•부번호에
따라 다르게 동작하
는 디바이스 드라이버를 작성
할 수 있슴.
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>를 소
스에 포함(대부분 매크로 함수)
사용자 메모리 공간
일반적인 동작 상태
의미
프로세스 #1
(사용자 모드)
커널 메모리 공간
프로세스 #1
(커널 모드)
시스템 콜 인터럽트
시스템 전체를 처리
하는 상태 의미
복귀
저수준 파일 입출력
함수(소프트웨어 인
터럽트를 이용)
프로세스 #2
(사용자 모드)
프로세스 #2
(커널 모드)
시스템 콜 인터럽트
커널 모드에서
사용자 메모리 공간 접근
복귀
프로세스 #3
(사용자 모드)
verify_area(type, addr, size)
프로세스 #3
(커널 모드)
시스템 콜 인터럽트
복귀
copy_to_user(to, from, n)
copy_from_user(to, from, n)
get_user(x, ptr)
put_user(x, ptr)
#define RDWR_DEV_NAME
#define RDWR_DEV_MAJOR
"rdwrdev"
240
#define RDWR_WRITE_ADDR
#define RDWR_READ_ADDR
0x0378
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;
13
4
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,
};
13
5
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");
13
6
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;
write(dev,buff,1 );
sleep(1);
}
close(dev);
13
7
13
8

주 번호
리눅스 파일
-
응용 프로그램 #1
응용 프로그램 #2
디바이스 제어 요청
커널(Kernel)
디바이스 제어
함수 호출
디바이스 드라이버
디바이스 제어
디바이스
그림9-1] 리눅스에서의 디바이스 제어 구조
-
디렉토리 구조로 알려진 레이블의 계층구
조에 의해서 구성
레이블에 의해 참조되는 파일
 정규파일(regular file)
 디렉토리 파일(directory file)
 특수파일(special file)
- 커널 내부의 함수를 호출할 수 있는 정
보 제공
- 디바이스 파일(타입정보, 주 번호, 부
번호)
- mknod /dev/xxx c M m
응용프로그램
OPEN()
READ()
부 번호
주 번호
디바이스 드라이버
타입정보
chrdevs[주번호]
blkdevs[주번호]
파일 입출력 함수 정보
file_operation
xxx_open()
xxx_read()
제어하려는 디바이스를
구분하기 위한 디바이스의 ID

Inode(/include/linux/fs.h)
struct inode {
…
unsigned long
atomic_t
umode_t
unsigned int
uid_t
gid_t
dev_t
};
i_ino;
i_count;
i_mode;
i_nlink;
i_uid;
i_gid;
-
파일의 유형:정규파일, 디렉토리, 특수 등
허가권
소유권과 그룹id
하드링크 계수
마지막 수정과 마지막 접근 시간
정규/디렉토리 파일 : 디스크 블록의 위치
특수 파일 : 주/보조 장치 번호
심볼릭 링크 : 심볼릭 링크의 값
…
i_rdev;
loff_t
struct timespec
struct timespec
struct timespec
unsigned int
unsigned long
unsigned long
unsigned long
unsigned short
unsigned char
…
i_size;
i_atime;
i_mtime;
i_ctime;
i_blkbits;
i_blksize;
i_version;
i_blocks;
i_bytes;
i_sock;
/2
bin3
ls5
usr4
cp6
test.c7
label inode #
.
2
..
2
bin
3
usr
4

부 번호
◦ 디바이스의 구분
COM1, COM2 구분
◦ 용도에 따른 디바이스의 구분(misc 계열 디바이스)
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;
}

디바이스 타입
◦ kdev_t (2.4)
 “include/linux/kdev_t.h”
typedef unsigned short kdev_t;
#define MINORBITS
#define MINORMASK
#define MAJOR(dev)
#define MINOR(dev)
#define HASHDEV(dev)
#define MKDEV(ma,mi)
8bits
8
((1U << MINORBITS) - 1)
((unsigned int) ((dev) >> MINORBITS))
((unsigned int) ((dev) & MINORMASK))
((unsigned int) (dev))
(((ma) << MINORBITS) | (mi))
8bits
Major
Minor
◦ dev_t (2.6)
 “include/linux/coda.h”
typedef unsigned long dev_t;
#define MINORBITS
12bits
20bits
Major
Minor
20
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);
}
#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;
}
#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);
if(filp->f_op && filp->f_op->open)
return filp->f_op->open(inode, filp);
retun 0;
}
Int init_module()
Int cleanup_module()
return 0;
}
◦ 실행방법
[root @#] mknod /dev/minor_write c 240 1
[root @#] mknod /dev/minor_write c 240 2
[root @#] make
2.4 [root @#] insmod minor_dev.o
2.6 [root @#] insmod minor_dev.ko
참고) lsmod
cat /proc/ksyms | grep <모듈이름>
[root @#] ./minor.app
..
..
READ DATA [7F]
READ DATA [7F]
READ DATA [7F]
READ DATA [6F]
Input ok…
led flashing…
[root @#]
[root @#] rmmod minor_dev
[root @#] dmesg


Document/devices.txt
테스트나 특정 플랫폼용으로 할당된 주 번호와 부
번호
◦ 주 번호
 60~63
 120~127
 240~254
◦ 부 번호
 주 번호 10번의 부 번호 240~255
#define
#define
#define
#define
MINOR_DEV_NAME
MINOR_DEV_MAJOR
MINOR_WRITE_ADDR
MINOR_READ_ADDR
"minordev"
240
0x0378
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" );
return 0;
}
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" );
return 0;
}
struct file_operations minor0_fops = {
.owner = THIS_MODULE,
.write = minor0_write,
.open
= minor0_open,
.release = minor0_release,
};
14
8
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);
}
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");
return 0;
struct file_operations minor_fops =
{
.owner = THIS_MODULE,
.open
= minor_open,
};
14
9
#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;
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;
write(write_dev,buff,1 );
sleep(1);
}
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 );
exit(1);
}
close(read_dev);
close(write_dev);
}
return 0;
15
0
15
1

01. 디바이스 제어
◦
◦
◦
◦

디바이스 제어 ioctl()함수의 역할
ioctl()함수의 일반적 형태
ioctl()함수의 매개변수
cmd관련 MACRO함수들
02. ioctl() 함수 사용 예

디바이스 제어 ioctl() 함수의 역할
◦
◦
◦
◦
◦
Input / Output Control
device driver와 application간에 범용적인 대화 통로
할당 받은 인터럽트 변경
점유하고 있는 물리주소 변경
그외 여러가지…

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
cmd관련 MACRO 함수

31
15
0
2
14
8
8
direction
size
type
number
◦
◦
◦
◦
32bits cmd의 구성
구분번호(ordinal / sequential number) : 2 bits
명령을 구분하는 명령어의 순차번호
매직번호(type) : 8 bits
다른 디바이스 드라이버의 ioctl명령과 구분하기 위한 값
데이터크기(size) : 14 bits
매개변수 arg를 통해 전달되는 메모리의 크기
읽기쓰기구분(direction) : 8 bits
일기, 쓰기를 위한 요구 명령을 구분하는 속성

asm/ioctl.h
/* asm/ioctl.h */
#define _IOC_NRBITS
#define _IOC_TYPEBITS
#define _IOC_SIZEBITS
#define _IOC_DIRBITS
8
8
14
2
/*
/*
/*
/*
Number field */
Type field */
Size field */
Direction field */

asm/ioctl.h
/* Direction bits.*/
#define _IOC_NONE
#define _IOC_WRITE
#define _IOC_READ
0U
1U
2
/* Nothing */
/* Writing */
/* Reading */
/* 각 field들을 완전한 32 bits인 하나의 cmd 로 만듬*/
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))

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))

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)
#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
#define IOCTLTEST_LEDON
#define IOCTLTEST_GETSTATE
_IO( IOCTLTEST_MAGIC, 0 )
_IO( IOCTLTEST_MAGIC, 1 )
_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_
16
1
#include "ioctl_test.h"
#define IOCTLTEST_DEV_NAME
#define IOCTLTEST_DEV_MAJOR
"ioctldev"
240
#define IOCTLTEST_WRITE_ADDR
#define IOCTLTEST_READ_ADDR
0x0378
0x0379
int ioctltest_open (struct inode *inode, struct file *filp)
{
return 0;
}
int ioctltest_release (struct inode *inode, struct file *filp)
{
return 0;
}
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 );
16
2
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 );
break;
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 );
break;
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 );
break;
case IOCTLTEST_WRITE_READ : 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 );
}
}
return 0;
ctrl_info.buff[0] = inb( IOCTLTEST_READ_ADDR );
ctrl_info.size = 1;
copy_to_user ( (void *) arg, (const void *) &ctrl_info, (unsigned long ) size );
break;
16
3
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");
16
4
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 );
16
5
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( 500000 );
}
printf( "wait... input\n" );
cnt = 0;
state = 0xFF;
16
6
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( 100000 );
}
ioctl(dev, IOCTLTEST_LEDOFF );
close(dev);
}
return 0;
}
16
7
# mknod /dev/ioctldev c 240 0
# insmod ioctl_dev.ko
# ./ioctl_app
wait… input
wait… input
wait… input
# rmmod ioctl_dev
16
8
16
9
하드웨어 (x86)
RTC(Real Time Clock)
TSC(Time Stamp Counter)
PIT(Programmable Interval Timer) – 8254 호환칩
APIC(Advanced Programmable Interrupt Controller) 내의 타이머
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
do_IRQ(0)
timer_interrupt
do_timer_interrupt
do_timer_interrupt_hook
do_timer
jiffies_64++
update_times()
2.4
#define DIFF_TIME(3*HZ/10)
u32 aftertime;
aftertime = jiffies + DIFF_TIME;
#define DIFF_TIME (30)
u32 aftertime;
aftertime = jiffies + DIFF_TIME;
2.6
#define DIFF_TIME (30)
u64 aftertime;
aftertime = get_jiffies_64() + DIFF_TIME*(HZ/USER_HZ);
#define DIFF_TIME (3*HZ/10)
u64 aftertime;
aftertime = get_jiffies_64() + DIFF_TIME;
짧은 지연
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
#define DELAY_TIME_MSEC (3*HZ/10)
u64 endtime = get_jiffies_64() + DELAY_TIME_MSEC;
while(jiffies<endtime);
struct timeval {
time_t
suseconds
};
tv_Sec;
tv_usec;
// sec
// microsec
struct timespec {
time_t
long
};
tv_sec;
tv_nsec;
// sec
// nano sec
do_gettimeofday() : 시스템 시간을 초로 얻어온다
do_settimeofday() : 초로 환산된 시스템 시간을 설정한다
mktime() : 날짜와 시간을 초로 바꾼다
커널 타이머 목록 :
수행할 함수와 함수가 수행되어야 하는 시간에 대한 정보를 담
고 있는 연결리스트
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() : 커널 타이머 목록에서 등록된 것을 제거한다
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);
타이머 장치
1/Hz 간격으로 인터럽트 발생
디바이스 드라이버
커널
struct timer_list timer
타이머 인터럽트 처리
1/Hz 초 간격으로 호출
__run_timers
1) 초기화
timer_list
2) 등록
4)자동제거
등록된 timer_list 에서
timer.expires >= jiffies_64 검사
timer.function 호출 후 제거
data
3) 호출
4) 제거
init_timer(&timer);
timer.expires = jiffies_64 + 호출시간;
timer.data = (unsigned long)data addr;
timer.function = handler;
add_timer(&timer);
void handler (unsigned long arg) {
…
}
del_timer(&timer);
#define
#define
KERNELTIMER_WRITE_ADDR
TIME_STEP
0x0378
(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);
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);
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");
18
2


어떤 프로세스가 수행되는 도중에 다른 서비스 처리 루틴이 끼어들
어 프로세스의 수행을 방해하는 것.
인터럽트가 발생하면 수행 중인 프로세스의 상태를 저장하고 수행을
중단한 다음 ISR (Interrupt Service Routine)을 수행.

ISR처리가 끝나면 중단된 프로세스를 다시 수행.

인터럽트의 종류
◦ 오류 인터럽트와 같은 내부 인터럽트
◦ 외부 인터럽트 (IRQ요청 사용)
◦ IRQ – Interrupt Request Line

Interrupts : Asynchronous
◦ H/W device는 cpu clock에 비동기적 으로 interrupt를 발생시키
므로 kernel은 언제든지 interrupt에 의해 방해 받을 수 있음

Exception : Synchronous
◦ CPU clock에 동기화되어 발생
◦ CPU가 명령을 실행하는 도중이나 프로그래밍 에러등에 의해 발생

Interrupt나 exception에 의한 code는 process에 의한
실행code가 아님


Architecture에 따라 처리하는 방법이 다르므로 Linux
Kernel에서는 do_IRQ()함수를 통해 IRQ Interrupt를 처리.
Interrupt가 발생한 IRQ 번호에 대해 등록된 서비스 함수가
없는 경우 해당 Interrupt는 무시된다.
Hareware
IRQ interrupt 발생
Kernel
Device driver
아키텍쳐별 IRQ 처리루틴
do_IRQ(n)
irq_desc[irqs]
struct xxx data;
1) 등록
request_irq(irq, int_handler, flag, “xxx”, &data);
irqreturn_t int_handler(int irq, void *dev_id, struct ptr_regs
*regs)
{
2) 호출
handle a interrupt...
등록된 irq_desc에서 발생된 irq ISR
호출
}
3) 제거
free_irq(irq, &data)
Kernel 2.4
void int_interrupt( int irq, void *dev_id, struct pt_regs *regs)
{
}
Kernel 2.6
irq_return_t int_interrupt( int irq, void *dev_id, struct pt_regs *regs)
{
return IRQ_HANDLED;
}
Interrupt
number
Interrupt ID or
Interrupt 함수가 사용 가능한
메모리 주소
Interrupt 발생시
레지스터 값
irq re tu rn _ t in t_ in te rru p t( in t irq , vo id *d e v _ id , stru ct p t_ re g s *re g s )
{
ch a r *d a ta ;
…
d a ta = km a llo c( 1 2 , G F P _ A T O M IC );
if ( d a ta != N U L L )
{
…
kfre e (d a ta );
}
…
re tu rn IR Q _ H A N D L E D ;
}
 Memory 할당 시 kmalloc() / kfree() 사용.
 vmalloc() / vfree() / ioremap() 은 process휴면 가능성이 존재하기 때문
에 사용하기 곤란
 kmalloc()도 GFP_ATOMIC인자를 사용해 process휴면 가능성을 제거 해야
함
 ISR이전에 할당한 Memory는 제약없이 사용가능.
 ISR은 request_irq()를 이용해 kernel에 등록된 후 사용 가능하다.
int request_irq( unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long
frags, const char *device, void *dev_id );
Kernel 2.6
int request_irq ( unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned
long flags, const char *device, void * dev_id );

Flags



request_irq()는 sleep할 수
있으므로 interrupt context
나 중단돼서는 안 되는 상황에
서 호출할 수 없다.
 등록이 성공하면 /proc/irq에
해당 interrupt항목 생성.

Kernel 2.4
SA_INTERRUPT
: 다른 interrupt를 허가하지 않음
SA_SHIRQ
: interrupt 번호를 공유
SA_SAMPLE_RANDOM : Random 값 처리에 영향을 줌
irqreturn_t xxx_interrupt ( int irq, void *dev_id, struct pt_regs *regs )
{
…
return IRQ_HANDLED;
}
int xxx_open ( struct inode *inode, struct file *filp )
{
if ( !retuest_irq(XXX_IRQ, xxx_interrupt, SA_INTERRUPT, “xxx”, NULL) )
{ //정상등록 }
return 0;
}

Return value

Success : 0
◦ Arguments
K e rn e l 2 .4
in t re tu e s t_ irq ( u n s ig n e d in t irq , v o id (*h a n d le r)(in t, v o id *, s tru c t p t_ re g s *), u n s ig n e d lo n g fla g s , c o n s t
c h a r *d e v ic e , v o id * d e v _ id )
K e rn e l 2 .6
in t re q u e s t_ irq ( u n s ig n e d in t irq , irq re tu rn _ t (*h a n d le r)(in t, v o id *, s tru c t p t_ re g s *), u n s ig n e d lo n g fla g s ,
c o n s t c h a r *d e v ic e , v o id *d e v _ id )
해 당 d e v ic e
유일하게 식별하기
할 당 할 in te rru p t
A S C II 이 름
위 한 ID
번호
In te rru p t
h a n d le r
O p tio n fla g s
1.5 인터럽트 함수 해제
void free_irq( unsigned int irq, void *dev_id )
 반드시 process context에서 호출


Global Variable 사용
◦ Interrupt 하나에 interrupt service 함수 하나가 동작하는
경우
◦ 하나의 interrupt service함수로 여러 device를 제어하는 경
우에는 사용할 수 없음

Device의 정보를 전달하는 가장 보편적인 방법 ( 다중 프로
세스 환경에 적합 )
in t in t_ o p e n ( s tru c t in o d e *in o d e , s tru c t file *filp )
{
R _ IN T _ IN F O *p trIn fo ;
p trIn fo = k m a llo c (s iz e o f(R _ IN T _ IN F O ), G F P _ K E R N E L );
filp -> p riv a te _ d a ta = p trIn fo ;
…
if ( !re q u e s t_ irq (X X X _ IR Q , c o u n t_ in te rru p t, S A _ IN T E R R U P T , “ te s t”, p trIn fo ) )
{
e n a b le _ h a rd w a re _ in t(p trIn fo );
}
re tu rn 0 ;
}
in t in t_ re le a s e ( s tru c t in o d e *in o d e , s tru c t file *filp )
{
R _ IN T _ IN F O *p trIn fo = (R _ IN T _ IN F O *) filp -> p riv a te _ d a ta ;
d is a b le _ h a rd w a re _ in t(p trIn fo );
fre e _ irq ( X X X _ IR Q , p trIn fo );
k fre e ( p trIn fo );
re tu rn 0 ;
}

PC와 같은 범용 System
◦ open() / close() 에서 등록 / 해제

특정 목적의 System
◦ 모듈의 등록 / 해제 시점에 맞추어 등록 / 해제

request_irq() 함수를 사용할 때 flags와 dev_id를 변경
◦ flags : SA_SHIRQ가 포함되어야 함.
◦ dev_id : 0이 아닌 값을 사용


dev_id값을 사용해 인터럽트를 공유하는 device들을 구
분한다.
Kernel2.6에서는 같은 인터럽트에 대해 여러 ISR이 동작
하는 것을 방지하기 위해 ISR의 return value를 확인하
여 처리.

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을 먼저 수행


특정 처리 구간에서 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)


Device driver가 사용하는 변수와 ISR이 사용하는 변수가 전역변수로 서
로 같으면 동기를 맞추기 위해 interrupt en/disable을 사용 – interrupt
disable period가 길거나 빠른 처리를 요구할 경우 interrupt 처리에 문
제가 생길 수 있음.
Kernal 2.6에서는 보다 가벼운 seqlock_t를 사용해서 변수간 동기를 맞
춘다.
제어용 변수 선언
seqlock_t the_lock =
S E Q LO C K_U N LO C K E D ;
unsigned int seq ;
데이터 변경 루틴
데이터 취득 루틴
w rite_seqlock(& the_lock);
do{ seq = read_seqbegin (& the_lock);
…
…
데이터 변경
데이터 처리
…
w rite_sequnlock(& the_lock);
…
} w hile read_seqretry(&the_lock,seq);

인터럽트와 난수 발생 처리
◦ 예측 불가능한 값 ( 난수 )가 필요한 경우 srand()함수를
사용
 /proc/sys/kernel/random/uuid을 매개 변수로 사용
 Kernel interrupt 함수와 random device driver에 의해 생
성
◦ 일반적인 Device driver도 난수 발생에 영향을 줄 수 있
음
 request_irq()에 SA_SAMPLE_RANDOM을 포함

인터럽트 발생 횟수 확인
◦ /proc/interrupt

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)
#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;
19
9
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;
20
0
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;
20
1
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;
}
return 0;
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");
20
2
#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 );
20
3
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);
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 );
sleep(1);
buff[0] = 0x00;
write(dev,buff,1 );
sleep(1);
}
close(dev);
return 0;
20
4

다중 프로세스와 시분할 처리

프로세스의 Sleep과 시스템의 효율성

프로세스가 잠든 상태

사건과 프로세스 스케줄링
206


프로세스가 하드웨어의 외부 입력상태 변화를 기다리기위
해 Sleep 하게 되는 것
Example: Serial App
int dev;
char buff[128];
int readsize;
dev = open(“/dev/ttyS0”, O_RDWR);
…
readsize = read(dev, buff, 16);
…
207
Interrupt Handler
read 호출
프로세스 #1
실제로 수행됨
처리
처리
할당된 시간
프로세스 #2 처리
처리
스케줄 전환
스케줄 전환
처리
스케줄 전환
스케줄 전환
208

__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_RUNNING
(task is not runnable)
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
209


헤더파일: 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)
210

블록킹 I/O mode로 열기

wait_queue_head_t 구조체
◦ open() flag 옵션으로 O_NDELAY를 주지 않으면 됨
◦ 프로세스를 관리하기 위한 대기큐 필요
◦ blocking 조건마다 각각 대기큐 필요
◦ 대기큐 변수 생성 방법
 wait_queue_head_t WaitQueue;
init_waitqueue_head(&WaitQueue);
 DECLARE_WAIT_QUEUE_HEAD(WaitQueue);
211

재우기
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)
212



헤더 파일
◦ 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;
213
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 );
214
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 );
sleep(1);
buff[0] = 0x00;
write(dev,buff,1 );
sleep(1);
}
close(dev);
return 0;
215
#define BLOCKIO_DEV_NAME
#define BLOCKIO_DEV_MAJOR
#define BLOCKIO_WRITE_ADDR
#define BLOCKIO_READ_ADDR
#define BLOCKIO_CTRL_ADDR
"blockiodev"
240
0x0378
0x0379
0x037A
#define BLOCKIO_IRQ
7
#define BLOCKIO_IRQ_ENABLE_MASK
#define BLOCKIO_BUFF_MAX
0x10
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 ); // 읽기에 대한 블럭 모드 구현을 위한 대기
큐 변수
216
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;
217
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 );
218
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;
}
219
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 );
}
22
0


입출력 다중화 개요
입출력 다중화 system call
◦ Select
◦ Poll


입출력 다중화 내부 구현 – driver –
예제 (프린터 포트)


특정 장치 및 파일과 같은 resource들에 대한 입출
력 처리를 event 중심으로 비 동기적으로 처리
관련 user system call
◦ Select
◦ Poll

암수 원형
#include <sys/select.h>
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *time-out);

입력 패러미터
◦
◦
◦
◦
◦

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 <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;
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 );
}
}
// 에러에 대한 검사를 수행한다.
for (i = 0; i < FD_SETSIZE; ++i)
{
if (FD_ISSET (i, &errorfds))
{
printf("장치 에러 발생.\n");
exit (EXIT_FAILURE);
}
}
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);
}
close( sfd1 );
close( sfd2 );
close( sfd3 );
}

암수 원형
#include <sys/poll.h>
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);

입력 패러미터
◦ ufds : 대상 fd와 관심있는 event 그리고 return된 event를 담은 자료형
struct pollfd {
};
int fd;
short events;
short revents;
/* file descriptor */
/* requested events */
/* returned events */
◦ nfds : 등록된 event 개수
◦ Timeout : 이벤트 발생이 안되어 poll함수 리턴 할 timeout (ms)

설명
◦ Select와 같이 특정 fd에 대해 등록된 event 발생 시 return하여 관련
event에 대한 처리를 할 수 있도록 한다.

Event 확인
◦ Poll() 리턴 시, 등록한 pollfd 자료형의 revents 필드를 event
macro상수 와 AND 연산으로 확인

Event macro
◦ POLLIN
 fd 로부터 데이터 읽기 가능한 상태
◦ POLLPRI (network 수신)
 fd로부터 긴급한 데이터 읽기 가능한 상태
◦ POLLOUT
 fd 로부터 데이터 쓰기 가능한 상태
◦ POLLERR
◦ POLLHUP
◦ POLLNVAL
 fd가 유효하지 않은 값이 된 상태
예) socket fd의 무효화 여
부
#include
#include
#include
#include
#include
#include
#include
#include
<stdio.h>
<string.h>
<fcntl.h>
<termios.h>
<sys/time.h>
<sys/types.h>
<unistd.h>
<sys/poll.h>
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;
}
int main( int argc, char **argv )
{
int sfd1, sfd2, sfd3;
struct pollfd Events[3];
int retval;
char buff[256];
int readcnt;
for (i = 0; i < 3; i++)
{
// 에러에 대한 검사를 수행한다.
if( Events[i].revents & POLLERR )
{
printf("장치 에러 발생.\n");
exit (EXIT_FAILURE);
}
sfd1 = open( "/dev/ttyS1", O_RDWR | O_NOCTTY );
sfd2 = open( "/dev/ttyS2", O_RDWR | O_NOCTTY );
sfd3 = open( "/dev/ttyS3", O_RDWR | O_NOCTTY );
:
각각의 시리얼 환경 설정 루틴들
// 수신된 데이타가 있는가를 검사한다.
if( Events[i].revents & POLLIN )
{
readcnt = read( i, buff, 256 );
write( i, buff, readcnt );
}
memset( Events, 0, sizeof( Events ) );
Events[0].fd
= sfd1;
Events[0].events = POLLIN
| POLLERR;
// 수신 이벤트
// 에러 이벤트
}
Events[1].fd
= sfd2;
Events[1].events = POLLIN
| POLLERR;
}
// 수신 이벤트
// 에러 이벤트
Events[2].fd
= sfd3;
Events[2].events = POLLIN
| POLLERR;
// 수신 이벤트
// 에러 이벤트
close( sfd1 );
close( sfd2 );
close( sfd3 );
}


드라이버의 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);
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;
int main()
{
int
dev;
struct pollfd Events[1];
int
retval;
char
int
int
int
buff[128];
readcnt;
loop;
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 );
231
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;
}
23
2
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;
}
23
3
#define POLL_DEV_NAME
#define POLL_DEV_MAJOR
#define POLL_WRITE_ADDR
#define POLL_READ_ADDR
#define POLL_CTRL_ADDR
"polldev"
240
0x0378
0x0379
0x037A
#define POLL_IRQ
7
#define POLL_IRQ_ENABLE_MASK
#define poll_BUFF_MAX
0x10
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;
234
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;
}
23
5
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;
23
6
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;
}
23
7
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;
}
return 0;
void cleanup_module(void)
{
unregister_chrdev( POLL_DEV_MAJOR, POLL_DEV_NAME );
}
23
8
23
9
01.
02.
03.
04.
태스크 큐와 워크 큐의 필요성
태스크 큐
워크 큐
선점형 커널
240

특정 I/O에 대한 지속적인 감시가 필요한 경우
◦ 디바이스 드라이버의 주기적인 I/O 감시 방법
 타이머 인터럽트 이용 => 여분의 타이머 인터럽트 필요,
overhead 큼
 커널 타이머 이용 => 정확한 주기로 동작, 1/HZ 이하의 주기에
선 사용 불가능
 태스크 큐와 워크 큐 이용 => 동작 시점 예측 힘듬, 커널 타이머
보다 자주 혹은 빠르게 호출될 가
능성이 있음, 워크큐의 경우 유연
한 처리 가능
 bottomhalf 이용(ver 2.4 이하) => 제한적인 방법
241

인터럽트 핸들러 함수에서의 신속한 처리가 힘든 경우
◦ Tophalf/Bottomhalf 모델에서 Bottomhalf로 쓰임
 ex) 이더넷 디바이스, 사운드 디바이스

인터럽트 서비스 함수(ISR)가 가지는 제약 이외의 처리를 해
야할 경우
◦ 인터럽트 서비스 함수(ISR)는 제약이 많음
 ISR내의 긴 지연은 시스템 전체 성능 저하를 유발함
 vmalloc이나 파일 입출력과 같이 sleep 상태를 유발할 수 있는 함
수는 사용이 금지됨
242

태스크 큐의 초기 설계 목표
◦ 인터럽트를 허용한 실행(Bottomhalf)
◦ 사용자 프로세스와 같은 태스크 특성(sleep 가능 등) 보유
(구현 실패)
 워크 큐로 대치됨
243

tq_struct struct
struct tq_struct {
struct list_head
unsigned long
void
void
list;
sync;
(*routine)(void *);
*data
}
◦ routine : 나중에 커널이 수행해야할 함수
◦ data : routine 함수가 사용할 data
244

태스크 큐 작업의 등록
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
245

태스크 큐 작업의 실행
◦ 다른 인터럽트를 허용함
◦ 느린 인터럽트 핸들러와 유사 => sleep 해선 안됨

태스크 큐 작업의 종료
◦ 실행된 태스크 큐는 태스크 큐 관리 list에서 자동 제거됨
◦ 태스크 큐 struct는 디바이스 드라이버 내에서 수동으로 제
거해야 함
246

태스크 큐의 특별한 사용법
◦ 전용의 태스크 큐 사용 가능
◦ void run_task_queue(task_queue *list);
 전용의 태스크 큐 작업 강제 실행
 tq_timer, tq_immediate 를 대상으로 호출하면 안됨
247

워크 큐의 특징
◦ 태스크 큐와 기본 설계 이념 동일
◦ 태스크 큐의 문제점 보완
◦ 우선 순위가 높은 전용의 kernel thread를 이용하여 태스크
특성 부여 (선점형 커널 도입으로 성능이 보장되었음)
◦ 2.6에서 태스크 큐를 대체함
248

work_struct struct
struct work_struct {
unsigned long
struct list_head
void
void
void
struct timer_list
pending;
entry;
(*func)(void *);
*data;
*wq_data;
timer;
}
◦ func : 수행할 함수
◦ data : func 함수가 사용할 data
249

스케줄 워크 큐
◦ 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과 같은 작업을 수행하는 함
수
250

스케줄 워크 큐
◦ void flush_scheduled_work(void);
 keventd가 소모하는 워크 큐의 작업들이 모두 처리될 수 있게 스케
줄링 전환을 수행하며 대기하는 함수
 디바이스 드라이버 제거시나 커널 종료시에만 호출되어야 함
◦ keventd thread
 -10의 우선 순위 값으로 시작함
 보통 땐 sleep함
 schedule_work(), schedule_delayed_work()함수에 의해
깨
어남
◦ 모듈의 라이센스가 GPL이 아니어도 사용 가능
251

디바이스 드라이버 고유의 워크 큐
◦ 다른 디바이스 드라이버가 스케줄 워크 큐를 통해 처리 시간이 긴 작
업을 수행해서 스케줄 워크 큐 사용이 힘들어 질 때 필요함
◦ 모듈의 라이센스가 GPL이나 BSD일 때 사용 가능
◦ struct workqueue_struct *
create_workqueue(const char * name);
 name을 이름으로 갖는 워크 큐 처리 thread와 워크 큐를 생성하는
함수
◦ void destroy_workqueue(struct workqueue_struct *queue);
 워크 큐에서 수행 중인 작업이나 대기 중인 작업의 완료를 기다린
후, 워크 큐를 안전하게 제거하고 스레드를 종료시키는 함수
252

디바이스 드라이버 고유의 워크 큐
◦ 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()과 유사
253

비선점형 커널
◦ 실행 흐름이 커널 안에 있을 경우, 다른 태스크로 선점되는 것이 불가
능
◦ 커널 코드 종료나 정지 시에만 다른 태스크로 스케줄링 가능

2.6 커널
◦ nested ISR에 schedule 함수 추가됨
◦ 커널 안에서 수행되는 태스크가 lock을 획득하고 있지 않다면 선점
가능(preempt_count로 관리)
254

커널 선점이 가능한 경우
◦
◦
◦
◦
인터럽트 핸들러로부터 커널 공간으로 리턴할 경우
커널 코드가 다시 선점 가능해질 경우
커널 안의 태스크가 명시적으로 schedule()을 호출하는 경우
커널 안의 태스크가 중단되는 경우
255
25
6

시스템 내부의 상황
◦
◦
◦
◦
◦
◦

인터럽트 할당
PCI 사용정보
프로세스 동작 상황
메모리 상태
커널 내부 상태 값 (실시간 시스템 정보)
etc .. (표 17-1 참조)
proc 디렉토리 마운트
◦ 부팅 스크립트에서 다음 명령을 실행
mount –t proc proc /proc






디바이스 드라이버를 디버깅 하거나 셀에서 디바이스 드라이버의 상
태를 쉽게 관찰하거나 제어할 때 유용
시스템 정보를 파일처럼 볼 수 있게 해주는 램 파일 시스템
커널과 디바이스 드라이버에서 제공하는 정보를 응용 프로그램에서
읽거나 데이터를 써 넣을 수 있다.
응용 프로그램이 /proc 디렉토리 하부에 디렉토리나 파일을 새로 만
들 수 없고, 삭제나 이름 변경 불가 (커널이나 디바이스 드라이버에
서만 가능)
디바이스 드라이버를 위해 반드시 proc 파일 시스템을 제공할 필요
없음 (기본 인터페이스가 구현 되어있어서 따로 proc 파일 시스템을
만들 필요는 없다.)
파일이 특성은 일반 파일과 같다 (open(), close(), read(), write()등
을 쓸 수 있다.) <-> 파일을 새로 만들 수 없고 데이터는 보존 되지
않는다.
• 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;
};

/proc 디렉토리에 하부 디렉토리를 만들때
struct proc_dir_entry *root_proc_dir = NULL;
root_proc_dir = proc_mkdir(“testdir”,0);

proc 파일 시스템 만들기
struct proc_dir_entry * root_proc_file = NULL;
root_proc_file = create_proc_entry( “testfile", S_IFREG | S_IRWXU,
root_proc_dir );

proc 파일 시스템 읽기
int read_proc_test( char *page, char **start, off_t off, int count,
int *eof, void *data_unused )
{
page에 써 넣는다.
*eof = 1; /* 스트림에 해당하는데 일반적인 경우가 아님 */
return 써 넣어진 테이터 수;
}

proc 파일 시스템 쓰기
int write_proc_test( struct file *file, const char *buffer, unsigned long count, void
*data)
{
사용자 공간의 메모리인 buffer의 내용을 커널 공간에 써 넣는다 .
return 처리된 데이터 수
}

proc 파일 시스템 삭제
remove_proc_entry( “testfile" , root_proc_dir );
remove_proc_entry( “testdir" , 0 );
struct
struct
struct
struct
proc_dir_entry
proc_dir_entry
proc_dir_entry
proc_dir_entry
*sumproc_root_fp
*sumproc_val1_fp
*sumproc_val2_fp
*sumproc_result_fp
=
=
=
=
NULL;
NULL;
NULL;
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;
}
26
2
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
b
sum
buf
= simple_strtoul( sumproc_str1, NULL, 10 );
= simple_strtoul( sumproc_str2, NULL, 10 );
= a + b;
+= sprintf( buf, "Result [%d + %d = %d]\n", a,b, sum );
*eof = 1;
return buf - page;
}
26
3
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;
}
26
4
void cleanup_module(void)
{
remove_proc_entry( "result"
remove_proc_entry( "val2"
remove_proc_entry( "val1"
remove_proc_entry( "sumproc"
}
,
,
,
,
sumproc_root_fp );
sumproc_root_fp );
sumproc_root_fp );
0 );
26
5
26
6
267





MMUless system의 메모리 공
간을 플랫 메모리 공간이라 함
프로세스 별로 주소 공간이 나
눠져야 함
컴파일시 프로세스가 수행될
위치를 미리 지정하거나 상대
적인 주소로 프로세스가 컴파
일 되어야 함
H/W적인 메모리 보호 장치 없
음
메모리 단편화 문제 발생
26
8





플랫 메모리 공간의 문제점 해결
다중 프로세스 지원
메모리 공간 보호
물리적인 메모리 제약에서 벗어나 프로그래밍 가능
하드디스크와 같은 보조 기억 장치를 메모리처럼 사용할 수 있게 함(swap
device)
269


모든 가상 주소와 물리 주소를 1대1로 매핑
=> 메모리 소모가 큼
=> 페이지 단위로 처리함 (리눅스에선 보통 4KB)
페이지 테이블은 주소 변환 정보 뿐만 아니라, 메모리 접근 속성도 관
리함
=> 허가되지 않은 접근
=> exception 발생(H/W)
=> exception 처리(커널)
270
271

프로세스마다 독자적인 메모리 공간을 보유함
◦ 단, 커널 주소 공간은 공유함


1개의 커널 MMU 테이블과 다수의 프로세스 MMU 테이블
존재
mmap 등의 API를 이용하여 I/O device의 물리 주소를 프
로세스 주소 공간으로 동적 매핑 가능
272
273

3단계 페이징 모델
◦ 실제로 보통 2단계만 사용됨



PGD : Page Directory
PMD : Page Mid level Directory
PTE : Page Table Entry
274


디바이스 드라이버에서 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 영역같은 noncachable 영역에 사용

void iounmap (void *addr);
◦ 매핑 해제 함수
275



리눅스 커널은 부팅 단계에서 시스템을 제어하기 위한 모든 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등과 관련된 주소 공간에 사용(호환성을 위해 권장함)
276

mmap
◦ 대용량의 데이터를 처리하는 하드웨어 디바이스 드라이버를 작성할
때 효율적임
◦ 파일이나 디바이스 드라이버가 제공하는 물리 주소 영역을 프로세스
가 직접 사용할 수 있게 해줌
 메모리 복사 없이 access 가능 => 효율적임
277
278
279

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); => 매핑 해제 함수
280

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 : 스왑 아웃되지 않도록 예약
281
 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;
282
 vma->vm_flags |= (VM_IO | VM_RESERVED)의 의미
 VM_IO : 매핑된 물리 주소 공간이 I/O 영역임을 표시,
해당 영역 접근 실패 시, 프로세서가 코어 덤프하며 죽
는
것을 방지한다
 VM_RESERVED : 스왑 아웃 대상에서 제외 시킨다
283
284
28
5
수행중 커널에 모듈 추가 -> 링크 과정(심볼릭에 주소 부여)
-> 심볼릭 테이블 등록 -> 외부 참조 가능

커널 프로그램
외부참조심볼선언
5
1
모듈코드
외부참조심볼선언
심볼
심볼 테이블
심볼 주소
3
2
/proc/ksyms
4
286



EXPORT_SYMBOL(심볼명) : 2.4, 2.6
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);
287
-
커널내에 심볼이 위치한 주소
심볼의 타입정보 : 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]
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).
288
int func_var1 = 0;
int func_var2 = 0;
int func_sum ( int var3
{
printk( "func_var1
printk( "func_var2
printk( "var3
return func_var1 +
}
)
= %d\n", func_var1 );
= %d\n", func_var2 );
= %d\n", var3 );
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);
28
9
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)
{
}
29
0

similar documents