Excuse the ads! We need some help to keep our site up.
List
Character Device Drivers
- Character Device 드라이버는 버퍼 캐시(Buffer cache)를 사용하지 않고 데이터를 한번에 하나의 문자를 읽고 쓰는 드라이버입니다.
- 예) Keyboad, Sound card, Printer 드라이버
- 이외에도 Block Device, Network Device 드라이버가 있습니다.
- 블록 디바이스 드라이버는 버퍼 캐시를 통한 임의 접근과 블록단위로 입출력이 가능합니다.
- 예) 하드디스크
- 네트워크 디바이스 드라이버는 네트워크 스택과 네트워크 하드웨어 사이에 위치해 데이터의 송수신을 담당합니다.
- 예) Ethernet, Network interface card,
- 블록 디바이스 드라이버는 버퍼 캐시를 통한 임의 접근과 블록단위로 입출력이 가능합니다.
struct file_operations
- file_operations 구조체는 Character Device, Block Device 드라이버와 일반 프로그램간의 통신을 위해 제공되는 인터페이스입니다.
- read, write, open, release, unlocked_ioctl, 등의 함수 포인터를 사용할 수 있습니다.
- Network Device는 file_operations 구조체를 사용하지 않습니다.
- "include/linux/netdevice.h"파일의 "net_device" 구조체를 사용합니다.
struct file_operations
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*setfl)(struct file *, unsigned long);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, u64);
} __randomize_layout;
- 예를 들어 다음과 같은 방법으로 디바이스 모듈에서 open()함수를 제공할 수 있습니다.
- file operations 구조체에 작성된 함수 포인터의 인자값을 이용하여 open 함수의 코드를 작성합니다.
- 해당 함수명은 "chardev_open" 이며, 해당 함수가 호출되면 "chardev_open"가 커널 메시지로 출력됩니다.
작성한 함수의 명은 "struct file_operations" 형태로 선언된 구조체의 ".open" 필드에 함수의 주소(chardev_open)를 저장합니다.
- file operations 구조체에 작성된 함수 포인터의 인자값을 이용하여 open 함수의 코드를 작성합니다.
chardev.c
static int chardev_open(struct inode *inode, struct file *file)
{
printk("chardev_open");
return 0;
}
struct file_operations chardev_fops = {
.open = chardev_open,
};
Example [struct file_operations - .open]
OS Information
Ubuntu 18.10
lazenca0x0@ubuntu:~$ uname -a Linux ubuntu 4.18.0-11-generic #12-Ubuntu SMP Tue Oct 23 19:22:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux lazenca0x0@ubuntu:~$ gcc --version gcc (Ubuntu 8.2.0-7ubuntu1) 8.2.0 Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. lazenca0x0@ubuntu:~$
Source code
- 해당 코드는 다음과 같은 동작은 합니다.
- 디바이스 모듈이 커널에 등록될 때 chardev_init() 함수가 동작합니다.
- 해당 함수에서는 register_chrdev() 함수에 의해 해당 문자 디바이스의 메이저 번호를 등록합니다.
User space에서 디바이스가 open할때 chardev_open()함수가 호출됩니다.
- 디바이스 모듈이 커널에서 제거될 때 chardev_exit() 함수가 동작합니다.
- 해당 함수에서는 unregister_chrdev() 함수에 의해 해당 문자 디바이스의 메이저 번호를 제거됩니다.
- 디바이스 모듈이 커널에 등록될 때 chardev_init() 함수가 동작합니다.
chardev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "chardev"
#define DEVICE_FILE_NAME "chardev"
#define MAJOR_NUM 100
static int chardev_open(struct inode *inode, struct file *file)
{
printk("chardev_open");
return 0;
}
struct file_operations chardev_fops = {
.open = chardev_open,
};
static int chardev_init(void)
{
int ret_val;
ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &chardev_fops);
if (ret_val < 0) {
printk(KERN_ALERT "%s failed with %d\n",
"Sorry, registering the character device ", ret_val);
return ret_val;
}
printk(KERN_INFO "%s The major device number is %d.\n",
"Registeration is a success", MAJOR_NUM);
printk(KERN_INFO "If you want to talk to the device driver,\n");
printk(KERN_INFO "you'll have to create a device file. \n");
printk(KERN_INFO "We suggest you use:\n");
printk(KERN_INFO "mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
printk(KERN_INFO "The device file name is important, because\n");
printk(KERN_INFO "the ioctl program assumes that's the\n");
printk(KERN_INFO "file you'll use.\n");
return 0;
}
static void chardev_exit(void)
{
unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
}
module_init(chardev_init);
module_exit(chardev_exit);
Makefile
obj-m := chardev.o all: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
Build & Run
- 다음과 같이 코드를 빌드합니다.
Build
lazenca0x0@ubuntu:~/Kernel/Module/chardev$ make make -C /lib/modules/4.18.0-11-generic/build M=/home/lazenca0x0/Kernel/Module/chardev modules make[1]: Entering directory '/usr/src/linux-headers-4.18.0-11-generic' Makefile:982: "Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel" CC [M] /home/lazenca0x0/Kernel/Module/chardev/chardev.o Building modules, stage 2. MODPOST 1 modules WARNING: modpost: missing MODULE_LICENSE() in /home/lazenca0x0/Kernel/Module/chardev/chardev.o see include/linux/module.h for more information CC /home/lazenca0x0/Kernel/Module/chardev/chardev.mod.o LD [M] /home/lazenca0x0/Kernel/Module/chardev/chardev.ko make[1]: Leaving directory '/usr/src/linux-headers-4.18.0-11-generic'
- 다음과 같이 빌드된 Kernel module을 Kernel에 적재합니다.
- mknod 명령어를 이용하여 적재된 Module을 디바이스 파일로 생성합니다.
- chmod 명령어를 이용하여 일반 유저들이 읽고 쓸수 있도록 설정합니다.
- chardev_open() 함수의 동작을 확인하기 위해 echo 명령어를 이용하여 디바이스 파일을 열어서 'A'를 저장하도록합니다.
- 해당 디바이스 파일에 'A' 문자가 저장되지 않습니다.
- dmesg 명령어를 이용하여 "chardev_open" 메시지를 확인할 수 있습니다.
- 즉, echo 명령어에 의해 chardev_open() 함수가 동작되었다는 것을 알수 있습니다.
Run
lazenca0x0@ubuntu:~/Kernel/Module/chardev$ sudo insmod chardev.ko lazenca0x0@ubuntu:~/Kernel/Module/chardev$ dmesg |tail [14735.412269] chardev_open [14886.237136] chardev_open [15136.839556] Registeration is a success The major device number is 100. [15136.839557] If you want to talk to the device driver, [15136.839558] you'll have to create a device file. [15136.839582] We suggest you use: [15136.839583] mknod chardev c 100 0 [15136.839583] The device file name is important, because [15136.839584] the ioctl program assumes that's the [15136.839584] file you'll use. lazenca0x0@ubuntu:~/Kernel/Module/chardev$ sudo mknod chardev c 100 0 lazenca0x0@ubuntu:~/Kernel/Module/chardev$ sudo chmod 666 chardev lazenca0x0@ubuntu:~/Kernel/Module/chardev$ echo 'A' > chardev -bash: echo: write error: Invalid argument lazenca0x0@ubuntu:~/Kernel/Module/chardev$ dmesg |tail [14886.237136] chardev_open [15136.839556] Registeration is a success The major device number is 100. [15136.839557] If you want to talk to the device driver, [15136.839558] you'll have to create a device file. [15136.839582] We suggest you use: [15136.839583] mknod chardev c 100 0 [15136.839583] The device file name is important, because [15136.839584] the ioctl program assumes that's the [15136.839584] file you'll use. [15231.493826] chardev_open lazenca0x0@ubuntu:~/Kernel/Module/chardev$
Module 관련 명령어
| Command | |
|---|---|
| insmod | 생성된 모듈을 Kernel의 symbol table을 통해 Kernel에 링크하는 명령어 |
| rmmod | Kernel에 등록된 모듈을 제거하는 명령어 |
| lsmod | Kernel에 등록된 모듈 목록을 출력하는 명령어 |
| modinfo | 모듈에 상세한 정보를 출력하는 명령어 |
mknod 명령어
| 인자 | mknod <디바이스 파일명> <디바이스 파일 형식> <Major number> <Minor number> |
|---|---|
| 디바이스 파일 형식 |
|
| Major Number & Minor Number |
|
Example [struct file_operations .open, .release, .read, .write]
Source code
chardev_init()
해당 모듈이 커널에 등록될 때 chardev_init() 함수에서 다음 기능을 처리합니다.
- alloc_chrdev_region() 함수를 이용하여 Character Device의 번호를 시스템에 등록합니다.
- major(),mkdev()함수를 이용하여 디바이스에서 사용할 Major, Minor 번호를 취득합니다.
- cdev_init() 함수를 이용하여 chardev_cdev 구조체를 초기화합니다.
- cdev_add() 함수를 이용하여 Character Device를 시스템에 추가합니다.
- class_create() 함수를 이용하여 시스템에 생성할 디바이스의 클래스를 생성합니다.
- device_create() 함수를 이용하여 시스템에 디바이스를 생성합니다.
chardev_exit()
- 해당 모듈이 커널에서 제거될 때 chardev_exit() 함수에서 다음 기능을 처리합니다.
- device_destroy() 함수를 이용하여 device_create() 함수에 의해 생성된 디바이스를 제거합니다.
class_destroy() 함수를 이용하여 class_create() 함수에 의해 생성된 디바이스 클래스를 소멸시킵니다.
- cdev_del() 함수를 이용하여 cdev_add() 함수에 의해 추가된 Character Device를 제거합니다.
unregister_chrdev_region() 함수를 이용하여 alloc_chrdev_region() 함수에 의해 등록된 장치 번호를 반환합니다.
- device_destroy() 함수를 이용하여 device_create() 함수에 의해 생성된 디바이스를 제거합니다.
chardev_open()
- 사용자 공간(User space)에서 해당 디바이스를 열 때마다 chardev_open() 함수가 호출되며 다음 기능을 처리합니다.
- kmalloc() 함수를 이용하여 Kernel heap 영역에 data 구조체의 크기만큼의 공간을 할당받습니다.
- strlcpy() 함수를 이용하여 str변수에 저장된 값을 p→buffer 영역에 복사합니다.
chardev_release()
- 사용자 공간(User space)에서 해당 디바이스를 닫을 때 chardev_release() 함수가 호출되며 다음 기능을 처리합니다.
- kfree() 함수를 이용하여 할당받은 Heap영역을 해제합니다.
- kfree() 함수를 이용하여 할당받은 Heap영역을 해제합니다.
chardev_write()
- 사용자 공간(User space)에서 해당 디바이스로 데이터가 전송 될 때 chardev_write() 함수가 호출되며 다음 기능을 처리합니다.
copy_from_user() 함수를 이용하여 사용자 공간으로 부터 전달 받은 데이터(buf)를 "p→buffer" 변수에 복사합니다.
chardev_read()
- 사용자 공간(User space)에서 해당 디바이스로 부터 데이터를 받을때 chardev_read() 함수가 호출되며 다음 기능을 처리합니다.
- copy_to_user() 함수를 이용하여 커널 영역에 저장된 "p→buffer" 데이터를 "buf" 변수에 복사합니다.
chardev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <linux/uaccess.h>
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "chardev"
#define BUFFER_SIZE 256
static const unsigned int MINOR_BASE = 0;
static const unsigned int MINOR_NUM = 2;
static unsigned int chardev_major;
static struct cdev chardev_cdev;
static struct class *chardev_class = NULL;
static int chardev_open(struct inode *, struct file *);
static int chardev_release(struct inode *, struct file *);
static ssize_t chardev_read(struct file *, char *, size_t, loff_t *);
static ssize_t chardev_write(struct file *, const char *, size_t, loff_t *);
struct file_operations chardev_fops = {
.open = chardev_open,
.release = chardev_release,
.read = chardev_read,
.write = chardev_write,
};
struct data {
unsigned char buffer[BUFFER_SIZE];
};
static int chardev_init(void)
{
int alloc_ret = 0;
int cdev_err = 0;
int minor;
dev_t dev;
printk("The chardev_init() function has been called.");
alloc_ret = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DRIVER_NAME);
if (alloc_ret != 0) {
printk(KERN_ERR "alloc_chrdev_region = %d\n", alloc_ret);
return -1;
}
//Get the major number value in dev.
chardev_major = MAJOR(dev);
dev = MKDEV(chardev_major, MINOR_BASE);
//initialize a cdev structure
cdev_init(&chardev_cdev, &chardev_fops);
chardev_cdev.owner = THIS_MODULE;
//add a char device to the system
cdev_err = cdev_add(&chardev_cdev, dev, MINOR_NUM);
if (cdev_err != 0) {
printk(KERN_ERR "cdev_add = %d\n", alloc_ret);
unregister_chrdev_region(dev, MINOR_NUM);
return -1;
}
chardev_class = class_create(THIS_MODULE, "chardev");
if (IS_ERR(chardev_class)) {
printk(KERN_ERR "class_create\n");
cdev_del(&chardev_cdev);
unregister_chrdev_region(dev, MINOR_NUM);
return -1;
}
for (minor = MINOR_BASE; minor < MINOR_BASE + MINOR_NUM; minor++) {
device_create(chardev_class, NULL, MKDEV(chardev_major, minor), NULL, "chardev%d", minor);
}
return 0;
}
static void chardev_exit(void)
{
int minor;
dev_t dev = MKDEV(chardev_major, MINOR_BASE);
printk("The chardev_exit() function has been called.");
for (minor = MINOR_BASE; minor < MINOR_BASE + MINOR_NUM; minor++) {
device_destroy(chardev_class, MKDEV(chardev_major, minor));
}
class_destroy(chardev_class);
cdev_del(&chardev_cdev);
unregister_chrdev_region(dev, MINOR_NUM);
}
static int chardev_open(struct inode *inode, struct file *file)
{
char *str = "helloworld";
int ret;
struct data *p = kmalloc(sizeof(struct data), GFP_KERNEL);
printk("The chardev_open() function has been called.");
if (p == NULL) {
printk(KERN_ERR "kmalloc - Null");
return -ENOMEM;
}
ret = strlcpy(p->buffer, str, sizeof(p->buffer));
if(ret > strlen(str)){
printk(KERN_ERR "strlcpy - too long (%d)",ret);
}
file->private_data = p;
return 0;
}
static int chardev_release(struct inode *inode, struct file *file)
{
printk("The chardev_release() function has been called.");
if (file->private_data) {
kfree(file->private_data);
file->private_data = NULL;
}
return 0;
}
static ssize_t chardev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
struct data *p = filp->private_data;
printk("The chardev_write() function has been called.");
printk("Before calling the copy_from_user() function : %p, %s",p->buffer,p->buffer);
if (copy_from_user(p->buffer, buf, count) != 0) {
return -EFAULT;
}
printk("After calling the copy_from_user() function : %p, %s",p->buffer,p->buffer);
return count;
}
static ssize_t chardev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct data *p = filp->private_data;
printk("The chardev_read() function has been called.");
if(count > BUFFER_SIZE){
count = BUFFER_SIZE;
}
if (copy_to_user(buf, p->buffer, count) != 0) {
return -EFAULT;
}
return count;
}
module_init(chardev_init);
module_exit(chardev_exit);
Makefile
obj-m := chardev.o all: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
Test program
Source code
test.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define TEXT_LEN 12
int main()
{
static char buff[256];
int fd;
if ((fd = open("/dev/chardev0", O_RDWR)) < 0){
printf("Cannot open /dev/chardev0. Try again later.\n");
}
if (write(fd, "lazenca0x0", TEXT_LEN) < 0){
printf("Cannot write there.\n");
}
if (read(fd, buff, TEXT_LEN) < 0){
printf("An error occurred in the read.\n");
}else{
printf("%s\n", buff);
}
if (close(fd) != 0){
printf("Cannot close.\n");
}
return 0;
}
Build & Run
- 다음과 같이 모듈을 빌드합니다.
make
lazenca0x0@ubuntu:~/Kernel/Module/WR$ make make -C /lib/modules/4.18.0-11-generic/build M=/home/lazenca0x0/Kernel/Module/WR modules make[1]: Entering directory '/usr/src/linux-headers-4.18.0-11-generic' Makefile:982: "Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel" CC [M] /home/lazenca0x0/Kernel/Module/WR/chardev.o Building modules, stage 2. MODPOST 1 modules CC /home/lazenca0x0/Kernel/Module/WR/chardev.mod.o LD [M] /home/lazenca0x0/Kernel/Module/WR/chardev.ko make[1]: Leaving directory '/usr/src/linux-headers-4.18.0-11-generic' lazenca0x0@ubuntu:~/Kernel/Module/WR$
- 모듈을 커널에 등록하기 전에 "/etc/udev/rules.d/" 경로에 모듈 등록시 자동으로 생성되는 디바이스 파일의 규칙을 저장합니다.
- insmod 명령어를 이용하여 모듈을 커널에 등록하면 "/dev/" 경로에 자동으로 chardev0, chardev1 두개의 디바이스가 생성됩니다.
- 해당 디바이스는 접근권한은 666이기 때문에 일반 유저들도 해당 디바이스를 사용할 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Module/WR$ echo 'KERNEL == "chardev[0-9]*",GROUP="root",MODE="0666"' >> /etc/udev/rules.d/80-chardev.rules lazenca0x0@ubuntu:~/Kernel/Module/WR$ sudo insmod chardev.ko [sudo] password for lazenca0x0: lazenca0x0@ubuntu:~/Kernel/Module/WR$ ls -al /dev/chardev* crw-rw-rw- 1 root root 240, 0 Nov 27 22:39 /dev/chardev0 crw-rw-rw- 1 root root 240, 1 Nov 27 22:39 /dev/chardev1 lazenca0x0@ubuntu:~/Kernel/Module/WR$
- 테스트 프로그램을 빌드하고 실행하면 다음과 같은 내용을 확인 할 수 있습니다.
- 테스트 프로그램이 정상적으로 동작하는 것을 확인할 수 있습니다.
- "dmesg" 명령어를 이용하여 모듈에서 출력된 메시지를 이용하여 모듈에서 작성한 모든 함수들이 정상적으로 동작하는 것을 확인 할 수 있습니다.
- 그리고 여기서 중요한것은 write()함수에 의해 커널의 Heap 영역에 데이터를 저장하고, read() 함수를 이용하여 해당 데이터를 사용자 공간에서 출력되었다는 것입니다.
lazenca0x0@ubuntu:~/Kernel/Module/WR$ gcc -o test test.c lazenca0x0@ubuntu:~/Kernel/Module/WR$ ./test lazenca0x0 lazenca0x0@ubuntu:~/Kernel/Module/WR$ dmesg |tail [ 10.899284] random: 7 urandom warning(s) missed due to ratelimiting [ 11.942688] [drm:vmw_stdu_crtc_page_flip [vmwgfx]] *ERROR* Page flip error -16. [ 130.546522] chardev: loading out-of-tree module taints kernel. [ 130.546570] chardev: module verification failed: signature and/or required key missing - tainting kernel [ 130.547051] The chardev_init() function has been called. [ 133.948317] The chardev_open() function has been called. [ 133.948320] The chardev_write() function has been called. [ 133.948322] Before calling the copy_from_user() function : 0000000012f53a81, helloworld [ 133.948323] After calling the copy_from_user() function : 0000000012f53a81, lazenca0x0 [ 133.948324] The chardev_read() function has been called. lazenca0x0@ubuntu:~/Kernel/Module/WR$
- 다음과 같은 형태로도 동작가능합니다.
- 동일한 디바이스를 중복으로 열수 있으며, 동일한 모듈에 2개의 디바이스를 생성할 수도 있습니다.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main()
{
static char buff[256];
int fd0_A, fd0_B, fd1_A;
if ((fd0_A = open("/dev/mydevice0", O_RDWR)) < 0) perror("open");
if ((fd0_B = open("/dev/mydevice0", O_RDWR)) < 0) perror("open");
if ((fd1_A = open("/dev/mydevice1", O_RDWR)) < 0) perror("open");
if (write(fd0_A, "0_A", 4) < 0) perror("write");
if (write(fd0_B, "0_B", 4) < 0) perror("write");
if (write(fd1_A, "1_A", 4) < 0) perror("write");
if (read(fd0_A, buff, 4) < 0) perror("read");
printf("%s\n", buff);
if (read(fd0_B, buff, 4) < 0) perror("read");
printf("%s\n", buff);
if (read(fd1_A, buff, 4) < 0) perror("read");
printf("%s\n", buff);
if (close(fd0_A) != 0) perror("close");
if (close(fd0_B) != 0) perror("close");
if (close(fd1_A) != 0) perror("close");
return 0;
}
- 이 테스트 프로그램을 실행하면 다음과 같은 내용을 확인 할 수 있습니다.
- 테스트 프로그램에서 전달된 값을 정상적으로 출력합니다.
- 동일한 모듈 또는 동일한 디바이스이지만 전달된 문자열을 저장하기 위해 사용되는 Heap 영역이 다르다는 것을 알 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Module/WR$ ./test1 0_A 0_B 1_A lazenca0x0@ubuntu:~/Kernel/Module/WR$ [ 1837.876449] The chardev_open() function has been called. [ 1837.876452] The chardev_open() function has been called. [ 1837.876484] The chardev_open() function has been called. [ 1837.876486] The chardev_write() function has been called. [ 1837.876488] Before calling the copy_from_user() function : 000000000affca99, helloworld [ 1837.876489] After calling the copy_from_user() function : 000000000affca99, 0_A [ 1837.876489] The chardev_write() function has been called. [ 1837.876490] Before calling the copy_from_user() function : 00000000d881b9fa, helloworld [ 1837.876491] After calling the copy_from_user() function : 00000000d881b9fa, 0_B [ 1837.876491] The chardev_write() function has been called. [ 1837.876492] Before calling the copy_from_user() function : 00000000ecd9f924, helloworld [ 1837.876492] After calling the copy_from_user() function : 00000000ecd9f924, 1_A [ 1837.876493] The chardev_read() function has been called. [ 1837.876559] The chardev_read() function has been called. [ 1837.876561] The chardev_read() function has been called. [ 1837.876563] The chardev_release() function has been called. [ 1837.876564] The chardev_release() function has been called. lazenca0x0@ubuntu:~/Kernel/Module/WR$
References
- https://www.tldp.org/LDP/lkmpg/2.4/html/x579.html
- http://forum.falinux.com/zbxe/index.php?document_srl=406234&mid=device_driver
- http://dev-ahn.tistory.com/100
- http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/
- https://qiita.com/take-iwiw/items/26d5f7f4894ccc4ce227
- https://www.joinc.co.kr/w/man/1/mknod