jiffies
커널에서 시간은 타이머 인터럽트를 통해 관리되는데, 타이머 인터럽트는 1초당 n번 발생하는 빈도가 HZ로 정의되어 있음. 이 HZ값은 아키텍처마다 다르게 정의되어 있다. 만약 100HZ라고 하면 1초당 100번의 타이머 인터럽트가 발생함
해당 커널 시스템의 HZ 값은 .config에 CONFIG_HZ로 정의되어 있다.
커널에는 시스템이 시작한 후, 해당 타이머 인터럽트 발생횟수를 저장하는 jiffies라고하는 전역변수가 정의되어 있는데
extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
만약 HZ가 100인 시스템에서 시스템이 시작된 뒤 1초 후 jiffies는 값은 100이다.
해당 jiffies값을 이용하여 상대적인 시간 처리가 가능해지는데 예를 들어 HZ 값이 100일 때, 3초 뒤의 시간은 현재 jiffies 값에 3 * 100(HZ)를 더한 값이 3초 후를 나타낸다.
int timeout = jiffies + 3*HZ; // 현재 시간으로 부터 3초 뒤를 나타내는 수식
위 jiffies를 기반으로 커널에서는 비동기적으로 타이머 작업을 수행할 수 있다.
struct timer_list
/* linux/timer.h*/
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct hlist_node entry;
unsigned long expires;
void (*function)(struct timer_list *);
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
커널에 정의되어 있는 timer API를 사용하기 위해서는 대개 struct timer_list를 정의해주어야 한다.
해당 구조체의 주요 변수로 expires의 경우 타이머가 만료되는 시간을 나타내며 대개 jiffies + α * HZ로 설정된다.
function의 경우 해당 타이머가 만료될 때 호출되는 콜백 함수를 등록하기 위한 변수이다.
flags는 해당 timer의 속성을 나타낸다.
타이머 초기화 및 등록
timer_setup
위의 struct timer_list를 초기화하는 macro로 timer객체, callback, flags를 인자로 받아 타이머 객체를 초기화한다.
#define timer_setup(timer, callback, flags) __init_timer((timer), (callback), (flags))
mod_timer, add_timer
타이머 객체를 초기화 한 뒤에는, 초기화 된 timer 객체를 커널에 등록해야 하는데 mod_timer 또는 add_timer를 통해 타이머를 등록한다. 등록된 타이머는 설정한 expires 시간에 callback함수를 호출한다.
/**
* mod_timer - modify a timer's timeout
* @timer: the timer to be modified
* @expires: new timeout in jiffies
*
* mod_timer() is a more efficient way to update the expire field of an
* active timer (if the timer is inactive it will be activated)
*
* mod_timer(timer, expires) is equivalent to:
*
* del_timer(timer); timer->expires = expires; add_timer(timer);
*
* Note that if there are multiple unserialized concurrent users of the
* same timer, then mod_timer() is the only safe way to modify the timeout,
* since add_timer() cannot modify an already running timer.
*
* The function returns whether it has modified a pending timer or not.
* (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
* active timer returns 1.)
*/
int mod_timer(struct timer_list *timer, unsigned long expires)
{
return __mod_timer(timer, expires, 0);
}
EXPORT_SYMBOL(mod_timer);
/**
* add_timer - start a timer
* @timer: the timer to be added
*
* The kernel will do a ->function(@timer) callback from the
* timer interrupt at the ->expires point in the future. The
* current time is 'jiffies'.
*
* The timer's ->expires, ->function fields must be set prior calling this
* function.
*
* Timers with an ->expires field in the past will be executed in the next
* timer tick.
*/
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
EXPORT_SYMBOL(add_timer);
add_timer()는 timer list에 정의한 expires와 function이 반드시 등록되어 있어야 하며, expires 이후 function에 등록한 함수를 호출한다.
mod_timer()의 경우 새로 timeout을 인자로 받아들여 새로 expires 값을 설정한 후 timer 등록을 수행하는 함수이다. (description에 나와있듯이 기존 등록된 timer를 제거하고, expires를 재설정 후, add_timer() 동작을 수행한다.)
타이머 callback 함수에 해당 add_timer 함수를 재귀 호출하게 되면, 반복적으로 일정 간격마다 callback 함수를 호출하여 반복작업을 수행한다.
del_timer()
등록된 타이머는 타이머 동작 수행 후 알아서 등록된 timer를 제거하는데, 수행 이전 등록한 timer를 제거해야 하는 경우가 있다. (e.g. timeout 이전에 특정 동작이 되서 timeout를 알릴 필요가 없는 경우)
int del_timer(struct timer_list *timer)
'Linux System > Linux Kernel' 카테고리의 다른 글
Kernel Panic Calltrace 분석 (0) | 2024.02.08 |
---|---|
runtime에 target board kernel의 config 확인 (0) | 2022.09.14 |
User Space와 Kernel Space (0) | 2022.08.17 |
Device Driver - Atomic (0) | 2022.05.23 |
Kernel Build (0) | 2022.03.03 |