본문 바로가기

리눅스 커널/기타

Kernel wait queue

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/signal.h>
#include <linux/sched/signal.h>
#include <linux/wait.h>

static struct task_struct * ts = NULL;
static DECLARE_WAIT_QUEUE_HEAD(wq);
//static wait_queue_head_t wq;
static int cond;

static int ts_routine(void * ptr)
{
        unsigned long remain;

        //allow_signal(SIGUSR1);
        while (1)
        {
                //remain = msleep_interruptible(5000);
                cond = 0;
                remain = wait_event_interruptible_timeout(wq, cond == 1, 5 * HZ);
                if (kthread_should_stop())
                        break;
                printk("wake up, remain time: %lums\n", remain);
        }

        printk("remain time: %lums\n", remain);
        return 0;
}


int sig_init (void)
{
        //init_waitqueue_head(&wq);
        ts = kthread_run(ts_routine, NULL, "test_thread");
        return 0;
}

void sig_cleanup(void)
{
        if (ts)
        {
                //send_sig(SIGUSR1, ts, 0);
                cond = 1;
                //wake_up_interruptible(&wq);
                kthread_stop(ts);
        }
}

module_init(sig_init);
module_exit(sig_cleanup);
MODULE_LICENSE("GPL");

 커널에서 대기 큐 자료구조는 다음과 같이 구현되어 있다.

struct wait_queue_head {
	spinlock_t		lock;
	struct list_head	head;
};
typedef struct wait_queue_head wait_queue_head_t;

그래서 해당 자료구조를 생성 및 초기화 하는 방식은 크게 두 가지로,

첫번째는 위 코드처럼 정적으로 만드는 방식과,

두번째는 wait_queue_head_t 변수를 선언 한 뒤, init_waitqueue_head() 매크로를 통해 필요한 곳에서 동적으로 초기화 하는 방법이 있다.

 

다음은 현재 실행 중인 태스크를 대기 큐에 넣어 정지 시키는 방법이다.

원하는 지점에 아래의 매크로를 통해 대기 큐에 넣을 수 있다.

#define wait_event(wq_head, condition)						\
do {										\
	might_sleep();								\
	if (condition)								\
		break;								\
	__wait_event(wq_head, condition);					\
} while (0)
//조건이 성립 될 때 까지 sleep.

#define wait_event_timeout(wq_head, condition, timeout)				\
({										\
	long __ret = timeout;							\
	might_sleep();								\
	if (!___wait_cond_timeout(condition))					\
		__ret = __wait_event_timeout(wq_head, condition, timeout);	\
	__ret;									\
})
//timeout동안 조건이 성립 될 때 까지 sleep.

#define wait_event_interruptible(wq_head, condition)				\
({										\
	int __ret = 0;								\
	might_sleep();								\
	if (!(condition))							\
		__ret = __wait_event_interruptible(wq_head, condition);		\
	__ret;									\
})
//wait_event와 동일하나 시그널을 받아 wake up 할 수 있음.

#define wait_event_interruptible_timeout(wq_head, condition, timeout)		\
({										\
	long __ret = timeout;							\
	might_sleep();								\
	if (!___wait_cond_timeout(condition))					\
		__ret = __wait_event_interruptible_timeout(wq_head,		\
						condition, timeout);		\
	__ret;									\
})
//위 매크로에서 timeout을 걸 수 있음.

타임아웃은 jiffies로 전달해야 한다.

 

마지막은 wake up 시키는 방법이다.

첫번째는 간단하게 조건을 완성시키면 된다. 위 코드에서 처럼 말이다.

두번째는 아래의 매크로를 통해 할 수 있다.

#define wake_up(x)			__wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_interruptible(x)	__wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

 

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

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