成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

原來操作系統獲取時間的方式也這么 low

系統
書接上回,上回書咱們說到,通過初始化控制臺的 tty_init 操作,內核代碼可以很方便地在控制臺輸出字符啦!

書接上回,上回書咱們說到,通過初始化控制臺的 tty_init 操作,內核代碼可以很方便地在控制臺輸出字符啦!

作為用戶也可以通過敲擊鍵盤,或調用諸如 printf 這樣的庫函數,在屏幕上輸出信息,同時支持換行和滾屏等友好設計,這些都是 tty_init 初始化,以及其對外封裝的小功能函數,來實現的。

我們繼續往下看下一個初始化的倒霉鬼,time_init。

  1. void main(void) { 
  2.     ... 
  3.     mem_init(main_memory_start,memory_end); 
  4.     trap_init(); 
  5.     blk_dev_init(); 
  6.     chr_dev_init(); 
  7.     tty_init(); 
  8.     time_init(); 
  9.     sched_init(); 
  10.     buffer_init(buffer_memory_end); 
  11.     hd_init(); 
  12.     floppy_init(); 
  13.      
  14.     sti(); 
  15.     move_to_user_mode(); 
  16.     if (!fork()) {init();} 
  17.     for(;;) pause(); 

曾經我很好奇,操作系統是怎么獲取到當前時間的呢?

當然,現在都聯網了,可以從網絡上實時同步。那當沒有網絡時,為什么操作系統在啟動之后,可以顯示出當前時間呢?難道操作系統在電腦關機后,依然不停地在某處運行著,勤勤懇懇數著秒表么?

當然不是,那我們今天就打開這個 time_init 函數一探究竟。

打開這個函數后我又是很開心,因為很短,且沒有更深入的方法調用。

  1. #define CMOS_READ(addr) ({ \ 
  2.     outb_p(0x80|addr,0x70); \ 
  3.     inb_p(0x71); \ 
  4. }) 
  5.  
  6. #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) 
  7.  
  8. static void time_init(void) { 
  9.     struct tm time
  10.     do { 
  11.         time.tm_sec = CMOS_READ(0); 
  12.         time.tm_min = CMOS_READ(2); 
  13.         time.tm_hour = CMOS_READ(4); 
  14.         time.tm_mday = CMOS_READ(7); 
  15.         time.tm_mon = CMOS_READ(8); 
  16.         time.tm_year = CMOS_READ(9); 
  17.     } while (time.tm_sec != CMOS_READ(0)); 
  18.     BCD_TO_BIN(time.tm_sec); 
  19.     BCD_TO_BIN(time.tm_min); 
  20.     BCD_TO_BIN(time.tm_hour); 
  21.     BCD_TO_BIN(time.tm_mday); 
  22.     BCD_TO_BIN(time.tm_mon); 
  23.     BCD_TO_BIN(time.tm_year); 
  24.     time.tm_mon--; 
  25.     startup_time = kernel_mktime(&time); 

夢想的代碼呀!

那主要就是對 CMOS_READ 和 BCD_TO_BIN 都是啥意思展開講一下就明白了了。

首先是 CMOS_READ

  1. #define CMOS_READ(addr) ({ \ 
  2.     outb_p(0x80|addr,0x70); \ 
  3.     inb_p(0x71); \ 
  4. }) 

就是對一個端口先 out 寫一下,再 in 讀一下。

這是 CPU 與外設交互的一個基本玩法,CPU 與外設打交道基本是通過端口,往某些端口寫值來表示要這個外設干嘛,然后從另一些端口讀值來接受外設的反饋。

至于這個外設內部是怎么實現的,對使用它的操作系統而言,是個黑盒,無需關心。那對于我們程序員來說,就更不用關心了。

對 CMOS 這個外設的交互講起來可能沒感覺,我們看看與硬盤的交互。

最常見的就是讀硬盤了,我們看硬盤的端口表。

 


 

端口


0x1F0

數據寄存器 數據寄存器

0x1F1

錯誤寄存器 特征寄存器

0x1F2

扇區計數寄存器 扇區計數寄存器

0x1F3

扇區號寄存器或 LBA 塊地址 0~7 扇區號或 LBA 塊地址 0~7
0x1F4
磁道數低 8 位或 LBA 塊地址 8~15 磁道數低 8 位或 LBA 塊地址 8~15
0x1F5
磁道數高 8 位或 LBA 塊地址 16~23 磁道數高 8 位或 LBA 塊地址 16~23
0x1F6
驅動器/磁頭或 LBA 塊地址 24~27 驅動器/磁頭或 LBA 塊地址 24~27
0x1F7
命令寄存器或狀態寄存器 命令寄存器

那讀硬盤就是,往除了第一個以外的后面幾個端口寫數據,告訴要讀硬盤的哪個扇區,讀多少。然后再從 0x1F0 端口一個字節一個字節的讀數據。這就完成了一次硬盤讀操作。

如果覺得不夠具體,那來個具體的版本。

  • 在 0x1F2 寫入要讀取的扇區數
  • 在 0x1F3 ~ 0x1F6 這四個端口寫入計算好的起始 LBA 地址
  • 在 0x1F7 處寫入讀命令的指令號
  • 不斷檢測 0x1F7 (此時已成為狀態寄存器的含義)的忙位
  • 如果第四步驟為不忙,則開始不斷從 0x1F0 處讀取數據到內存指定位置,直到讀完

看,是不是對 CPU 最底層是如何與外設打交道有點感覺了?是不是也不難?就是按照人家的操作手冊,然后無腦按照要求讀寫端口就行了。

當然,讀取硬盤的這個無腦循環,可以 CPU 直接讀取并做寫入內存的操作,這樣就會占用 CPU 的計算資源。

也可以交給 DMA 設備去讀,解放 CPU,但和硬盤的交互,通通都是按照硬件手冊上的端口說明,來操作的,實際上也是做了一層封裝。

好了,我們已經學會了和一個外設打交道的基本玩法了。

那我們代碼中要打交道的是哪個外設呢?就是 CMOS。

它是主板上的一個可讀寫的 RAM 芯片,你在開機時長按某個鍵就可以進入設置它的頁面。

那我們的代碼,其實就是與它打交道,獲取它的一些數據而已。

我們回過頭看代碼。

  1. static void time_init(void) { 
  2.     struct tm time
  3.     do { 
  4.         time.tm_sec = CMOS_READ(0); 
  5.         time.tm_min = CMOS_READ(2); 
  6.         time.tm_hour = CMOS_READ(4); 
  7.         time.tm_mday = CMOS_READ(7); 
  8.         time.tm_mon = CMOS_READ(8); 
  9.         time.tm_year = CMOS_READ(9); 
  10.     } while (time.tm_sec != CMOS_READ(0)); 
  11.     BCD_TO_BIN(time.tm_sec); 
  12.     BCD_TO_BIN(time.tm_min); 
  13.     BCD_TO_BIN(time.tm_hour); 
  14.     BCD_TO_BIN(time.tm_mday); 
  15.     BCD_TO_BIN(time.tm_mon); 
  16.     BCD_TO_BIN(time.tm_year); 
  17.     time.tm_mon--; 
  18.     startup_time = kernel_mktime(&time); 

前面幾個賦值語句 CMOS_READ 就是通過讀寫 CMOS 上的指定端口,依次獲取年月日時分秒等信息。具體咋操作代碼上也寫了,也是按照 CMOS 手冊要求的讀寫指定端口就行了,我們就不展開了。

所以你看,其實操作系統程序,也是要依靠與一個外部設備打交道,來獲取這些信息的,并不是它自己有什么魔力。操作系統最大的魅力,就在于它借力完成了一項偉大的事,借 CPU 的力,借硬盤的力,借內存的力,以及現在借 CMOS 的力。

至于 CMOS 又是如何知道時間的,這個就不在我們討論范圍了。

接下來 BCD_TO_BIN 就是 BCD 轉換成 BIN,因為從 CMOS 上獲取的這些年月日都是 BCD 碼值,需要轉換成存儲在我們變量上的二進制數值,所以需要一個小算法來轉換一下,沒什么意思。

最后一步 kernel_mktime 也很簡單,就是根據剛剛的那些時分秒數據,計算從 1970 年 1 月 1 日 0 時起到開機當時經過的秒數,作為開機時間,存儲在 startup_time 這個變量里。

想研究可以仔細看看這段代碼,不過我覺得這種細節不必看。

  1. startup_time = kernel_mktime(&time); 
  2.  
  3. // kernel/mktime.c 
  4. long kernel_mktime(struct tm * tm) 
  5.     long res; 
  6.     int year
  7.     year = tm->tm_year - 70; 
  8.     res = YEAR*year + DAY*((year+1)/4); 
  9.     res += month[tm->tm_mon]; 
  10.     if (tm->tm_mon>1 && ((year+2)%4)) 
  11.         res -= DAY
  12.     res += DAY*(tm->tm_mday-1); 
  13.     res += HOUR*tm->tm_hour; 
  14.     res += MINUTE*tm->tm_min; 
  15.     res += tm->tm_sec; 
  16.     return res; 

就這。

所以今天其實就是,計算出了一個 startup_time 變量而已,至于這個變量今后會被誰用,怎么用,那就是后話了。

相信你逐漸也體會到了,此時操作系統好多地方都是用外設要求的方式去詢問,比如硬盤信息、顯示模式,以及今天的開機時間的獲取等。

所以至少到目前來說,你還不應該感覺操作系統有多么的“高端”,很多時候都是繁瑣地,讀人家的硬件手冊,獲取到想要的的信息,拿來給自己用,或者對其進行各種設置。

但你一定要耐得住寂寞,真正體現操作系統的強大設計之處,還得接著往下讀。

欲知后事如何,且聽下回分解。

本文轉載自微信公眾號「低并發編程」,可以通過以下二維碼關注。轉載本文請聯系低并發編程公眾號。本網站已獲得低并發編程的授權。

 

責任編輯:武曉燕 來源: 低并發編程
相關推薦

2010-08-02 13:55:20

2010-04-22 14:18:42

Aix操作系統

2018-08-09 16:12:59

操作系統內存分配

2019-03-14 09:29:02

Linux系統內存

2010-02-26 14:13:51

Linux操作系統

2010-04-15 14:40:26

Unix操作系統

2010-04-28 18:59:59

Unix操作系統

2010-03-04 14:34:50

Linux操作系統

2009-12-25 17:05:50

Linux操作系統

2010-04-30 16:02:45

Unix操作系統

2009-12-09 17:25:19

Linux操作系統

2021-04-19 11:23:29

操作系統計算機DOS

2009-07-23 18:43:25

操作系統LinuxWindows

2015-04-30 08:00:05

數據中心多種操作系統

2021-04-19 05:42:51

Mmap文件系統

2023-05-07 23:22:24

golang

2009-12-02 17:01:05

路由器功能

2021-11-15 06:56:46

操作系統U盤

2012-03-30 14:43:23

2022-01-07 13:34:25

Java時間格式化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美精品免费观看二区 | 亚洲免费在线观看 | 一区二区三区高清不卡 | 在线色网址 | 婷婷色国产偷v国产偷v小说 | 亚洲国产精品福利 | 华丽的挑战在线观看 | 国产一区二区不卡 | 国产精品中文字幕在线 | a久久 | 成人在线免费看 | 午夜www| 久久av网站| 欧美精品久久久久 | 精品蜜桃一区二区三区 | 亚洲精选久久 | 亚洲人成网亚洲欧洲无码 | 免费黄色网址视频 | 精品一区二区三区中文字幕 | 日韩一区二区三区视频 | 九九综合九九 | a级性视频 | 亚洲精品视频在线播放 | 欧美久久久 | 成人在线播放 | 亚州影院 | 91精品在线播放 | 亚洲欧美日韩在线 | 亚洲a毛片 | 亚洲精品久久久蜜桃 | 亚洲一区二区在线免费观看 | 欧美日韩三级在线观看 | 一区二区播放 | 欧美黄 片免费观看 | 一区二区三区在线播放 | 国产伦精品一区二区三区照片91 | 日本三级电影在线看 | 欧美日韩在线视频一区 | 福利片在线观看 | 国产成人综合久久 | 99pao成人国产永久免费视频 |