1. 程式人生 > >(萊昂氏unix原始碼分析導讀-21)時鐘中斷處理

(萊昂氏unix原始碼分析導讀-21)時鐘中斷處理

時鐘中斷是系統中最重要的中斷,每個時鐘滴答都會產生時鐘中斷,它的中斷向量為(0100)(0103)

0533: . = 100^.

0534:       kwlp; br6

0535:       kwlp; br6

0569: .globl  _clock

0570: kwlp:  jsr r0,call; _clock

顯然,時鐘中斷會通過call例程呼叫_clock函式,對clock函式,11.1小節有詳細的介紹。

我在這裡只談幾個問題:

1.引數

clock(dev, sp, r1, nps, r0, pc, ps)7個引數,想要逐一瞭解他們,請回頭看看上面的棧圖;

2.時鐘中斷是典型的“多用途”中斷,它需要進行多個層次的處理。實際上,clock

函式對時間進行

    3個維度的處理:

1)時鐘滴答

修改p_cpu值,對於大多數程序來說,p_cpu就是佔用的cpu滴答數,但如果程序執行時間較長的話,

      p_cpu的值會有一個迴歸——其作用是防止程序的優先順序變的太低而導致餓死。

檢查定時器,看是否需要執行操作。

2)秒

修改u.stimeu.utimetime[ ]等以秒為單位的計時器。

甚至還有以4s為單位的系統程序喚醒。

3)時間片

此版本的unix正好使用1秒作為一個時間片,故與秒的處理髮生了重疊。

3Clock函式的各項工作的“Priority

顯然,在clock函式的多項工作中,優先順序是不同的,而且各項工作對於時延的容忍程度是不同的。

   對秒級、時間片級的工作——即使是推遲幾個時鐘滴答再處理對整體的影響也不大。

於是,clock函式內對低優先順序、高耗時的工作進行了控制——會進行這樣的檢查:

if((ps&0340) != 0) return;  /by pass following codes

即只有在“前ps”的cpu優先順序為0時,才會從事下面那些工作。而一般情況下,

(1)         usercpu優先順序為0

(2)         kernel態時,程式呼叫spl0將自己設定為優先順序0

受到控制的工作有:

(1)         時間片——重設程序優先順序的工作;

(2)         計時器處理

(3)         Etc

4.時間片處理

按我所學習的作業系統理論,時間片應該是很重要的程序切換的時機。但很遺憾,clock()函式中僅僅呼叫setpri()

重新設定了程序的priority,並沒有直接呼叫swtch()切換程序。

但是,且慢……

(1)         clock()函式返回到call例程後,會有機會切換程序的,關鍵在runrun是否設定;

(2)         setpri()函式是可以增加runrun計數的——還記得嗎,那個詭異的判斷?

2165:    if(p > curpri)

2166:       runrun++;

讓我們仔細考察一下clock()setpri()

對大多數的程序,clock()會以更高的p_cpu呼叫setpri()函式,這樣會降低程序的優先順序,顯然,對active

程序也是如此。而這正好會使2165那個判斷成真——即增加runrun計數,也就引起了後面的程序切換。

【注】:對於2165謎題,希望讀者能給出更好的答案。

5time陣列

time[0]time[1]組成,記錄自1970年以來所經過的秒數。它使用216bit整數模擬132位整數,

     time[0]為高16bittime[1]為低16bit。因此才有以下的演算法:

3801: if(++time[1] == 0)

3802: ++time[0];

6.定時器

   callout陣列記錄了要執行的定時任務。

任務按照執行先後順序排列,其c_time意義如下:

(1)         callout[0].c_time ——“絕對時鐘滴答數”,再過這麼多滴答,即執行函式;

(2)         callout[n] .c_time (n>0)——相對於callout[n-1]的相對時鐘滴答數

7tout的叫醒服務

顯然,tout[ ]內記錄的是下一個要被wakeup的時間,當與當前時間(time[])相同時,

  會呼叫wakeup(tout)喚醒程序——顯然,睡眠的程序使用tout[ ]陣列首地址作為睡眠id

萊昂針對tout的實現提出了自己的看法,但這部分涉及到system call,我們會在下面的章節進行討論。

注意,我們將swap的部分留在後面討論。