#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 |