본문 바로가기

리눅스 커널/기타

Device file

해당 글은 아래 사이트를 참조하여 개인적으로 공부한 바를 정리한 글이다.

https://sysplay.in/blog/linux-kernel-internals/2015/10/waiting-blocking-in-linux-driver/

 

리눅스 커널에서 프로세스를 관리하는 자료구조는 task_struct이며, state라는 멤버를 통해 현재 해당 태스크의 상태정보를 알 수 있다.

/* Used in tsk->state: */
#define TASK_RUNNING			0 //run 큐 내에 있거나, 실행중 인 상태.
#define TASK_INTERRUPTIBLE		1 //이벤트를 기다리는 상태, 시그널에 의해 wake up 될 수 있다.
#define TASK_UNINTERRUPTIBLE		2 //역시 이벤트를 기다리나, 시그널에 의해 wake up 될 수 없다.
#define __TASK_STOPPED			4 //디버거에 의해 멈춘 상태.
#define __TASK_TRACED			8 //
/* Used in tsk->exit_state: */
#define EXIT_DEAD			16 //
#define EXIT_ZOMBIE			32 //좀비 프로세스인 상태

지금까지 디바이스 파일을 만들기 위해 mknod 명령어를 사용해 왔는데, 

다음과 같은 코드로 모듈 로드 시 자동으로 만들 수 있다.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/delay.h>

#define FIRST_MINOR 0
#define MINOR_CNT 1

static dev_t dev;
static struct cdev c_dev;
static struct class *cl;

struct file_operations fops =
{
	.read = read,
	.write = write,
	.open = open,
	.release = release
};

int schd_init (void)
{
	int ret;
	struct device *dev_ret;

	if ((ret = alloc_chrdev_region(&dev, FIRST_MINOR, MINOR_CNT, "wqd")) < 0)
	{
		return ret;
	}
	printk("Major Nr: %d\n", MAJOR(dev));

	cdev_init(&c_dev, &fops);

	if ((ret = cdev_add(&c_dev, dev, MINOR_CNT)) < 0)
	{
		unregister_chrdev_region(dev, MINOR_CNT);
		return ret;
	}

	if (IS_ERR(cl = class_create(THIS_MODULE, "chardrv")))
	{
		cdev_del(&c_dev);
		unregister_chrdev_region(dev, MINOR_CNT);
		return PTR_ERR(cl);
	}
	if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, "mychar%d", 0)))
	{
		class_destroy(cl);
		cdev_del(&c_dev);
		unregister_chrdev_region(dev, MINOR_CNT);
		return PTR_ERR(dev_ret);
	}
	return 0;
}

void schd_cleanup(void)
{
	printk(KERN_INFO " Inside cleanup_module\n");
	device_destroy(cl, dev);
	class_destroy(cl);
	cdev_del(&c_dev);
	unregister_chrdev_region(dev, MINOR_CNT);
}

module_init(schd_init);
module_exit(schd_cleanup);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pradeep Tewani");
MODULE_DESCRIPTION("Waiting Process Demo");

//Ref, https://sysplay.in/blog/linux-kernel-internals/2015/10/waiting-blocking-in-linux-driver/

먼저 모듈 로드 시 흐름에 대해 살펴보면,

1. alloc_chrdev_region() : 캐릭터 장치의 개수에 대한 범위를 할당, 메이저 번호는 동적으로 할당되며, 성공 시 에는 0이  실패시에는 에러 코드가 반환 된다.

 

2. cdev_init() : 캐릭터 장치 구조체를 초기화 한다. 

 

3. cdev_add() : 초기화 된 캐릭터 장치를 시스템에 등록한다.

 

4. class_create() : device_create()를 위한 class 구조체를 만든다. 성공 시 정상적인 포인터가 실패 시에는 ERR_PTR()이 반환 된다.

 

5. device_create() :  device를 만들고 등록한다.

 

모듈 제거시에는 반대되는 의미의 함수가 역순으로 실행되는 것을 볼 수 있다.

 

다음은 schedule() 함수에 대한 내용이다.

ssize_t read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
	printk(KERN_INFO "Inside read\n");
	printk(KERN_INFO "Scheduling out\n");
	//set_current_state(TASK_INTERRUPTIBLE);
	schedule();
	printk(KERN_INFO "Woken up\n");
	return 0;
}

schedule() 함수는 자발적으로 cpu소유권을 포기하게 만든다. 쉽게 말하면 잘 동작하고 있다가 그 실행을 멈추고 대기 큐에 들어가게 되는 것이다.

위 경우에서는 다시 cpu에 의해 디스패치 되어 실행을 이어하게 되지만,

주석이 해제되면 현재 해당 코드를 실행하는 프로세스, current의 상태 정보를 인자로 전달하는 것으로 바꾸게 되어,

특정 이벤트가 발생하거나, 시그널에 의해 wake up될 때 가지 실행을 이어가지 못하게 된다.

 

그래서 이러한 경우

wake_up_process()라는 함수를 통해 해당 태스크를 깨울 수 있다.



'리눅스 커널 > 기타' 카테고리의 다른 글

dmesg console output  (0) 2021.10.28
칼리 리눅스 커널 컴파일 시 에러에 관해  (0) 2021.10.28
Kernel wait queue  (0) 2021.10.21
Kernel thread & signal  (0) 2021.10.21
Kernel module compile  (0) 2021.10.21