本篇內(nèi)容主要講解“l(fā)inux的時(shí)間管理和定時(shí)器原理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“l(fā)inux的時(shí)間管理和定時(shí)器原理”吧!
創(chuàng)新互聯(lián)公司是一家專注于網(wǎng)站制作、成都網(wǎng)站制作與策劃設(shè)計(jì),邊壩網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)十年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:邊壩等地區(qū)。邊壩做網(wǎng)站價(jià)格咨詢:18982081108
linux初始化的時(shí)候,初始化了定時(shí)相關(guān)的代碼。
void sched_init(void)
{
...
// 43是控制字端口,0x36=0x00110110,即二進(jìn)制,方式3,先讀寫低8位再讀寫高8位,選擇計(jì)算器0
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
/*
寫入初始值,40端口是計(jì)數(shù)通道0,初始值
的含義是,8253每一個(gè)波動(dòng),初始值會(huì)減一,減到0則輸出一個(gè)通知,
LATCH = (1193180/100),1193180是8253的工作頻率,
一秒鐘波動(dòng)1193180次。(1193180/100)就是(1193180/1000)*10,即
(1193180/1000)減到0的時(shí)候,過去了1毫秒。乘以10即過去10毫秒。
*/
outb_p(LATCH & 0xff , 0x40); /* LSB */
// 再寫8位
outb(LATCH >> 8 , 0x40); /* MSB */
// 設(shè)置定時(shí)中斷處理函數(shù),中斷號(hào)是20,8253會(huì)觸發(fā)該中斷
set_intr_gate(0x20,&timer_interrupt);
...
}
_timer_interrupt:
push %ds # save ds,es and put kernel data space
push %es # into them. %fs is used by _system_call
push %fs
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't
pushl %ecx # save those across function calls. %ebx
pushl %ebx # is saved as we use that in ret_sys_call
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
incl _jiffies
movb $0x20,%al # EOI to interrupt controller #1
outb %al,$0x20
movl CS(%esp),%eax
andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)
pushl %eax
call _do_timer # 'do_timer(long CPL)' does everything from
addl $4,%esp # task switching to accounting ...
jmp ret_from_sys_call
我們看到中斷的時(shí)候執(zhí)行了do_timer函數(shù),該函數(shù)就是處理定時(shí)器和進(jìn)程調(diào)度的。在此之前我們先看看怎么新增一個(gè)定時(shí)器。
#define TIME_REQUESTS 64
// 定時(shí)器數(shù)組,其實(shí)是個(gè)鏈表
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
// 關(guān)中斷,防止多個(gè)進(jìn)程”同時(shí)“操作
cli();
// 直接到期,直接執(zhí)行回調(diào)
if (jiffies <= 0)
(fn)();
else {
// 遍歷定時(shí)器數(shù)組,找到一個(gè)空項(xiàng)
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
// 沒有空項(xiàng)了
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
// 給空項(xiàng)賦值
p->fn = fn;
p->jiffies = jiffies;
// 在數(shù)組中形成鏈表
p->next = next_timer;
// next_timer指向第一個(gè)節(jié)點(diǎn),即最早到期的
next_timer = p;
/*
修改鏈表,保證超時(shí)時(shí)間是從小到大的順序
原理:
每個(gè)節(jié)點(diǎn)都是以前面一個(gè)節(jié)點(diǎn)的到時(shí)時(shí)間為坐標(biāo),節(jié)點(diǎn)里的jiffies即超時(shí)時(shí)間
是前一個(gè)節(jié)點(diǎn)到期后的多少個(gè)jiffies后該節(jié)點(diǎn)到期。
*/
while (p->next && p->next->jiffies < p->jiffies) {
// 前面的節(jié)點(diǎn)比后面節(jié)點(diǎn)大,則前面節(jié)點(diǎn)減去后面節(jié)點(diǎn)的值,算出偏移值,下面準(zhǔn)備置換位置
p->jiffies -= p->next->jiffies;
// 先保存一下
fn = p->fn;
// 置換兩個(gè)節(jié)點(diǎn)的回調(diào)
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
// 置換兩個(gè)節(jié)點(diǎn)是超時(shí)時(shí)間
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
/*
到這,第一個(gè)節(jié)點(diǎn)是最快到期的,還需要更新后續(xù)節(jié)點(diǎn)的值,其實(shí)就是找到一個(gè)合適的位置
插入,因?yàn)閮?nèi)核是用數(shù)組實(shí)現(xiàn)的定時(shí)器隊(duì)列,所以是通過置換位置實(shí)現(xiàn)插入,
如果是鏈表,則直接找到合適的位置,插入即可,所謂合適的位置,
就是找到第一個(gè)比當(dāng)前節(jié)點(diǎn)大的節(jié)點(diǎn),插入到他前面。
*/
p = p->next;
}
/*
內(nèi)核這里實(shí)現(xiàn)有個(gè)bug,當(dāng)當(dāng)前節(jié)點(diǎn)是最小時(shí),需要更新原鏈表中第一個(gè)節(jié)點(diǎn)的值,,
否則會(huì)導(dǎo)致原鏈表中第一個(gè)節(jié)點(diǎn)的過期時(shí)間延長(zhǎng),修復(fù)代碼如下:
if (p->next && p->next->jiffies > p->jiffies) {
p->next->jiffies = p->next->jiffies - p->jiffies;
}
即更新原鏈表中第一個(gè)節(jié)點(diǎn)相對(duì)于新的第一個(gè)節(jié)點(diǎn)的偏移,剩余的節(jié)點(diǎn)不需要更新,因?yàn)樗鄬?duì)于
他前面的節(jié)點(diǎn)的偏移不變,但是原鏈表中的第一個(gè)節(jié)點(diǎn)之前前面沒有節(jié)點(diǎn),所以偏移就是他自己的值,
而現(xiàn)在在他前面插入了一個(gè)節(jié)點(diǎn),則他的偏移是相對(duì)于前面一個(gè)節(jié)點(diǎn)的偏移
*/
}
sti();
}
上面是示例圖。這樣就完成了定時(shí)節(jié)點(diǎn)的插入。我們?cè)倩仡^看一下do_timer的代碼,即系統(tǒng)由定時(shí)中斷時(shí)執(zhí)行的代碼。
void do_timer(long cpl)
{
...
// 當(dāng)前在用戶態(tài),增加用戶態(tài)的執(zhí)行時(shí)間,否則增加該進(jìn)程的系統(tǒng)執(zhí)行時(shí)間
if (cpl)
current->utime++;
else
current->stime++;
// next_timer為空說明還沒有定時(shí)節(jié)點(diǎn)
if (next_timer) {
// 第一個(gè)節(jié)點(diǎn)減去一個(gè)jiffies,因?yàn)槠渌?jié)點(diǎn)都是相對(duì)第一個(gè)節(jié)點(diǎn)的偏移,所以其他節(jié)點(diǎn)的值不需要變
next_timer->jiffies--;
// 當(dāng)前節(jié)點(diǎn)到期,如果有多個(gè)節(jié)點(diǎn)超時(shí)時(shí)間一樣,即相對(duì)第一個(gè)節(jié)點(diǎn)偏移是0,則會(huì)多次進(jìn)入while循環(huán)
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
...
// 進(jìn)程調(diào)度
schedule();
}
我們發(fā)現(xiàn),add_timer的時(shí)候已經(jīng)算好了定時(shí)器的順序是從先到期到后到期的,并且后面的節(jié)點(diǎn)是相對(duì)前面節(jié)點(diǎn)的偏移,所以判斷超時(shí)的時(shí)候,只需要從前往后判斷,如果第一節(jié)點(diǎn)沒到期則后面的也不會(huì)到期,如果第一個(gè)到期,則繼續(xù)判斷下一個(gè)節(jié)點(diǎn)。把耗時(shí)的操作放到新增節(jié)點(diǎn)的時(shí)候,因?yàn)樾略龆ú僮魇呛苌偾也活l繁的,但是定時(shí)中斷是頻繁的操作。所以這樣就保證了性能。最后我們還發(fā)現(xiàn),操作系統(tǒng)就是在這里通過schedule函數(shù)處理進(jìn)程調(diào)度的。即沒10毫秒,系統(tǒng)進(jìn)程一次進(jìn)程調(diào)度。
到此,相信大家對(duì)“l(fā)inux的時(shí)間管理和定時(shí)器原理”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!