全域性變數中斷原子操作 作業系統導論02 06章

2021-10-14 11:31:25 字數 3558 閱讀 1173

乙個正在執行的程式會做一件非常簡單的事情:執行指令。處理器從記憶體中獲取(fetch)一條指令,對其進行解碼(decode)(弄清楚這是哪條指令),然後執行(execute)它(做它應該做的事情,如兩個數相加、訪問記憶體、檢查條件、跳轉到函式等)。完成這條指令後,處理器繼續執行下一條指令,依此類推,直到程式最終完成。

虛擬化cpu和虛擬化記憶體、併發、檔案系統是這本書要講述的三個問題。

虛擬化cpu:

事實證明,在硬體的一些幫助下,作業系統負責提供這種假象(illusion),即系統擁有非常多的虛擬cpu的假象。將單個cpu(或其中一小部分)轉換為看似無限數量的cpu,從而讓許多程式看似同時執行,這就是所謂的虛擬化cpu(virtualizing the cpu)。

一次執行多個程式的能力會引發各種新問題。例如,如果兩個程式想要在特定時間執行,應該執行哪個?這個由作業系統的策略決定。作業系統承擔了資源管理器的角色。

虛擬化記憶體:

每個程序訪問自己的私有虛擬位址空間(virtual address space)(有時稱為位址空間,address space),作業系統以某種方式對映到機器的物理記憶體上。乙個正在執行的程式中的記憶體引用不會影響其他程序(或作業系統本身)的位址空間。對於正在執行的程式,它完全擁有自己的物理記憶體。但實際情況是,物理記憶體是由作業系統管理的共享資源。

併發:​i++這條指令每執行一次,它需要3條指令:一條將計數器的值從記憶體載入到暫存器,一條將其遞增,另一條將其儲存回記憶體。因為這3條指令並不是以原子方式(atomically)執行(所有的指令一次性執行)的,所以當兩個執行緒共同訪問乙個全域性變數的時候,奇怪的事情可能會發生。

永續性:

不像作業系統為cpu和記憶體提供的抽象,作業系統不會為每個應用程式建立專用的虛擬磁碟。相反,它假設使用者經常需要共享(share)檔案中的資訊。例如,在編寫c程式時,你可能首先使用編輯器(例如emacs[插圖])來建立和編輯c檔案(emacs -nw main.c)。之後,你可以使用編譯器將源**轉換為可執行檔案(例如,gcc -o main main.c)。再之後,你可以執行新的可執行檔案(例如./main)。因此,你可以看到檔案如何在不同的程序之間共享。

以最基本的計算機資源cpu為例,假設乙個計算機只有乙個cpu(儘管現代計算機一般擁有2個、4個或者更多cpu),虛擬化要做的就是將這個cpu虛擬成多個虛擬cpu並分給每乙個程序使用,因此,每個應用都以為自己在獨佔cpu,但實際上只有乙個cpu。這樣作業系統就創造了美麗的假象——它虛擬化了cpu。

如何提供有許多cpu的假象?

作業系統通過虛擬化(virtualizing)cpu來提供這種假象。通過讓乙個程序只執行乙個時間片,然後切換到其他程序,作業系統提供了存在多個虛擬cpu的假象。這就是時分共享(timesharing)cpu技術,允許使用者如願執行多個併發程序。潛在的開銷就是效能損失,因為如果cpu必須共享,每個程序的執行就會慢一點。

如何將**變成程序?(作業系統如何啟動並執行乙個程式?程序建立實際如何進行?)

1、作業系統執行程式必須做的第一件事是將**和所有靜態資料(例如初始化變數)載入(load)到記憶體中,載入到程序的位址空間中。

2、為程式的執行時棧(run-time stack或stack)分配一些記憶體。

3、作業系統也可能為程式的堆(heap)分配一些記憶體。

4、作業系統還將執行一些其他初始化任務,特別是與輸入/輸出(i/o)相關的任務。例如,在unix系統中,預設情況下每個程序都有3個開啟的檔案描述符(file descriptor),用於標準輸入、輸出和錯誤。

5、最後一項任務:啟動程式,在入口處執行,即main()。通過跳轉到main()例程(第5章討論的專門機制),os將cpu的控制權轉移到新建立的程序中,從而程式開始執行。

程序的三種狀態,執行,就緒,阻塞。

舉乙個例子,第乙個程序在執行一段時間後發起i/o請求。此時,該程序被阻塞,讓另乙個程序有機會執行。表4.2展示了這種場景(注意這裡阻塞的時候,另外乙個程序在占用cpu的時間執行,這裡要和掛起區分開,阻塞釋放cpu,而掛起不釋放cpu。)

fork()系統呼叫:

父程序獲得的返回值是新建立子程序的pid,而子程序獲得的返回值是0。

wait()系統呼叫:

父程序呼叫wait(),延遲自己的執行,直到子程序執行完畢。當子程序結束時,wait()才返回父程序。

最後是exec()系統呼叫:

這個系統呼叫可以讓子程序執行與父程序不同的程式。

我們重點看下面的**:

#include #include #include #include #include int

main(int argc, char *ar**)

else if (rc == 0) else

return 0;

}

子程序執行執行到 ​execvp(myargs[0], myargs);這裡,子程序會覆寫自己的**段(以及靜態資料),堆、棧及其他記憶體空間也會被重新初始化。所以 ​printf("this shouldn't print out");在子程序並不會被執行。當子程序執行完畢,父程序 ​int wc = wait(null);會返回,執行下面的**。

第乙個問題很簡單:如果我們只執行乙個程式,作業系統怎麼能確保程式不做任何我們不希望它做的事,同時仍然高效地執行它?第二個問題:當我們執行乙個程序時,作業系統如何讓它停下來並切換到另乙個程序,從而實現虛擬化cpu所需的時分共享?

1、硬體通過提供不同的執行模式來協助作業系統。在使用者模式(user mode)下,應用程式不能完全訪問硬體資源。在核心模式(kernel mode)下,作業系統可以訪問機器的全部資源。還提供了陷入(trap)核心和從陷阱返回(return-from-trap)到使用者模式程式的特別說明,以及一些指令,讓作業系統告訴硬體陷阱表(trap table)在記憶體中的位置。為了讓使用者可以執行某種特權操作,現代硬體都提供了使用者程式系統呼叫的能力.

2、要執行系統呼叫,程式必須執行特殊的陷阱(trap)指令。該指令同時跳入核心並將特權級別提公升到核心模式。一旦進入核心,系統就可以執行任何需要的特權操作(如果允許),從而為呼叫程序執行所需的工作。完成後,作業系統呼叫乙個特殊的從陷阱返回(return-from-trap)指令,如你期望的那樣,該指令返回到發起呼叫的使用者程式中,同時將特權級別降低,回到使用者模式。

如果乙個程序在cpu上執行,這就意味著作業系統沒有執行。作業系統如何重新獲得cpu的控制權(regain control),以便它可以在程序之間切換?

協作方式:等待系統呼叫。:在協作排程系統中,os通過等待系統呼叫,或某種非法操作發生,從而重新獲得cpu的控制權。

非協作方式:作業系統進行控制。時鐘中斷,時鐘裝置可以程式設計為每隔幾毫秒產生一次中斷。產生中斷時,當前正在執行的程序停止,作業系統中預先配置的中斷處理程式(interrupt handler)會執行。此時,作業系統重新獲得cpu的控制權,因此可以做它想做的事:停止當前程序,並啟動另乙個程序。

全域性變數中斷原子操作 微控制器中斷全域性變數保護方法

首先要明白這幾個知識點 關鍵字volatile的使用,原子操作,臨界區的使用。明白的直接跳到文中的4.全域性變數的使用及保護處檢視。1.關鍵字volatile 關鍵字volatile用於告訴編譯器,說明被修身的變數可能會被意想不到地改變,防止編譯器對 進行優化。比如如下程式 1ucnms 0x65 ...

全域性變數中斷原子操作 微控制器中斷全域性變數保護方法

首先要明白這幾個知識點 關鍵字volatile的使用,原子操作,臨界區的使用。明白的直接跳到文中的4.全域性變數的使用及保護處檢視。1.關鍵字volatile 關鍵字volatile用於告訴編譯器,說明被修身的變數可能會被意想不到地改變,防止編譯器對 進行優化。比如如下程式 1ucnms 0x65 ...

作業系統之原子操作

原子操作是指不會被執行緒排程機制打斷的操作 原子操作 atomic operation 是不需要同步 synchronized 這種操作一旦開始,就一直執行到結束,中間不會有任何執行緒切換。如果這個操作所處的層 layer 的更高層不能發現其內部實現與結構,那麼這個操作是乙個原子 atomic 操作...