[커널] ioctl

정재훈·2022년 5월 10일
0

Kernel

목록 보기
6/8

ioctl

ioctl : short for Input Output Control
ioctl(fd, cmd, argument) : 3 개의 파라미터
1. File descriptor
2. cmd : 구분자
3. argument : 추가 정보

CMD

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_usercopy_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);
profile
여러 방향으로 접근하는 개발자

0개의 댓글