1. Linux Time .
Trong Linux kernel, thời gian được đo bằng một biến golbal tên là jiffies
. Jiffies là biến kiểu usigned long
và có giá trị bằng số lần xảy ra ngát của iterrrupt timer kể từ khi hệ thống được khởi động. Tần số của ngắt này được khai báo trong biến HZ
và biến này có thể được cấu hình ở thời điểm biên dịch (CONFIG_HZ), tuy nhiên nếu như không có nhu cầu gì đặc biệt thì tốt nhất nên để nó ở giá trị mặc định (250HZ trên x86_64).
2. Linux Standard timer.
Có nhiều mục đích cần đến jiffies
, tuy nhiên phổ biến nhất có lẽ là dùng để tính timeout cho timer.
Trong Linux có 2 bộ timer là: Standard timer, hay còn gọi là timer wheel và một bộ timer có độ chính xác cao (đến nanosecond) gọi là high resolution timers (hrtimer). Mặc dụ timer wheel ít chính xác hơn so với hrtimer tuy nhiên nó là giải pháp hiệu quả trong trường hợp timer có thể bị hủy bỏ trước khi nó kịp timeout (thường dùng để kiểm tra timeout của peer trong networking).
Standard Timer cung cấp các hàm để tạo, hủy bỏ và quản lý timer:
Trong Linux kernel, mỗi Timer là một biến với kiểu struct timer_list
, cấu trúc này có chứa một callback function được gọi khi timer hết hạn, cấu trúc này và các hàm liên quan được khai báo trong linux/timer.h
. Dĩ nhiên là để sử dụng Timer chúng ta cần khởi tạo nó, bằng cách sử dụng init_timer
hoặc setup_timer
.
(ngoài ra còn có 1 cái macro tên DEFINE_TIMER nữa).
Hàm setup_timer
sẽ khởi tạo timer_list và gán hàm truyền vào (tham số thứ 2) cho callback function của timer_líst vừa được khởi tạo. Trong khi hàm init_timer
thì chúng ta phải tự gán các giá trị cần thiết của timer sau đó gọi init_timer
để đăng ký với kernel. (THật ra thì macro setup_timer sẽ gọi đến macro init_timer).
Note Trong các version kernel mới(từ 4.15) thì setup_timer
được đổi thành timer_setup
Sau khi đã khởi tạo timer, ta có thể đặt thời gian timeout cho nó thông qua hàm mod_timer
hay xóa timer với del_timer
.
3. Timer example.
Sau đây mình sẽ thử viết một module đơn giản để demo khả năng của timer api. Trong module này mình sẽ khai báo một timer trong hàm init_module và đặt timeout cho nó là 200ms.
Và gán hàm oni_callback
làm callback function cho nó. Hàm này sẽ được gọi sau 200ms. Trong code có giải thích cụ thể bằng comment.
Khi insert module này vào hệ thống, dmesg sẽ hiển thị log sau:
Mình đang chạy thử nó trên x86_64, và như nói ở trên thì HZ của platform này là 250, tức là 1 jiffies sẽ tương đương với 4ms. Theo log trên ta có fired_time - started_time = 4297990134 - 4297990083 = 51 jiffies * 4ms = 204 ms. (4ms là delta nhỏ ở trên). Tức là timer của mình chạy đủ 200ms.
4. High-resolution timer API
Như đã nói ở phần đầu thì kernel còn một loại timer nữa là hrtimer - high resolution timer, tạm dịch là timer độ chính xác cao: hrtimer cho độ chính xác tới nano secs. Khác với standard timer được xây dụng dựa trên giá trị jiffer, thì hrtimer
lại sử dụng giá trị ktime làm đơn vị đo, kernel cung cấp hàm ktime_set để chuyển đổi từ thời gian thông thường thành ktime. Hrtimer cũng được biểu diễn bằng một struct tên là hrtimer
.
Hrtimer sẽ dùng một cấu trúc cây (red-black tree) - struct rbtree_node
của kernel để quản lý các timer, các timer được thêm vào cây theo thứ tự thời gian trước-sau để giảm thiệu chi phí xử lý.
Để sử dụng được HRT thì kernl cần phải enable CONFIG_HIGH_RES_TIMERS. Để biết HRT có được enable trên thiết bị mà đang sử dụng hay không, ta có thể dùng cmd sau:
Nếu kết quả trả về có ít nhất 1 entry ghi “.resolution: 1 nsecs” thì có nghĩa là HRT đã được enable. Vậy câu hỏi là: khi nào thì dùng HRT? tất nhiên là khi cần độ chính xác cao - như cái tên rồi :gach:. Thật ra nó thường được dùng trong multimedia là chính thì phải, có lần đọc được thế ở đâu quên rồi, ahihi.
Một HRT được khởi tạo thông qua hàm hrtimer_init
, hàm này nhận 3 tham số: timer struct, clock được sử dụng và timer mode. Clock được định nghĩa trong file timer.h, đại diện cho các loại clock mà hệ thống hỗ trợ (ví dụ như CLOCK_MONOTONIC hay CLOCK_REALTIME), còn timer mode có hai dạng là thời gian tương đối (HRTIMER_MODE_RLL) và thời gian tuyệt đối (HRTIMER_MODE_ABS).
Một khi đã khởi tạo timer, thì ta có thể bắt đầu nó bằng cách gọi hàm hrtimer_start
, trong hàm này sẽ có thời gian timeout được truyền vào. Và tương tự như standard timer thì HRT cũng cung cấp khả năng hủy bọ timer bằng các hàm hrtimer_cancel
hoặc hrtimer_try_to_cancal
. Điểm khách nhau giữa hai hàm cancel này là nếu như chúng được gọi khi timer đã gọi hàm callback, thì hàm đầu tiên sẽ đợi hàm callback hoàn thành rồi, còn hàm thứ hai sẽ kệ nó và báo lỗi là không cancel được.
Ngoài ra ta cũng có thể kiểm tra xem timer đã gọi hàm callback của nó chưa bằng cách sử dụng hrtimer_callback_running
function. Hai hàm khác cũng có thể được dùng để kiểm tra trạng thái timer là : hrtimer_get_remaining
và hrtimer_cb_get_time
, một hàm dùng để kiểm tra thời gian còn lại của timer, một hàm dùng để kiểm tra thời gian đã chạy của timer.
Kernel cũng cung cấp hai hàm hrtimer_forward
và hrtimer_forward_now
để thay đổi thời gian timeout của timer sau khi nó đã start.
5. HRT example.
Một ví dụ tương tự phần trước, nhưng thay vì sử dụng timer_list, mình sẽ dùng hrtimer.
Khi thêm module trên vào hệ thống, ta thu được log sau. Có thể thấy là nó chính xác đúng 200ms luôn 0o0.
Leave a Comment