ioctl
: short for Input Output Control
ioctl(fd, cmd, argument) : 3 개의 파라미터
1. File descriptor
2. cmd : 구분자
3. argument : 추가 정보
CMD 구성
: 약속된 cmd 변수의 비트 단위 Format
하지만, 비트 연산하기 번거로워 Kernel 이 제공하는 Macro 함수
를 사용하자!
Macro 함수
: _IO(type, number)
=> type : 매직넘버 , number : 구분번호
ex) ioctl(fd, 3, 0) => ioctl(fd, _IO(3,0), 0)
커널 로그에 나타나는 메세지 색상을 바꿔줄 수 있다.
app에서 데이터 전달
char buf[10] = "Hi Jaehoon";
ioctl(fd, _IO(0, 3), buf);
커널에서 데이터 수신
char *buf = (char *)arg;
printfk (KERN_INFO, "%s\n", buf);
커널 스페이스에서 유저 스페이스의 주소값을 건드리기 때문에 위험하다. 커널 패닉(=블루 스크린) 이 발생할 위험이 있다.
따라서, 보내오는 데이터를 복사 하여, 주소를 건드리지 않는 방법으로 사용해야한다.
copy_to_user
와 copy_from_user
을 사용한다.
app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main()
{
printf("OPEN FILE!!\n");
int fd = open("/dev/nobrand", O_RDWR);
if (fd == -1) {
printf("FILE OPEN ERROR!!!\n");
close(fd);
return 0;
}
char buf[10] = "HI Jaehoon";
ioctl(fd, _IO(0, 3), buf); // kernel에 데이터 전송
ioctl(fd, _IO(0, 4), buf); // kernel로부터 데이터 받기
printf("from kernel : %s\n", buf);
printf("CLOSE FILE!!\n");
close(fd);
return 0;
}
kernel.c
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
// 송수신 라이브러리
#include <linux/uaccess.h>
#include <linux/string.h>
#define NOD_NAME "nobrand"
#define NOD_MAJOR 100
MODULE_LICENSE("GPL");
static int nobrand_open(struct inode *inode, struct file *filp) {
printk( KERN_INFO "OPEN!!!\n");
return 0;
}
static int nobrand_release(struct inode *inode, struct file *filp) {
printk( KERN_INFO "CLOSE!!!\n");
return 0;
}
static long nobrand_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
// 변수 선언
char buf[10] = { 0 };
unsigned long ret;
switch (cmd)
{
case _IO(0, 3) :
printk(KERN_INFO "IO 3\n");
// app에서 보낸 문자열 10자리를 복사하여 수신한다.
ret = copy_from_user((void*)buf, (void *)arg, 10);
printk(KERN_INFO "%s\n", buf);
break;
case _IO(0, 4) :
printk(KERN_INFO "IO 4\n");
buf[0] = 'Z';
// app 단에 'Z'를 전송한다.
ret = copy_to_user((void *)arg, (void *)buf, 1);
printk(KERN_INFO "kernel will send %s\n", buf);
break;
}
printk( KERN_ALERT "%d\n", cmd);
return 0;
}
static struct file_operations fops = {
.open = nobrand_open,
.release = nobrand_release,
.unlocked_ioctl = nobrand_ioctl
};
static int nobrand_init(void) {
printk( KERN_INFO "OK HELLO NOBRAND!!\n");
if (register_chrdev(NOD_MAJOR, NOD_NAME, &fops) < 0) {
printk( KERN_INFO "ERROR!!! register error\n");
}
return 0;
}
static void nobrand_exit(void) {
unregister_chrdev(NOD_MAJOR, NOD_NAME);
printk( KERN_INFO "BYE BYE\n\n");
}
module_init(nobrand_init);
module_exit(nobrand_exit);
app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
// 구조체 선언 : 계산하기 편하기 위해 모두 문자형으로 지정
struct Node{
char size;
char buf[100];
};
int main()
{
printf("OPEN FILE!!\n");
int fd = open("/dev/nobrand", O_RDWR);
if (fd == -1) {
printf("FILE OPEN ERROR!!!\n");
close(fd);
return 0;
}
// 구조체 정의
struct Node t = { 2, "HI" };
// 구조체 주소 전송
ioctl(fd, _IO(0, 3), (void*)&t);
printf("CLOSE FILE!!\n");
close(fd);
return 0;
}
kernel.c
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include<linux/string.h>
#define NOD_NAME "nobrand"
#define NOD_MAJOR 100
MODULE_LICENSE("GPL");
static int nobrand_open(struct inode *inode, struct file *filp) {
printk( KERN_INFO "OPEN!!!\n");
return 0;
}
static int nobrand_release(struct inode *inode, struct file *filp) {
printk( KERN_INFO "CLOSE!!!\n");
return 0;
}
// 구조체 선언
struct Node{
char size;
char buf[100];
};
static long nobrand_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct Node t; // 구조체 정의
switch (cmd)
{
case _IO(0, 3) :
// 구조체 중 특정 변수에 복사
ret = copy_from_user(&(t.size) ,(void*)arg,1);
// 배열은 첫 주소값으로 반환하므로 & 안붙임
// 널문자까지 받아 문자열임을 인식하기 위해 SIZE + 1
// 또한 size을 문자형으로 선언했기 때문에 다음 구조체 변수의 주소가 +1이 된거지, 숫자형이였으면 +4를 해줘야함
ret = copy_from_user(t.buf, (void*)(arg+1), t.size+1);
printk(KERN_INFO "t.size = %d\n", t.size);
printk(KERN_INFO "t.buf = %s\n", t.buf);
break;
}
printk( KERN_ALERT "%d\n", cmd);
return 0;
}
static struct file_operations fops = {
.open = nobrand_open,
.release = nobrand_release,
.unlocked_ioctl = nobrand_ioctl
};
static int nobrand_init(void) {
printk( KERN_INFO "OK HELLO NOBRAND!!\n");
if (register_chrdev(NOD_MAJOR, NOD_NAME, &fops) < 0) {
printk( KERN_INFO "ERROR!!! register error\n");
}
return 0;
}
static void nobrand_exit(void) {
unregister_chrdev(NOD_MAJOR, NOD_NAME);
printk( KERN_INFO "BYE BYE\n\n");
}
module_init(nobrand_init);
module_exit(nobrand_exit);