代碼講解及運行特效請參看視頻:
Linux kernel Hacker, 從零構(gòu)建自己的內(nèi)核
上一節(jié)纹腌,我們已經(jīng)實現(xiàn)了時鐘超時功能褪测,但一個操作系統(tǒng)砌溺,肯定是能支持多個時鐘的,本節(jié)尤勋,我們就看看如何從上一節(jié)的單時鐘向多時鐘轉(zhuǎn)變喘落,同時利用時鐘超時機制實現(xiàn)光標的閃爍特效。
上節(jié)最冰,我們的時鐘控制器對象只對應(yīng)于一個時鐘瘦棋,現(xiàn)在,我們要把有個時鐘的信息抽取出來暖哨,這樣的話赌朋,就使得一個控制器能對應(yīng)管理很多個時鐘對象,因此我們對timer.h 做如下改動:
#define PIT_CTRL 0x0043
#define PIT_CNT0 0x0040
#define MAX_TIMER 500
void init_pit(void);
struct TIMER {
unsigned int timeout, flags;
struct FIFO8 *fifo;
unsigned char data;
};
struct TIMERCTL {
unsigned int count;
struct TIMER timer[MAX_TIMER];
};
struct TIMER* timer_alloc(void);
void timer_free(struct TIMER *timer);
void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data) ;
void timer_settime(struct TIMER *timer, unsigned int timeout);
大家看到鹿蜀,原來在TIMERCTL中的一些信息已經(jīng)抽取出來箕慧,獨立成為一個TIMER對象,而在控制器里包含的是一組TIMER數(shù)組茴恰,這樣颠焦,一個控制器就能對應(yīng)很多個時鐘對象了。timer_alloc用來分配一個TIMER對象往枣,一個TIMER對象被分配出來后伐庭,需要用timer_init初始化,然后使用timer_settime設(shè)置時間片分冈。
我們看看相關(guān)函數(shù)的對應(yīng)實現(xiàn)圾另,在timer.c中:
struct TIMER* timer_alloc(void) {
int i;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == 0) {
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
return &timerctl.timer[i];
}
}
return 0;
}
void timer_free(struct TIMER *timer) {
timer->flags = 0;
return;
}
void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data) {
timer->fifo = fifo;
timer->data = data;
return;
}
void timer_settime(struct TIMER *timer, unsigned int timeout) {
timer->timeout = timeout;
timer->flags = TIMER_FLAGS_USING;
return;
}
TIMER對象的分配,其實就是從時鐘控制器的TIMER數(shù)組中找到一個還沒有被使用的對象雕沉,將其它的狀態(tài)從free 轉(zhuǎn)換為alloc, 也就是表明當前時鐘對象已經(jīng)被占用集乔,然后從數(shù)組中直接返回時鐘對象。
timer_settime 就是把時間片信息設(shè)置到對應(yīng)的TIMER對象而已坡椒。需要注意的是intHandlerForTimer函數(shù)的更改:
void intHandlerForTimer(char *esp) {
io_out8(PIC0_OCW2, 0x60);
timerctl.count++;
int i;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
timerctl.timer[i].timeout--;
if (timerctl.timer[i].timeout == 0) {
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
}
}
}
return;
}
每次時鐘中斷發(fā)生是扰路,我們的中斷處理函數(shù)會變量時鐘控制器中的時鐘數(shù)組尤溜,把數(shù)組中時鐘對象的時間片減一,如果有哪個時鐘的時間片已經(jīng)消耗完畢汗唱,那么就往對應(yīng)時鐘的數(shù)據(jù)隊列里寫入一個數(shù)據(jù)宫莱,用于通知內(nèi)核觸發(fā)對應(yīng)的超時操作。
有了多時鐘功能后哩罪,內(nèi)核就可以利用該機制授霸,創(chuàng)建多個時鐘,然后響應(yīng)多個超時事件了际插,內(nèi)核的改動如下碘耳,在write_vga_desktop.c中:
void CMain(void) {
.....
struct TIMER *timer, *timer2, *timer3;
static struct FIFO8 timerfifo2, timerfifo3;
static char timerbuf2[8], timerbuf3[8];
init_pit();
fifo8_init(&timerinfo, 8, timerbuf);
timer = timer_alloc();
timer_init(timer, &timerinfo, 1);
timer_settime(timer, 500);
fifo8_init(&timerfifo2, 8, timerbuf2);
timer2 = timer_alloc();
timer_init(timer2, &timerfifo2, 1);
timer_settime(timer2, 300);
fifo8_init(&timerfifo3, 8, timerbuf3);
timer3 = timer_alloc();
timer_init(timer3, &timerfifo3, 1);
timer_settime(timer3, 50);
....
}
我們看到,內(nèi)核入口函數(shù)申請了三個時鐘對象腹鹉,并將他們初始化藏畅,第一個時鐘的時間片是5秒,第二個時鐘的時間片是3秒功咒,第三個時鐘的時間片是0.5秒愉阎。有了三個時鐘,我們就需要對三個時鐘的超時做不同處理力奋。繼續(xù)看代碼:
void CMain(void) {
.....
for(;;) {
char* pStr = intToHexStr(timer->timeout);
boxfill8(shtMsgBox->buf, 160, COL8_C6C6C6, 40, 28, 119, 43);
showString(shtctl, shtMsgBox, 40, 28, COL8_000000,pStr);
io_cli();
if (fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) +
fifo8_status(&timerinfo) + fifo8_status(&timerfifo2)
+ fifo8_status(&timerfifo3) == 0) {
io_sti();
}
....
else if (fifo8_status(&timerinfo) != 0) {
io_sti();
fifo8_get(&timerinfo);
showString(shtctl, sht_back, 0, 0, COL8_FFFFFF, " new 5[sec]");
} else if (fifo8_status(&timerfifo2) != 0) {
fifo8_get(&timerfifo2);
io_sti();
showString(shtctl, sht_back, 0, 16, COL8_FFFFFF, "3[sec]");
}else if (fifo8_status(&timerfifo3) != 0) {
int i = fifo8_get(&timerfifo3);
io_sti();
if (i != 0) {
timer_init(timer3, &timerfifo3, 0);
boxfill8(buf_back, xsize, COL8_FFFFFF, 8, 96, 15, 111);
} else {
timer_init(timer3, &timerfifo3, 1);
boxfill8(buf_back, xsize, COL8_008484, 8, 96, 15, 111);
}
timer_settime(timer3, 50);
sheet_refresh(shtctl, sht_back, 8, 96, 16, 112);
}
....
}
第一個時鐘超時時榜旦,我們還是在桌面左上角顯示一個字符串,第二個時鐘超時時景殷,我們也同樣在左上角顯示一個字符串溅呢,對于第三個時鐘的處理,需要我們注意猿挚,在初始化第三個時鐘是咐旧,我們會傳入一個數(shù)值,也就是上一節(jié)描述的時鐘data, 當超時后绩蜻,這個數(shù)值會被放入回時鐘對應(yīng)的隊列里面铣墨,那么我們可以根據(jù)這個值采取不同的動作去處理超時。
如果第三個時鐘超時了办绝,同時在隊列里面的數(shù)值不等于0伊约,那么我們就在桌面上畫一個白色的小方塊,然后再次初始化該時鐘孕蝉,并把數(shù)值0傳進去屡律,下次時鐘再超時,那么內(nèi)核得到的數(shù)值就是0降淮,于是內(nèi)核把上次繪制的小方塊給擦掉超埋,這樣的話,就形成一個效果,就是一個白色小方塊在桌面上閃來閃去霍殴,就好像一個光標一樣窍蓝。
上面代碼編譯成內(nèi)核運行后,效果如下(動態(tài)效果請參看視頻):