## 7.2.?獲知當前時間
內核代碼能一直獲取一個當前時間的表示, 通過查看 jifies 的值. 常常地, 這個值只代表從最后一次啟動以來的時間, 這個事實對驅動來說無關, 因為它的生命周期受限于系統的 uptime. 如所示, 驅動可以使用 jiffies 的當前值來計算事件之間的時間間隔(例如, 在輸入驅動中從單擊中區分雙擊或者計算超時). 簡單地講, 查看 jiffies 幾乎一直是足夠的, 當你需要測量時間間隔. 如果你需要對短時間流失的非常精確的測量, 處理器特定的寄存器來幫忙了( 盡管它們帶來嚴重的移植性問題 ).
它是非常不可能一個驅動會需要知道墻上時鐘時間, 以月, 天, 和小時來表達的; 這個信息常常只對用戶程序需要, 例如 cron 和 syslogd. 處理真實世界的時間常常最好留給用戶空間, 那里的 C 庫提供了更好的支持; 另外, 這樣的代碼常常太策略相關以至于不屬于內核. 有一個內核函數轉變一個墻上時鐘時間到一個 jiffies 值, 但是:
~~~
#include <linux/time.h>
unsigned long mktime (unsigned int year, unsigned int mon,
unsigned int day, unsigned int hour,
unsigned int min, unsigned int sec);
~~~
重復:直接在驅動中處理墻上時鐘時間往往是一個在實現策略的信號, 并且應當因此而被置疑.
雖然你不會一定處理人可讀的時間表示, 有時你需要甚至在內核空間中處理絕對時間. 為此, <linux/time.h> 輸出了 do_gettimeofday 函數. 當被調用時, 它填充一個 struct timeval 指針 -- 和在 gettimeofday 系統調用中使用的相同 -- 使用類似的秒和毫秒值. do_gettimeofday 的原型是:
~~~
#include <linux/time.h>
void do_gettimeofday(struct timeval *tv);
~~~
這段源代碼聲明 do_gettimeofday 有" 接近毫秒的精度", 因為它詢問時間硬件當前 jiffy 多大比例已經流失. 這個精度每個體系都不同, 但是, 因為它依賴實際使用中的硬件機制. 例如, 一些 m68knommu 處理器, Sun3 系統, 和其他 m68k 系統不能提供大于 jiffy 的精度. Pentium 系統, 另一方面, 提供了非常快速和精確的小于嘀噠的測量, 通過讀取本章前面描述的時戳計數器.
當前時間也可用( 盡管使用 jiffy 的粒度 )來自 xtime 變量, 一個 struct timespec 值. 不鼓勵這個變量的直接使用, 因為難以原子地同時存取這 2 個字段. 因此, 內核提供了實用函數 current_kernel_time:
~~~
#include <linux/time.h>
struct timespec current_kernel_time(void);
~~~
用來以各種方式獲取當前時間的代碼, 可以從由 O' Reilly 提供的 FTP 網站上的源碼文件的 jit ("just in time") 模塊獲得. jit 創建了一個文件稱為 /proc/currentime, 當讀取時, 它以 ASCII 碼返回下列項:
-
當前的 jiffies 和 jiffies_64 值, 以 16 進制數的形式.
-
如同 do_gettimeofday 返回的相同的當前時間.
-
由 current_kernel_time 返回的 timespec.
我們選擇使用一個動態的 /proc 文件來保持樣板代碼為最小 -- 它不值得創建一整個設備只是返回一點兒文本信息.
這個文件連續返回文本行只要這個模塊加載著; 每次 read 系統調用收集和返回一套數據, 為更好閱讀而組織為 2 行. 無論何時你在少于一個時鐘嘀噠內讀多個數據集, 你將看到 do_gettimeofday 之間的差別, 它詢問硬件, 并且其他值僅在時鐘嘀噠時被更新.
~~~
phon% head -8 /proc/currentime
0x00bdbc1f 0x0000000100bdbc1f 1062370899.630126
1062370899.629161488
0x00bdbc1f 0x0000000100bdbc1f 1062370899.630150
1062370899.629161488
0x00bdbc20 0x0000000100bdbc20 1062370899.630208
1062370899.630161336
0x00bdbc20 0x0000000100bdbc20 1062370899.630233
1062370899.630161336
~~~
在上面的屏幕快照中, 由 2 件有趣的事情要注意. 首先, 這個 current_kernel_time 值, 盡管以納秒來表示, 只有時鐘嘀噠的粒度; do_gettimeofday 持續報告一個稍晚的時間但是不晚于下一個時鐘嘀噠. 第二, 這個 64-位的 jiffies 計數器有 高 32-位字集合的最低有效位. 這是由于 INITIAL_JIFFIES 的缺省值, 在啟動時間用來初始化計數器, 在啟動時間后幾分鐘內強加一個低字溢出來幫助探測與這個剛好溢出相關的問題. 這個在計數器中的初始化偏好沒有效果, 因為 jiffies 與墻上時鐘時間無關. 在 /proc/uptime 中, 這里內核從計數器中抽取 uptime, 初始化偏好在轉換前被去除.
- Linux設備驅動第三版
- 第 1 章 設備驅動簡介
- 1.1. 驅動程序的角色
- 1.2. 劃分內核
- 1.3. 設備和模塊的分類
- 1.4. 安全問題
- 1.5. 版本編號
- 1.6. 版權條款
- 1.7. 加入內核開發社團
- 1.8. 本書的內容
- 第 2 章 建立和運行模塊
- 2.1. 設置你的測試系統
- 2.2. Hello World 模塊
- 2.3. 內核模塊相比于應用程序
- 2.4. 編譯和加載
- 2.5. 內核符號表
- 2.6. 預備知識
- 2.7. 初始化和關停
- 2.8. 模塊參數
- 2.9. 在用戶空間做
- 2.10. 快速參考
- 第 3 章 字符驅動
- 3.1. scull 的設計
- 3.2. 主次編號
- 3.3. 一些重要數據結構
- 3.4. 字符設備注冊
- 3.5. open 和 release
- 3.6. scull 的內存使用
- 3.7. 讀和寫
- 3.8. 使用新設備
- 3.9. 快速參考
- 第 4 章 調試技術
- 4.1. 內核中的調試支持
- 4.2. 用打印調試
- 4.3. 用查詢來調試
- 4.4. 使用觀察來調試
- 4.5. 調試系統故障
- 4.6. 調試器和相關工具
- 第 5 章 并發和競爭情況
- 5.1. scull 中的缺陷
- 5.2. 并發和它的管理
- 5.3. 旗標和互斥體
- 5.4. Completions 機制
- 5.5. 自旋鎖
- 5.6. 鎖陷阱
- 5.7. 加鎖的各種選擇
- 5.8. 快速參考
- 第 6 章 高級字符驅動操作
- 6.1. ioctl 接口
- 6.2. 阻塞 I/O
- 6.3. poll 和 select
- 6.4. 異步通知
- 6.5. 移位一個設備
- 6.6. 在一個設備文件上的存取控制
- 6.7. 快速參考
- 第 7 章 時間, 延時, 和延后工作
- 7.1. 測量時間流失
- 7.2. 獲知當前時間
- 7.3. 延后執行
- 7.4. 內核定時器
- 7.5. Tasklets 機制
- 7.6. 工作隊列
- 7.7. 快速參考
- 第 8 章 分配內存
- 8.1. kmalloc 的真實故事
- 8.2. 后備緩存
- 8.3. get_free_page 和其友
- 8.4. 每-CPU 的變量
- 8.5. 獲得大量緩沖
- 8.6. 快速參考
- 第 9 章 與硬件通訊
- 9.1. I/O 端口和 I/O 內存
- 9.2. 使用 I/O 端口
- 9.3. 一個 I/O 端口例子
- 9.4. 使用 I/O 內存
- 9.5. 快速參考
- 第 10 章 中斷處理
- 10.1. 準備并口
- 10.2. 安裝一個中斷處理
- 10.3. 前和后半部
- 10.4. 中斷共享
- 10.5. 中斷驅動 I/O
- 10.6. 快速參考
- 第 11 章 內核中的數據類型
- 11.1. 標準 C 類型的使用
- 11.2. 安排一個明確大小給數據項
- 11.3. 接口特定的類型
- 11.4. 其他移植性問題
- 11.5. 鏈表
- 11.6. 快速參考
- 第 12 章 PCI 驅動
- 12.1. PCI 接口
- 12.2. 回顧: ISA
- 12.3. PC/104 和 PC/104+
- 12.4. 其他的 PC 總線
- 12.5. SBus
- 12.6. NuBus 總線
- 12.7. 外部總線
- 12.8. 快速參考
- 第 13 章 USB 驅動
- 13.1. USB 設備基礎知識
- 13.2. USB 和 sysfs
- 13.3. USB 的 Urbs
- 13.4. 編寫一個 USB 驅動
- 13.5. 無 urb 的 USB 傳送
- 13.6. 快速參考
- 第 14 章 Linux 設備模型
- 14.1. Kobjects, Ksets 和 Subsystems
- 14.2. 低級 sysfs 操作
- 14.3. 熱插拔事件產生
- 14.4. 總線, 設備, 和驅動
- 14.5. 類
- 14.6. 集成起來
- 14.7. 熱插拔
- 14.8. 處理固件
- 14.9. 快速參考
- 第 15 章 內存映射和 DMA
- 15.1. Linux 中的內存管理
- 15.2. mmap 設備操作
- 15.3. 進行直接 I/O
- 15.4. 直接內存存取
- 15.5. 快速參考
- 第 16 章 塊驅動
- 16.1. 注冊
- 16.2. 塊設備操作
- 16.3. 請求處理
- 16.4. 一些其他的細節
- 16.5. 快速參考
- 第 17 章 網絡驅動
- 17.1. snull 是如何設計的
- 17.2. 連接到內核
- 17.3. net_device 結構的詳情
- 17.4. 打開與關閉
- 17.5. 報文傳送
- 17.6. 報文接收
- 17.7. 中斷處理
- 17.8. 接收中斷緩解
- 17.9. 連接狀態的改變
- 17.10. Socket 緩存
- 17.11. MAC 地址解析
- 17.12. 定制 ioctl 命令
- 17.13. 統計信息
- 17.14. 多播
- 17.15. 幾個其他細節
- 17.16. 快速參考
- 第 18 章 TTY 驅動
- 18.1. 一個小 TTY 驅動
- 18.2. tty_driver 函數指針
- 18.3. TTY 線路設置
- 18.4. ioctls 函數
- 18.5. TTY 設備的 proc 和 sysfs 處理
- 18.6. tty_driver 結構的細節
- 18.7. tty_operaions 結構的細節
- 18.8. tty_struct 結構的細節
- 18.9. 快速參考