1、概述
不知道有沒有「時間系統」的說法,咱們暫且把它做爲Linux中和時間相關的內容的統稱吧。
Linux時間有兩個,系統時間(Wall Time),RTC時間。
系統時間(WT):由Linux系統軟件維持的時間,好比command date:
[java] view plain copyjava
- $ date
- 2017年 02月 25日 星期六 16:58:10 CST
獲取到的就是系統時間。
RTC時間:這個時間來自咱們設備上的RTC芯片,經過command hwclock 能夠讀取:
[java] view plain copypython
- # hwclock -r --> root權限才能夠運行
- 2017年02月25日 星期六 17時01分57秒 -0.906462 seconds
咱們經過man查看hwclock的介紹:
[python] view plain copylinux
- hwclock - query or set the hardware clock (RTC)
接下來,經過代碼看下二者的關係。app
2、WT時間和RTC時間
WT時間來自於RTC時間,流程是:
[python] view plain copy框架
- 上電-->RTC驅動加載-->從RTC同步時間到WT時間
上代碼:
[objc] view plain copyasync
- hctosys.c (drivers\rtc)
- static int __init rtc_hctosys(void)
- {
- struct timespec tv = {
- .tv_nsec = NSEC_PER_SEC >> 1,
- };
-
- err = rtc_read_time(rtc, &tm);
- err = do_settimeofday(&tv);
- dev_info(rtc->dev.parent,
- "setting system clock to "
- "%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- (unsigned int) tv.tv_sec);
- }
- late_initcall(rtc_hctosys);
late_initcall說明系統在啓動流程的後面纔會調用該函數去同步時間。關於late_initcall可見最後PS。
接下來從底層到上層進行梳理。函數
3、RTC時間框架
框架如圖:
post
注:圖中hwclock.c筆誤,實爲hctosys.cui
- Hardware:提供時間信息(time&alarm),經過必定的接口(好比I2C)和
- RTC Driver:進行交互。Driver完成硬件的訪問功能,提供訪問接口,以驅動的形式駐留在系統。註冊方式由
- class.c:文件提供。驅動註冊成功後會構建rtc_device結構體表徵的rtc設備,並把rtc芯片的操做方式存放到rtc設備的ops成員中
- interface.c:文件屏蔽硬件相關的細節,向上提供統一的獲取/設置時間或Alarm的接口
- rtc-lib.c:文件提供通用的時間操做函數,如rtc_time_to_tm、rtc_valid_tm等
- rtc-dev.c:文件在/dev/目錄下建立設備節點供應用層訪問,如open、read、ioctl等,訪問方式填充到file_operations結構體中
- hctosys.c/rtc-sys.c/rtc-proc.c:看名字就知道其做用
接下來從驅動一層一層看下spa
4、RTC驅動
驅動主要工做是填充rtc_class_ops結構體,結構體描述了RTC芯片可以提供的全部操做方式:
[objc] view plain copy
- struct rtc_class_ops {
- int (*open)(struct device *);
- void (*release)(struct device *);
- int (*ioctl)(struct device *, unsigned int, unsigned long);
- int (*read_time)(struct device *, struct rtc_time *);
- int (*set_time)(struct device *, struct rtc_time *);
- int (*read_alarm)(struct device *, struct rtc_wkalrm *);
- int (*set_alarm)(struct device *, struct rtc_wkalrm *);
- int (*proc)(struct device *, struct seq_file *);
- int (*set_mmss)(struct device *, unsigned long secs);
- int (*read_callback)(struct device *, int data);
- int (*alarm_irq_enable)(struct device *, unsigned int enabled);
- };
實現:
[objc] view plain copy
- static const struct rtc_class_ops test_rtc_ops = {
- .read_time = test_rtc_read_time,
- .set_time = test_rtc_set_time,
- .read_alarm = test_rtc_read_alarm,
- .set_alarm = test_rtc_set_alarm,
- .ioctl = test_rtc_ioctl,
- .proc = test_rtc_proc
- };
註冊:
[objc] view plain copy
- rtc_device_register(name, dev, &test_rtc_ops, THIS_MODULE);
成功的話log:
[python] view plain copy
- [ 1.531114] test_rtc_init Enter.
- [ 1.531126] bus: 'i2c': add driver test_rtc
- [ 1.531189] test_rtc_probe Enter.
- [ 1.533990] test_rtc_read_time Enter.
- [ 1.534527] test_rtc_read_time Exit.
- [ 1.534537] test_rtc_read_alarm Enter.
- [ 1.534546] test_rtc_read_alarm Exit.
- [ 1.534556] test_rtc_read_time Enter.
- [ 1.535083] test_rtc_read_time Exit.
- [ 1.535237] test_rtc 2-0051: rtc core: registered test_rtc as rtc0
- [ 1.535250] test_rtc_probe Exit.
5、class.c和RTC驅動註冊
class.c文件在RTC驅動註冊以前開始獲得運行:
[objc] view plain copy
- static int __init rtc_init(void)
- {
- rtc_class = class_create(THIS_MODULE, "rtc");
- rtc_class->suspend = rtc_suspend;
- rtc_class->resume = rtc_resume;
- rtc_dev_init();
- rtc_sysfs_init(rtc_class);
- return 0;
- }
- subsys_initcall(rtc_init);
完成:
- 三、rtc_dev_init():動態申請/dev/rtcN的設備號
- 四、rtc_sysfs_init():rtc類具備的device_attribute屬性
接着看RTC驅動註冊:
[objc] view plain copy
- struct rtc_device *rtc_device_register(const charchar *name, struct device *dev,
- const struct rtc_class_ops *ops,
- struct module *owner)
- {
- struct rtc_device *rtc;
- struct rtc_wkalrm alrm;
- int id, err;
-
- // 一、Linux支持多個RTC設備,因此須要爲每個設備分配一個ID
- // 對應與/dev/rtc0,/dev/rtc1,,,/dev/rtcN
- id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
-
- // 二、建立rtc_device設備(對象)並執行初始化
- rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
- rtc->id = id;
- rtc->ops = ops; // 2.1 對應RTC驅動填充的test_rtc_ops
- rtc->owner = owner;
- rtc->irq_freq = 1;
- rtc->max_user_freq = 64;
- rtc->dev.parent = dev;
- rtc->dev.class = rtc_class;// 2.2 rtc_init()建立的rtc_class
- rtc->dev.release = rtc_device_release;
-
- // 2.3 rtc設備中相關鎖、等待隊列的初始化
- mutex_init(&rtc->ops_lock);
- spin_lock_init(&rtc->irq_lock);
- spin_lock_init(&rtc->irq_task_lock);
- init_waitqueue_head(&rtc->irq_queue);
-
- // 2.4 Init timerqueue:咱們都知道,手機等都是能夠設置多個鬧鐘的
- timerqueue_init_head(&rtc->timerqueue);
- INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
- // 2.5 Init aie timer:alarm interrupt enable,RTC鬧鐘中斷
- rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (voidvoid *)rtc);
- // 2.6 Init uie timer:update interrupt,RTC更新中斷
- rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (voidvoid *)rtc);
- /* Init pie timer:periodic interrupt,RTC週期性中斷 */
- hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- rtc->pie_timer.function = rtc_pie_update_irq;
- rtc->pie_enabled = 0;
-
- /* Check to see if there is an ALARM already set in hw */
- err = __rtc_read_alarm(rtc, &alrm);
-
- // 三、若是RTC芯片中設置了有效的Alarm,則初始化:加入到rtc->timerqueue隊列中
- if (!err && !rtc_valid_tm(&alrm.time))
- rtc_initialize_alarm(rtc, &alrm);
-
- // 四、根據name參數設置rtc的name域
- strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
- // 五、設置rtc的dev成員中的name域
- dev_set_name(&rtc->dev, "rtc%d", id);
-
- // 六、/dev/rtc0的rtc做爲字符設備進行初始化
- // rtc_dev_prepare-->cdev_init(&rtc->char_dev, &rtc_dev_fops);
- rtc_dev_prepare(rtc);
-
- // 七、添加rtc設備到系統
- err = device_register(&rtc->dev);
-
- // 八、rtc設備做爲字符設備添加到系統
- // rtc_dev_add_devicec-->dev_add(&rtc->char_dev, rtc->dev.devt, 1)
- // 而後就存在/dev/rtc0了
- rtc_dev_add_device(rtc);
- rtc_sysfs_add_device(rtc);
- // 九、/proc/rtc
- rtc_proc_add_device(rtc);
-
- dev_info(dev, "rtc core: registered %s as %s\n",
- rtc->name, dev_name(&rtc->dev));
-
- return rtc;
- }
有了/dev/rtc0後,應用層就能夠經過open/read/ioctl操做RTC設備了,對應與內核的file_operations:
[objc] view plain copy
- static const struct file_operations rtc_dev_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = rtc_dev_read,
- .poll = rtc_dev_poll,
- .unlocked_ioctl = rtc_dev_ioctl,
- .open = rtc_dev_open,
- .release = rtc_dev_release,
- .fasync = rtc_dev_fasync,
- };
6、硬件抽象層
硬件抽象,即屏蔽具體的硬件細節,爲上層用戶提供統一的調用接口,使用者無需關心這些接口是怎麼實現的。
以RTC訪問爲例,抽象的實現位於interface.c文件,其實現基於class.c中建立的rtc_device設備。
實現原理,以rtc_set_time爲例:
[objc] view plain copy
- int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
- {
- int err;
- // 一、參數檢測
- err = rtc_valid_tm(tm);
-
- err = mutex_lock_interruptible(&rtc->ops_lock);
- if (err)
- return err;
-
- // 二、調用rtc_device中ops結構體的函數指針
- // ops結構體的函數指針已經在RTC驅動中被賦值
- if (!rtc->ops)
- err = -ENODEV;
- else if (rtc->ops->set_time)
- err = rtc->ops->set_time(rtc->dev.parent, tm);
- mutex_unlock(&rtc->ops_lock);
- /* A timer might have just expired */
- schedule_work(&rtc->irqwork);
- return err;
- }
7、rtc在文件系統中的呈現
一、rtc在sysfs
以前曾創建過名爲rtc的class:
[python] view plain copy
- rtc_class = class_create(THIS_MODULE, "rtc");
查看之:
[python] view plain copy
- # ls /sys/class/rtc/
- rtc0
- # ls -l /sys/class/rtc/
- lrwxrwxrwx root root 2014-01-02 16:51 rtc0 -> ../../devices/ff660000.i2c/i2c-2/2-0051/rtc/rtc0
咱們系統中只有一個RTC,因此編號爲rtc0。
同時發現rtc0文件爲指向/sys/devices/ff660000.i2c/i2c-2/2-0051/rtc/rtc0的符號連接,RTC芯片是I2C接口,因此rtc0掛載在I2C的總線上,總線控制器地址ff660000,控制器編號爲2,RTC芯片做爲slave端地址爲0x51。
rtc0設備屬性:
[objc] view plain copy
- void __init rtc_sysfs_init(struct classclass *rtc_class)
- {
- rtc_class->dev_attrs = rtc_attrs;
- }
- static struct device_attribute rtc_attrs[] = {
- __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),
- __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),
- __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),
- __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),
- __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,
- rtc_sysfs_set_max_user_freq),
- __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),
- { },
- };
查看之:
[python] view plain copy
- ls -l /sys/class/rtc/rtc0/
- -r--r--r-- root root 4096 2014-01-02 16:51 date
- -r--r--r-- root root 4096 2014-01-02 16:51 dev
- lrwxrwxrwx root root 2014-01-02 16:51 device -> ../../../2-0051
- -r--r--r-- root root 4096 2014-01-02 16:51 hctosys
- -rw-r--r-- root root 4096 2014-01-02 16:51 max_user_freq
- -r--r--r-- root root 4096 2014-01-02 16:51 name
- drwxr-xr-x root root 2014-01-02 16:48 power
- -r--r--r-- root root 4096 2014-01-02 16:51 since_epoch
- lrwxrwxrwx root root 2014-01-02 16:51 subsystem -> ../../../../../../class/rtc
- -r--r--r-- root root 4096 2014-01-02 16:51 time
- -rw-r--r-- root root 4096 2014-01-02 16:48 uevent
- -rw-r--r-- root root 4096 2014-01-02 16:51 wakealarm
二、rtc在proc
以前曾rtc0設備加入到了/proc
[python] view plain copy
- rtc_device_register
- --->rtc_proc_add_device(rtc);
-
- void rtc_proc_add_device(struct rtc_device *rtc)
- {
- if (is_rtc_hctosys(rtc))
- proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);
- }
查看之:
[python] view plain copy
- # cat /proc/driver/rtc
- rtc_time : 17:19:53
- rtc_date : 2014-01-02
- alrm_time : 00:00:00
- alrm_date : 1970-01-01
- alarm_IRQ : no
- alrm_pending : no
- update IRQ enabled : no
- periodic IRQ enabled : no
- periodic IRQ frequency : 1
- max user IRQ frequency : 64
- 24hr : yes
信息來源:
[python] view plain copy
- rtc_proc_fops
- -->rtc_proc_open
- -->rtc_proc_show
PS:
關於late_initcall
kernel中__init類型函數都位於.init.text段中,對應的在.initcall.init段中保存相應的函數指針。系統在啓動過程當中,根據定義在段中的等級值(0~7)從低到高依次執行。定義:
[objc] view plain copy
- init.h (include\linux)
-
- #define pure_initcall(fn) __define_initcall(fn, 0)
-
- #define core_initcall(fn) __define_initcall(fn, 1)
- #define core_initcall_sync(fn) __define_initcall(fn, 1s)
- #define postcore_initcall(fn) __define_initcall(fn, 2)
- #define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
- #define arch_initcall(fn) __define_initcall(fn, 3)
- #define arch_initcall_sync(fn) __define_initcall(fn, 3s)
- #define subsys_initcall(fn) __define_initcall(fn, 4)
- #define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
- #define fs_initcall(fn) __define_initcall(fn, 5)
- #define fs_initcall_sync(fn) __define_initcall(fn, 5s)
- #define rootfs_initcall(fn) __define_initcall(fn, rootfs)
- #define device_initcall(fn) __define_initcall(fn, 6)
- #define device_initcall_sync(fn) __define_initcall(fn, 6s)
- #define late_initcall(fn) __define_initcall(fn, 7)
- #define late_initcall_sync(fn) __define_initcall(fn, 7s)
http://blog.csdn.net/u013686019/article/details/57126940
版權聲明:本文爲博主原創文章,未經博主容許不得轉載。 https://blog.csdn.net/u013686019/article/details/57126940
我的分類: Linux內核