為什麼會出現程序這個概念
程序切換會產生什麼影響
哪些狀態需要被保護呢
如何儲存這些狀態呢
如何排程程序
時鐘中斷
中斷重入
如果沒有程序這個概念,程式是如何執行的呢?那只能執行乙個程式,執行完乙個程式,才能接著執行下乙個。哪怕上個程式中間要等待某件事十天半個月,也是沒有辦法的事情了。
如果有程序這個概念呢?那麼作業系統看到有個程序閒了,就不把cpu分給它了,這就是程序切換的概念。這樣cpu利用率就上來了。此時還要考慮乙個情況,要是乙個程序一直忙,難道其他程序就只能在旁邊等著嗎?又規定每個程序最多能連續使用多久的cpu時間,到時間必須讓給別人,這就是所謂的程序時間片。
而由於程序的出現,可能會引發這樣乙個問題,某乙個程序崩潰了,大家都一起玩完了。為了避免這樣的情況出現,intel引入了保護模式的概念。乙個程序掛了,給作業系統乙個機會,清除這個程序,而不是和它一起滅亡。
在保護模式,intel規定了不同的特權級。linux系統用0和3兩個特權級。關於特權級,見保護模式下處理器特性那篇文章。咱們這個實踐過程中用了三個特權級,0、1、3,比linux多用了乙個1特權級,這是因為作者用的微核心,對於一些系統的功能模組給了1這個特權級,比核心低,比使用者程序高。
不同的程序執行的是不同的**,同乙個程序不同的特權級這兩個問題又帶來了堆疊切換的問題。因為不同的特權級、不同的程序,如果共用乙個堆疊,誰也不能保證再切回去的時候堆疊沒有被破壞。所以我們必須保護好切換前的執行環境。
哪些狀態需要被保護呢。
上帝的歸上帝,撒旦的歸撒旦。程序的當然歸程序自己了。
又需要乙個賬本了:
記錄每個程序還剩多少時間
記錄程序此時用到的各個暫存器,比如說eip,esp這些比較重要的資料,等下次切換回來接著用
再就是聯絡上檔案系統,還有開啟的檔案描述符陣列
還有保護模式下ldt的選擇子
還有自己的狀態
程序自己的**段和資料段描述符
這些就完了嗎?肯定不可能一次考慮周全,只能隨著工作的推進,我們的考慮才能越來越完善。
那麼這些東西儲存在**呢?如果隨便儲存在乙個地方,系統還需要額外做個記錄,既然每個程序都獨有乙份不同的記錄,並且所需的空間是固定的,不會一會兒大一會小。這樣的話,最好就是放在程序表裡,既免去了系統要做的額外記錄,而且程序本身如果要對這些值做修改是非常方便的(例如exec載入乙個新的映象)。
儲存的地方有了,似乎就應該可以切換了。來看看有幾種切換方式。
從核心態切換到使用者態
從乙個程序的核心態切換到另乙個程序的核心態
從使用者態切到核心態
前兩種狀態比較好處理,都是按照儲存在程序表中的暫存器順序依次彈出即可。
單單是從使用者態到核心態有點麻煩,因為使用者態的時候你不能訪問核心態的資料,那麼你怎麼會知道你的程序表在**呢?那麼使用者態的執行環境就沒法儲存了。
intel從硬體上給予了支援。在從使用者態切換到核心態的時候需要用到tss段了。當因為某些訊號從使用者態切換到核心態時候,cpu會把ss、esp暫時儲存在cpu中,然後從tr暫存器中拿到選擇子,從gdt中得到tss的段基位址,然後將tss.ss0給ss暫存器,tss.esp0給esp暫存器,這兩個值就相當於從使用者態切換到核心態時候的堆疊指標。再把剛才儲存的ss、esp加上eflag、cs、eip放到esp0指向的棧上。
這就要求程序在離開核心態回到使用者態的時候,必須要把tss段中的ss0和esp0的值設定為自己程序表中儲存使用者執行環境的起始位址。
保護程序的現場,好像又引出了不少東西。gdt、ldt、tss。其實它們幾個都是記憶體中的一張表。網上都有說明。
說一下以前自己容易忽略的ldt定址的細節。
當ti=1時表示段描述符在ldt中:
還是先從gdtr暫存器中獲得gdt基址。
從ldtr暫存器中獲取ldt所在段的位置索引(ldtr高13位)。而ldtr的內容是用lldt載入的,這個時候就清楚了**中從核心返回的時候為什麼要載入程序各自的ldt選擇子了。
以這個位置索引在gdt中得到ldt段描述符從而得到ldt段基址。ldt表是作為乙個段存在的。從這可以看出來gdt是總目錄,大家都要從要作為起點來定址。
用段選擇器高13位位置索引值從ldt段中得到段描述符。(此時是段選擇子,一般是cs)
段描述符符包含段的基址、限長、優先順序等各種屬性,這就得到了段的起始位址(基址),再以基址加上偏移位址yyyyyyyy才得到最後的線性位址。(cpu來完成)
似乎程序這個值得費筆墨的知識點真的沒有什麼東西可以說的了。
也難怪,偉大的東西似乎都是簡單的。
那程序切換的時機呢?總不能讓程序自己決定什麼時候不用cpu了吧?萬一有個無賴怎麼辦?
這個時候,中斷就是乙個非常好的仲裁者了,依靠物理特性,準時準點的報時。系統只需要在石英晶體報時的時候做一些計算就可以了。
在時鐘中斷裡面,我們可以來判斷當前程序是不是把時間片用完了,那系統從準備好的程序佇列中挑選乙個程序來執行。當然,這個挑選的過程可以簡單也可以複雜,看實現者想做什麼了。目前我們要做的比較簡單,每個程序分配一些時間片,每次時鐘中斷裡面把當前程序的時間片減1,等於0的時候,被系統拿掉cpu的使用權,讓下乙個程序使用cpu。
時鐘中斷還是比較簡單的,設定好8259a就可以響應中斷訊號了。但是在書中,作者又讓我了解到中斷重入的概念。以前看linux 0.12的時候時鐘中斷的**中卻是沒有重入的概念。
來看一下cpu響應中斷時候會發生什麼:
cpu響應中斷後,輸出中斷響應訊號,自動將狀態標誌暫存器的內容壓入堆疊保護起來,然後將狀態標誌暫存器中的中斷標誌位if與陷阱標誌位tf清零,從而自動關閉外部硬體中斷。因為cpu剛進入中斷時要保護現場,主要涉及堆疊操作,此時不能再響應中斷,否則將造成系統混亂。
書中給出的例項是儲存了暫存器現場後用sti指令開啟了中斷,所以允許中斷重入,而linux 0.12中進入中斷後沒有做額外的處理,所以不用理會中斷的重入。
處理中斷重入也不算難,需要定義乙個全域性變數,並初始化為-1。進入中斷對這個變數加1,如果本次的計算結果不等於0,說明是重入的中斷,直接結束本次中斷。
對於STL的理解
stl standard template library stl是一些 容器 的集合,這些 容器 有list,vector,set,map等,stl也是演算法和其他一些元件的集合。這裡的 容器 和演算法的集合指的是世界上很多聰明人很多年的傑作。stl的目的是標準化元件,這樣就不用重新開發,可以使用...
對於ftok的理解
回顧了一下以前的實驗,一直不理解為什麼在訊息佇列中要使用ftok,比如下面的乙個例項 if key ftok a 1 qid msgget key,ipc creat 0666 建立乙個訊息佇列 if qid 0 printf created queue id d n qid 輸出訊息佇列的id 要...
對於Zookeeper的理解
zookeeper是google 的chubby乙個開源的實現,是hadoop 的分布式協調服務。它包含乙個簡單的原語集,分布式應用程式可以基於它實現同步服務,配置維護和命名服務等。zookeeper包括乙個leader和多個follower。為什麼使用zookeeper?大部分分布式應用需要乙個主...