커널에는 2가지 종류의 타이머가 존재한다. 하드웨어에 의해 일정한 주기마다 인터럽트가 발생되어 작업을 처리하는 system timer 와, 소프트웨어로 구현되어 특정 시간이 경과된 이후 한번 작업을 실행하는 dynamic timer 가 있다. 커널은 dynamic timer 를 동적으로 생성하거나 제거할 수 있고, 등록된 작업은 system timer 의 timer interrupt handler 과정에서 처리 된다.
이 포스팅에선 dynamic timer 에 구조와 구현방법을 위주로 살펴보려고 한다.
시스템 타이머의 인터럽트를 timer interrupt, timer interrupt 간의 주기를 tick 이라고 한다. 커널은 tick rate 를 미리 알고 있으며, 이를 활용하여 wall time (유저 프로그램에서 사용하는 현재 시각) 과 system uptime (시스템 부팅 후 상대적인 경과시간) 을 계속 갱신한다.
또 타이머와 관련한 중요 변수로 Jiffies 가 존재한다. 이는 시스템 부팅 후에 몇번의 tick 이 실행되었는지를 기록하는 변수로 dynamic timer 를 구현하는데 사용된다.
커널의 타이머 구조체는 다음과 같다.
jiffies 값으로 expires, 즉 만료 시점을 나타내며 그 시점이 지나면 function() callback 함수를 수행한다.
커널에서 동적 타이머를 초기화 하려면 timer_setup() 함수를 호출해야 한다.
그 외에 타이머를 등록/재등록/삭제하려면 다음 함수를 호출한다.
dynamic timer 를 512 개의 doubly-linked circular list 자료구조를 활용하여, 256개의 인덱스를 가지는 리스트 하나와 64개의 인덱스를 가지는 4개의 리스트로 분리해 구현한 예시를 살펴보자. 먼저, 이는 설명을 위한 단순화된 버젼으로 실제 커널에는 이보다 복잡하고 효율적인 방식으로 구현되어 있다는 것을 유념해야한다.
타이머가 초기화 될 때 timer_list 구조체에 expires 변수에 jiffies 값이 등록된다. 이 jiffies 값을 기준으로 표 tv1 부터 tv5 중 어떤 그룹의 어떤 index 에 해당 인스턴스가 등록될지 결정된다. 여기서 tv1 테이블만 인덱스가 tick 의 단위로 쪼개져 있고, 그 외에 테이블은 단위별로 grouping 되어있다.
tick interrupt 가 발생할 때 마다 tv1 의 index를 가르키는 포인터가 하나씩 증가한다(0~255). 만약 해당하는 index 에 타이머 인스턴스가 등록된게 있다면 해당 callback 함수를 호출하며 타이머가 expired 된다(1개 이상의 경우 리스트를 따라가며 여러개의 callback 함수를 수행한다). 만약 한바퀴를 다 돌았다면, tv2의 index를 가르키는 포인터에서 관리하는 256개의 리스트를 가져와 다시 tv1을 채워준다. tv2가 전체를 돌았을 때는 같은 동작으로 tv3의 그룹에서 tv2 로 가져오며, 이는 tv5 까지 반복되어 수십시간이 넘어가는 jiffies 값을 가지는 타이머 인스턴스를 모두 커버하며 효율적으로 관리할 수 있는 것이다.
'Linux kernel' 카테고리의 다른 글
[Interrupt] Top half & bottom half (1) | 2023.04.27 |
---|---|
sysfs (0) | 2023.04.17 |
Device Driver (0) | 2023.04.14 |
virtual file system (VFS) (0) | 2023.04.09 |