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)를 저장합니다.

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.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에 링크하는 명령어
rmmodKernel에 등록된 모듈을 제거하는 명령어
lsmodKernel에 등록된 모듈 목록을 출력하는 명령어
modinfo모듈에 상세한 정보를 출력하는 명령어

mknod 명령어

인자mknod <디바이스 파일명> <디바이스 파일 형식> <Major number> <Minor number>
디바이스 파일 형식
  • p : FIFO(:12)
  • b : 블럭 장치 파일 (block device file)
  • c, u : 문자 파일 (character special file), unbuffered special file
Major Number & Minor Number
  • MAJOR는 블럭장치 혹은 문자장치의 그룹에 할당되는 번호입니다.
  • MINOR 번호는 MAJOR로 묶여진 문자장치의 그룹중 하나에 할당되는 번호다.
  • 이 두개의 번호를 이용해서 장치를 식별할 수 있습니다.

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() 함수에 의해 등록된 장치 번호를 반환합니다.

chardev_open()

  • 사용자 공간(User space)에서 해당 디바이스를 열 때마다 chardev_open() 함수가 호출되며 다음 기능을 처리합니다.
    • kmalloc() 함수를 이용하여 Kernel heap 영역에 data 구조체의 크기만큼의 공간을 할당받습니다.
    • strlcpy() 함수를 이용하여 str변수에 저장된 값을 p→buffer 영역에 복사합니다.

chardev_release()

  • 사용자 공간(User space)에서 해당 디바이스를 닫을 때 chardev_release() 함수가 호출되며 다음 기능을 처리합니다.
    • 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