第17回 | 原來操作系統(tǒng)獲取時(shí)間的方式也這么 low
新讀者看這里,老讀者直接跳過。
本系列會(huì)以一個(gè)讀小說的心態(tài),從開機(jī)啟動(dòng)后的代碼執(zhí)行順序,帶著大家閱讀和賞析 Linux 0.11 全部核心代碼,了解操作系統(tǒng)的技術(shù)細(xì)節(jié)和設(shè)計(jì)思想。

你會(huì)跟著我一起,看著一個(gè)操作系統(tǒng)從啥都沒有開始,一步一步最終實(shí)現(xiàn)它復(fù)雜又精巧的設(shè)計(jì),讀完這個(gè)系列后希望你能發(fā)出感嘆,原來操作系統(tǒng)源碼就是這破玩意。
以下是已發(fā)布文章的列表,詳細(xì)了解本系列可以先從開篇詞看起。
第一部分 進(jìn)入內(nèi)核前的苦力活
第二部分 大戰(zhàn)前期的初始化工作
第12回 | 管理內(nèi)存前先劃分出三個(gè)邊界值
第15回 | 塊設(shè)備請(qǐng)求項(xiàng)初始化 blk_dev_init
本系列的 GitHub 地址如下(文末閱讀原文可直接跳轉(zhuǎn))
https://github.com/sunym1993/flash-linux0.11-talk
------- 正文開始?-------

void?main(void)?{
????...
????mem_init(main_memory_start,memory_end);
????trap_init();
????blk_dev_init();
????chr_dev_init();
????tty_init();
????time_init();
????sched_init();
????buffer_init(buffer_memory_end);
????hd_init();
????floppy_init();
????
????sti();
????move_to_user_mode();
????if?(!fork())?{init();}
????for(;;)?pause();
}
#define?CMOS_READ(addr)?({?\
????outb_p(0x80|addr,0x70);?\
????inb_p(0x71);?\
})
#define?BCD_TO_BIN(val)?((val)=((val)&15)?+?((val)>>4)*10)
static?void?time_init(void)?{
????struct?tm?time;
????do?{
????????time.tm_sec?=?CMOS_READ(0);
????????time.tm_min?=?CMOS_READ(2);
????????time.tm_hour?=?CMOS_READ(4);
????????time.tm_mday?=?CMOS_READ(7);
????????time.tm_mon?=?CMOS_READ(8);
????????time.tm_year?=?CMOS_READ(9);
????}?while?(time.tm_sec?!=?CMOS_READ(0));
????BCD_TO_BIN(time.tm_sec);
????BCD_TO_BIN(time.tm_min);
????BCD_TO_BIN(time.tm_hour);
????BCD_TO_BIN(time.tm_mday);
????BCD_TO_BIN(time.tm_mon);
????BCD_TO_BIN(time.tm_year);
????time.tm_mon--;
????startup_time?=?kernel_mktime(&time);
}
#define?CMOS_READ(addr)?({?\
????outb_p(0x80|addr,0x70);?\
????inb_p(0x71);?\
})
| 端口 | 讀 | 寫 |
|---|---|---|
0x1F0 | 數(shù)據(jù)寄存器 | 數(shù)據(jù)寄存器 |
0x1F1 | 錯(cuò)誤寄存器 | 特征寄存器 |
0x1F2 | 扇區(qū)計(jì)數(shù)寄存器 | 扇區(qū)計(jì)數(shù)寄存器 |
0x1F3 | 扇區(qū)號(hào)寄存器或 LBA 塊地址 0~7 | 扇區(qū)號(hào)或 LBA 塊地址 0~7 |
| 0x1F4 | 磁道數(shù)低 8 位或 LBA 塊地址 8~15 | 磁道數(shù)低 8 位或 LBA 塊地址 8~15 |
| 0x1F5 | 磁道數(shù)高 8 位或 LBA 塊地址 16~23 | 磁道數(shù)高 8 位或 LBA 塊地址 16~23 |
| 0x1F6 | 驅(qū)動(dòng)器/磁頭或 LBA 塊地址 24~27 | 驅(qū)動(dòng)器/磁頭或 LBA 塊地址 24~27 |
| 0x1F7 | 命令寄存器或狀態(tài)寄存器 | 命令寄存器 |
在 0x1F2 寫入要讀取的扇區(qū)數(shù)
在 0x1F3 ~ 0x1F6 這四個(gè)端口寫入計(jì)算好的起始 LBA 地址
在 0x1F7 處寫入讀命令的指令號(hào)
不斷檢測(cè) 0x1F7 (此時(shí)已成為狀態(tài)寄存器的含義)的忙位
如果第四步驟為不忙,則開始不斷從 0x1F0 處讀取數(shù)據(jù)到內(nèi)存指定位置,直到讀完

static?void?time_init(void)?{
????struct?tm?time;
????do?{
????????time.tm_sec?=?CMOS_READ(0);
????????time.tm_min?=?CMOS_READ(2);
????????time.tm_hour?=?CMOS_READ(4);
????????time.tm_mday?=?CMOS_READ(7);
????????time.tm_mon?=?CMOS_READ(8);
????????time.tm_year?=?CMOS_READ(9);
????}?while?(time.tm_sec?!=?CMOS_READ(0));
????BCD_TO_BIN(time.tm_sec);
????BCD_TO_BIN(time.tm_min);
????BCD_TO_BIN(time.tm_hour);
????BCD_TO_BIN(time.tm_mday);
????BCD_TO_BIN(time.tm_mon);
????BCD_TO_BIN(time.tm_year);
????time.tm_mon--;
????startup_time?=?kernel_mktime(&time);
}
startup_time?=?kernel_mktime(&time);
//?kernel/mktime.c
long?kernel_mktime(struct?tm?*?tm)
{
????long?res;
????int?year;
????year?=?tm->tm_year?-?70;
????res?=?YEAR*year?+?DAY*((year+1)/4);
????res?+=?month[tm->tm_mon];
????if?(tm->tm_mon>1?&&?((year+2)%4))
????????res?-=?DAY;
????res?+=?DAY*(tm->tm_mday-1);
????res?+=?HOUR*tm->tm_hour;
????res?+=?MINUTE*tm->tm_min;
????res?+=?tm->tm_sec;
????return?res;
}
所以至少到目前來說,你還不應(yīng)該感覺操作系統(tǒng)有多么的“高端”,很多時(shí)候都是繁瑣地,讀人家的硬件手冊(cè),獲取到想要的的信息,拿來給自己用,或者對(duì)其進(jìn)行各種設(shè)置。
?
但你一定要耐得住寂寞,真正體現(xiàn)操作系統(tǒng)的強(qiáng)大設(shè)計(jì)之處,還得接著往下讀。
?
欲知后事如何,且聽下回分解。
------- 關(guān)于本系列?-------
本系列的開篇詞看這
本系列的擴(kuò)展資料看這(也可點(diǎn)擊閱讀原文),這里有很多有趣的資料、答疑、互動(dòng)參與項(xiàng)目,持續(xù)更新中,希望有你的參與。
https://github.com/sunym1993/flash-linux0.11-talk
本系列全局視角

最后,祝大家都能追更到系列結(jié)束,只要你敢持續(xù)追更,并且把每一回的內(nèi)容搞懂,我就敢讓你在系列結(jié)束后說一句,我對(duì) Linux 0.11 很熟悉。
公眾號(hào)更新系列文章不易,閱讀量越來越低,希望大家多多傳播,不方便的話點(diǎn)個(gè)小小的贊我也會(huì)很開心,謝謝大家咯。
另外,本系列完全免費(fèi),希望大家能多多傳播給同樣喜歡的人,同時(shí)給我的 GitHub 項(xiàng)目點(diǎn)個(gè) star,就在閱讀原文處,這些就足夠讓我堅(jiān)持寫下去了!我們下回見。
