#include헤더 파일을 보니 이전의 모듈과는 달리 커널의 기능을 더 많이 필요로 한다는 걸 알 수 있습니다. fs.h 파일은 파일 연산 관련 구조체에 대한 정의를 담고 있는 파일로써, 이 파일 연상 구조체에 내용을 저장한 후에 나중에 /dev 파일을 만들때 지정해주어야 합니다. miscdevice.h 파일은 기타 장치 파일을 등록하는데 필요한 함수를 포함하고 있습니다. asm/uaccess.h 파일은 유저 스페이스 메모리에 쓰거나 읽을때 해당 연산이 가능한지 아닌지를 테스트할때 사용하는 함수들을 포함하고 있습니다.#include #include #include #include
static ssize_t hello_read(struct file * file, char * buf, size_t count, loff_t *ppos) { char *hello_str = "Hello, world!n"; int len = strlen(hello_str); /* 비어있는 문자열인지 확인하기 위해서 길이를 계산한다. */ /* * 전체 스트링을 한 번에 읽어들이는 연산만 허용한다. */ if (count < len) return -EINVAL; /* * position 값이 0이 아니면, * 더 이상 읽을 데이터가 없는 것으로 간주한다. */ if (*ppos != 0) return 0; /* * 문자열을 유저 버퍼에 쓰는 것 외에 * 해당 유저가 버퍼에 쓸 수 있는 권한이 있는지도 확인한다. * */ if (copy_to_user(buf, hello_str, len)) return -EINVAL; /* * 진행상황을 유저에게 알려준다. */ *ppos = len; return len; }이제 각 파일 연산이 들어왔을때 어떤 일을 해야하는지를 지정하는 구조체를 만들어 보겠습니다. 여기서는 읽기 연산만 구현해 볼 것입니다.
static const struct file_operations hello_fops = { .owner = THIS_MODULE, .read = hello_read, };이제 장치를 커널에 등록하는데 필요한 정보를 담고 있는 구조체를 만들어 보겠습니다.
static struct miscdevice hello_dev = { /* * MINOR 번호를 직접 지정하지 않고 * 커널이 선택하도록 한다. */ MISC_DYNAMIC_MINOR, /* * 장치의 이름을 hello로 한다. */ "hello", /* * 파일 연산을 할 때 호출되게 될 * 함수를 정의 */ &hello_fops };언제나 마찬가지로 모듈의 초기화 함수에서 장치를 등록하겠습니다.
static int __init hello_init(void) { int ret; /* * sys/class/misc 디렉토리에 /dev/hello 장치를 만든다. * udev가 자동적으로 정해진 규칙에 따라서 /dev/hello 장치를 * 만들게 된다. */ ret = misc_register(&hello_dev); if (ret) printk(KERN_ERR "Unable to register "Hello, world!" misc devicen"); return ret; } module_init(hello_init);그리고 exit 함수에서 장치 등록을 해제하는 것도 잊으면 안됩니다.
static void __exit hello_exit(void) { misc_deregister(&hello_dev); } module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Valerie Henson이제 컴파일 하고 모듈을 로딩해 보겠습니다."); MODULE_DESCRIPTION(""Hello, world!" minimal module"); MODULE_VERSION("dev");
$ cd hello_dev $ make $ sudo insmod ./hello_dev.ko이제 /dev/hello 라는 장치가 생성되었고, root 유저로 파일을 읽었을때 "Hello, world!"라는 메시지가 출력되게 됩니다.
$ sudo cat /dev/hello Hello, world!하지만 일반 유저로는 이 장치를 읽을 수가 없습니다.
$ cat /dev/hello cat: /dev/hello: Permission denied $ ls -l /dev/hello crw-rw---- 1 root root 10, 61 2007-06-20 14:31 /dev/hello이건 udev의 기본 규칙때문에 일반 유저는 엑세스 할 수가 없게 된 것입니다. 원래 udev는 장치가 생성되면 /dev/<장치 이름>의 파일을 /dev 디렉토리에 만들고 0660의 권한(주인과 그룹은 읽고 쓰기 가능, 그 외에는 접근 불가능)을 주도록 되어 있기때문입니다. 우리가 일반 유저도 읽을 수 있게 만들고 싶기 때문에 다음과 같이 udev의 규칙을 변경해 보도록 하겠습니다.
KERNEL=="hello", SYMLINK+="hello_world", MODE="0444"위의 규칙을 하나씩 살펴보겠습니다.
$ ls -d /sys/class/misc/hello/ /sys/class/misc/hello/SYMLINK+="hello_world"는 장치가 생성될 때 생성되어야 하는 심볼릭 링크의 리스트를 가지고 있는 곳에 "hello_world"를 추가하는 명령어 입니다. 물론 이번 모듈에서는 심볼릭 링크를 하나만 만들기 때문에 += 연산자 대신에 = 연산자를 쓸 수도 있지만, 나중에 복잡한 장치를 만들때는 여러개의 심볼릭 링크를 만들 수도 있기때문에 연습삼아서 +=로 해보도록 하겠습니다.
$ sudo cp hello.rules /etc/udev/ $ sudo ln -s ../hello.rules /etc/udev/rules.d/010_hello.rules그럼 이제 hello world 드라이버를 다시 로딩하고 새로 만들어지는 /dev 파일을 보겠습니다.
$ sudo rmmod hello_dev $ sudo insmod ./hello_dev.ko $ ls -l /dev/hello* cr--r--r-- 1 root root 10, 61 2007-06-19 21:21 /dev/hello lrwxrwxrwx 1 root root 5 2007-06-19 21:21 /dev/hello_world -> hello/dev/hello_world 가 생성된 걸 확인할 수 있습니다. 일반 유저로 장치에서 읽을 수 있는지를 확인해 봅시다.
$ cat /dev/hello_world Hello, world! $ cat /dev/hello Hello, world!udev 규칙에 관한 더 자세한 내용은 Daniel Drake님이 쓴 Writing udev rules 를 참고해 주시면 좋겠습니다.
이전 글 : 리눅스 디바이스 드라이버 심플 소개(1)
다음 글 : 고급 자바스크립트 활용Ⅰ-Ⅰ
최신 콘텐츠