1. 程式人生 > 其它 >計算機作業系統-1

計算機作業系統-1

學習作業系統

1、併發和並行

並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔發生

我覺得併發單核CPU來說,某一個時刻只有一個執行緒執行,隔了一段時間之後,由另外一個執行緒來進行執行,這個是併發。

對於一個多核CPU來說,可以同時有多個執行緒在多個CPU上執行,這個是並行。

超執行緒的到來說明了一個CPU上又可以執行多個執行緒(下面講到),說明了單核CPU也是可以實現並行和併發操作的。

2、程序和執行緒:

程序和執行緒是由早期的計算機發展史引入而來的。

2.1、單處理機系統

早期的計算機用紙帶讓CPU來執行對應的操作。但是這種方式存在一個問題,在進行IO操作的時候,也就是從紙帶讀取到記憶體中的時候(那時候不知道有沒有記憶體的概念),這個時候CPU處於等待狀態,這是一件無法讓人忍受的事情,因為理想目標是讓CPU一直處於執行狀態;

2.2、批處理系統

2.2.1、程序由來

在批處理系統中講解程序由來

批量讀取命令,我讀取了A指令的時候,就去執行;那麼CPU在執行的過程中,還在讀取紙帶上的命令。那麼就實現了在讀取的時候,CPU也在處於執行狀態。但是這又有了一個新的問題。紙帶上有好多命令,比如說有一段命令是進行1+1的操作,另外一段是2+2的操作,結果記憶體沒有管理好,導致了CPU執行的是1+2,最終得到的結果和理想的結果不一致。那咋整?這時候引入了程序的概念,計算機給兩個不同的命令分配兩塊不同的空間,你執行你的,我執行我的,咱倆互不干擾。於是乎程序的概念就來了。

程序表示單個執行活動集的計算機程式,是系統的資源分配排程

的基本單元,是作業系統結構的基礎。
在早期面向程序的計算機結構中,過程是程式的基本執行實體,在面向執行緒設計的現代計算機結構中,程序是執行緒的容器。程式是對指令、資料及其組織形式的描述,流程是程式的實體。

作業系統引入程序的概念的原因:從理論角度看,是對正在執行的程式過程的抽象。從實現角度看,是一種資料結構,目的在於清晰地刻畫動態系統的內在規律,有效管理和排程進入計算機系統主儲存器執行的程式。這就是上面說的如何來劃分記憶體區域來防止資料干擾的問題解決方式。

2.2.2、執行緒由來

上面介紹了程序是執行著的程式的抽象,那麼一個程式中存在著多個需要執行完成的任務。

例子:

A程序:
{
    // 計算操作會導致處於執行狀態
    XXX;
    // IO操作或者是檔案上傳操作
    YYY;
    // 需要使用到XXX中執行的結果
    ZZZ;
}

在A程序中發現,YYY這個任務和XXX和ZZZ沒關係,結果就造成了ZZZ的執行需要等到YYY執行完成,然後才能執行ZZZ,但是YYY又和ZZZ沒啥關係。就是YYY阻攔了ZZZ的執行,一直讓ZZZ處於等待狀態。這個時候程式設計師就很不爽了,我希望YYY執行的時候,ZZZ也執行,可能YYY執行完了或者還沒有執行完,ZZZ已經執行完了,A程序任務都結束了,等你YYY執行完就可以了。那麼多核CPU完全是可以勝任的。(我嚴重懷疑,指令重排就和這個有關係)。那麼引入了執行緒的概念,我們這個將YYY用一個執行緒來進行執行,ZZZ用一個執行緒來執行。那麼這樣子操作,一來節省了時間;而來,還讓CPU都處於執行狀態。合理的運用了CPU的資源。

執行緒的實體包括程式、資料和TCB。執行緒是動態概念,它的動態特性由執行緒控制塊TCB(Thread Control Block)描述。TCB包括以下資訊:
1、執行緒狀態。
2、當執行緒不執行時,被儲存的現場資源。
3、一組執行堆疊。
4、存放每個執行緒的區域性變數主存區。
5、訪問同一個程序中的主存和其它資源。
用於指示被執行指令序列的程式計數器、保留區域性變數、少數狀態引數和返回地址等的一組暫存器和堆疊。

也就是說執行緒切換的時候,CPU中得儲存到這裡資訊。

執行緒上下文切換。

如:執行緒在某一個時間段發生了切換,此時CPU需要使用暫存器等其他器件儲存當前執行緒的狀態,也就是執行現場。當執行緒再次執行的時候,需要恢復執行現場,再次進行執行。

3、超執行緒

超執行緒,Intel提出來的一種思想:一個物理CPU提供了兩個執行緒邏輯,也就是模擬出兩個邏輯CPU。也就是說,兩個執行緒可以在同時在一個CPU上進行執行,雖然採用超執行緒技術能夠同時執行兩個執行緒,當兩個執行緒同時需要某個資源時,其中一個執行緒必須讓出資源暫時掛起,直到這些資源空閒以後才能繼續。因此,超執行緒的效能並不等於兩個CPU的效能。而且,超執行緒技術的CPU需要晶片組、作業系統和應用軟體的支援,才能比較理想地發揮該項技術的優勢。

4、使用者空間和核心空間

作業系統採用的是虛擬地址空間,以32位作業系統舉例,它的定址空間為4G(2的32次方),這裡解釋二個概念:

  1. 定址: 是指作業系統能找到的地址範圍,32位指的是地址匯流排的位數,你就想象32位的二進位制數,每一位可以是0,可以是1,是不是有2的32次方種可能,2^32次方就是可以訪問到的最大記憶體空間,也就是4G。
  2. 虛擬地址空間:為什麼叫虛擬,因為我們記憶體一共就4G,但作業系統為每一個程序都分配了4G的記憶體空間,這個記憶體空間實際是虛擬的,虛擬記憶體到真實記憶體有個對映關係。例如X86 cpu採用的段頁式地址對映模型。(不用去管如何實現的)

作業系統將這4G可訪問的記憶體空間分為二部分,一部分是核心空間,一部分是使用者空間。

核心空間是留給作業系統中的程式的,使用者空間是用來留給使用者自定義的程式來進行使用的。

以linux作業系統為例,將最高的1G位元組(從虛擬地址0xC0000000到0xFFFFFFFF),供核心使用,稱為核心空間,而將較低的3G位元組(從虛擬地址0x00000000到0xBFFFFFFF),供各個程序使用,稱為使用者空間

-------------   0xC0000000
|            |  核心空間
|            |	給OS中的程式使用
-------------   0xFFFFFFFF
|            |  使用者空間(通常比核心空間大一點)
|            |	給我們的程式來執行
------------- 

OS的作用就是為了管理計算機硬體裝置。如果使用者空間的程式使用到硬體裝置了,那麼需用通過向作業系統來進行申請使用。作業系統申請通過,那麼就可以幫助使用者空間的程式來執行命令。這裡說的申請,其實是通過作業系統向外提供的介面來進行操作的。

java底層用C語言來進行實現的,當java啟動時候,會啟動一個程序。通過庫函式呼叫系統呼叫介面,與計算機進行互動。

4.1、為什麼有使用者空間和核心空間

在早期的計算機設計中,其實是沒有區別的。這裡就會設計到為什麼會有作業系統的由來了。在早期計算機中,如果想要去操作計算機,那麼就必須通過計算機的指令來進行執行。但是計算機的指令全部都是010101組成的,這樣的可讀性太差了。後來出現了類似彙編指令,用一個可讀的指令,如ADD,代表加操作,對應機器指令0101010100(這裡是亂寫的),那麼彙編在經過翻譯之後,對應的就是二進位制資料,然後將資料運送到計算機中去進行執行。

於是乎,彙編指令將常用的二進位制指令封裝成人能夠看懂的程式碼。然後編寫的使用者程式來寫出來對應的程式來操作計算機。

隨著計算機的發展,程式設計師們可以在裸機上實現任何想要的操作,只要懂二進位制對應的彙編操作。於是軟體行業日新月異,但是人們發現這種效率太低了,因為每寫一個程式,都需要去查詢對應的指令,然後將指令進行收集編寫程式碼。

作業系統的出現,一統天下。我將你想要的操作都給封裝起來,指令我都給你收集好。我把常用的功能也給封裝好,你想要用,直接呼叫作業系統給你提供的介面就好了。人們也發現這種方式比較方便,於是作業系統的趨勢就發展起來了。

這裡的重點是作業系統給使用者提供介面。如果沒有對應功能的介面實現,那麼還不如自己來寫程式實現響應的功能。所以作業系統的優點在這塊體現的比較明顯。

在早期的作業系統中,存在著很大的缺陷。存在著一些惡意破壞計算機的人,這些人難道是出於好玩嗎?不過還是要感謝這些人,因為有了這些人的破壞,作業系統才會發展的越來越好。早期OS的設計本來是為了提供便利,但是有些人繞過了作業系統,利用一些指令去破壞在作業系統上執行的使用者的程式。比如我在用微信發信息,結果傳送的資訊被別人截獲了,雖然資訊傳送出去了,但是別人也獲取到了我傳送的資訊。那麼資料安全方面無法得到保障。再比如:我電腦程式執行的好好的,突然一個指令繞過了作業系統,傳送了關閉計算機的指令訊號,CPU不管這個指令是從哪裡來的,直接就給執行了。然後計算機直接關機了。這種情況是作業系統無法容忍的。因為帶給使用者的體驗感極差,所以作業系統為了優化,希望將外界程式操作計算機的命令都給遮蔽掉,外界程式想要來操作,那麼只能通過我的作業系統來進行操作。

如下圖:

在使用者編寫的程式中,如果用到了計算機中作業系統中的功能,那麼直接通過作業系統提供的介面來使用即可。

除了上面的檔案管理,還有最常見的網路管理。OS也封裝好了網路通訊的功能,使用者在使用的時候直接進行使用就可以了。

這樣子看,簡化了程式編寫者的難度。

那麼接著聊使用者空間和核心空間,既然作業系統是為了方便外界來進行操作的。那麼肯定是得為外來的資料進行儲存,並且為其提供執行環境,但是作業系統又不想讓你直接繞過我的作業系統,於是進行分層。作業系統內部執行的空間叫做核心空間,使用者使用的叫做使用者空間。使用者空間的程式需要通過OS申請通過後,指令在核心空間中進行執行。那麼既保護了內部程式,也讓外部程式實現了對應的工作,還保證了安全性。

5、使用者態和核心態

使用者態是指在使用者空間中正在執行的程式碼和資料,核心態是指在核心空間中正在執行的程式碼和資料。

6、執行緒模型

1、程序是OS分配資源的基本單位,執行緒是CPU執行的基本單位。

在現代作業系統中,按照使用者空間和核心空間,執行緒模型分為有ULT(使用者執行緒)KLT(核心執行緒)

6.1、ULT

使用者執行緒是完全建立在使用者空間的執行緒庫,執行緒執行在Run-Time System中,核心對此一無所知。對於核心來說,它這是在處理一個單執行緒的程序而已。在使用者空間實現執行緒時,每一個程序針對自己的執行緒維護了一個用於儲存執行緒執行的各種變數,比如暫存器,PC,狀態等資訊的執行緒表(Thread Table),該執行緒表在程序的Run-Time System中維護,當一個執行緒被block,她的當前執行狀態會被儲存線上程表中,當再次啟動時,也會讀取執行緒表中已經儲存的狀態,從該狀態進行再次執行。使用者執行緒的建立、排程、同步和銷燬全由庫函式在使用者空間完成,不需要核心的幫助。

優勢

  1. 執行緒的建立由Run-Time System通過呼叫現有的LibraryProcedure完成,建立和銷燬程序的開銷非常小。
  2. 因為執行緒由Run-Time System進行維護,在同一個程序內部,執行緒的切換沒有必要和核心打交道,所以執行緒之間的切換的開銷非常小,沒有Context Switch,也沒有記憶體快取的重新整理重置。
  3. 使用者空間的執行緒可以自定義排程演算法,程式設計師完全可以自己寫一套針對自己程式的執行緒排程演算法。

劣勢

  1. 對於核心來說,不管程序裡面有多少個執行緒,核心仍然按照單執行緒程序來處理這個程序,所以同一時間一個程序裡面只能有一個執行緒執行,就算有多個cpu空閒,也只能有一個執行緒執行,所以無法最大限度的使用資源
  2. 由於只能有一個執行緒執行,當某一個執行緒被block後,整個程序都會被block。

通過上面的ULT模型知曉,執行緒管理的所有工作都由應用程式完成,核心意識不到執行緒的存在。應用程式可以通過使用執行緒庫設計成多執行緒程式,使用者執行緒的建立、排程、同步和銷燬全由執行緒庫完成,無需利用系統呼叫(也就是通過介面),而是自己來進行實現。由於處理器時間片分配是以程序為基本單位,所以同一時間一個程序裡面只能有一個執行緒執行,程序中的所有執行緒競爭分配到的時間片。

6.2、KLT

核心執行緒是建立在核心空間的執行緒庫,只執行在核心態,不受使用者態上下文的拖累。和使用者執行緒注意區分

核心執行緒就是核心的分身,一個分身可以處理一件特定事情。核心執行緒同樣有執行緒表(Thread Table),該執行緒表在核心中維護,核心執行緒可以在全系統內進行資源的競爭,它們的建立和銷燬都是由作業系統負責、通過系統呼叫完成的。

在此模型下,執行緒的切換排程由系統核心完成,系統核心負責將多個執行緒執行的任務對映到各個CPU中去執行。

如下:

核心執行緒駐留在核心空間,它們是核心物件。有了核心執行緒,每個使用者執行緒被對映或繫結到一個核心執行緒。使用者執行緒在其生命期內都會繫結到該核心執行緒。一旦使用者執行緒終止,兩個執行緒都將離開系統。這被稱作”一對一”執行緒對映。

優勢

  1. 執行緒表包含所有程序的執行緒,所以一個程序有可能有多個執行緒同時在多個cpu上同時執行
  2. 一個執行緒被block不會導致整個程序被block,CPU會看是不是有其他執行緒可以執行。

劣勢

  1. 建立執行緒消耗非常大,需要在使用者空間和核心之間切換。核心執行緒需要完整的上下文切換,修改記憶體映像,使快取記憶體失效,這會導致了若干數量級的延遲。

程式一般不會直接去使用核心執行緒,而是去使用核心執行緒的一種高階介面——輕量級程序(Light Weight Process,LWP),輕量級程序就是我們通常意義上所講的執行緒,由於每個輕量級程序都由一個核心執行緒支援,因此只有先支援核心執行緒,才能有輕量級程序。這種輕量級程序與核心執行緒之間1:1的關係稱為一對一的執行緒模型

使用者程序使用系統核心提供的介面———輕量級程序(Light Weight Process,LWP)來使用系統核心執行緒。在此種執行緒模型下,由於一個使用者執行緒對應一個LWP,因此某個LWP在呼叫過程中阻塞了不會影響整個程序的執行。核心執行緒通過執行緒表去查詢其他的來進行執行。

由於核心執行緒的支援,每個輕量級程序都成為一個獨立的排程單元,即使有一個輕量級程序在系統呼叫中阻塞了,也不會影響整個程序繼續工作,但是輕量級程序具有它的侷限性:

首先,由於是基於核心執行緒實現的,所以各種執行緒操作,如建立、析構及同步,都需要進行系統呼叫。而系統呼叫的代價相對較高,需要在使用者態(User Mode)和核心態(Kernel Mode)中來回切換。

其次,每個輕量級程序都需要有一個核心執行緒的支援,因此輕量級程序要消耗一定的核心資源(如核心執行緒的棧空間),因此一個系統支援輕量級程序的數量是有限的。

6.3、混合型執行緒模型

這裡不再仔細去分析了。

放個連結:

1、https://blog.csdn.net/weixin_39954674/article/details/110395028?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-1&spm=1001.2101.3001.4242

2、https://www.cnblogs.com/kaleidoscope/p/9598140.html

7、java執行緒模型

證明下java使用的是KLT模型。

/**
 * 測試java使用的執行緒模型
 */
public class ThreadDemoFour {
    public static void main(String[] args) {
        // 建立一千個執行緒
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                try {
                    // 讓每個執行緒睡眠5秒鐘
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

開啟windows工作管理員,選擇效能,然後看下執行緒數,然後執行程式看看是不是大概增長了一千個執行緒。

我經過驗證,結論是正確的。

總結

1、執行緒、程序的由來及其帶來的好處

2、執行緒和程序是兩個相對獨立的概念,執行緒更多是對執行序列的抽象,程序更多是執行空間的抽象,它們是交叉的,按OS實現的方便,有時可以切換執行序列而不切換執行空間(例如Linux的程序內執行緒切換),有時可以切換執行空間而不切換執行序列。所以說這兩種情況在計算機中都是可能發生的。所以在java高併發中,我們的通常理解成就是空間不發生切換,而讓執行緒發生切換,讓執行緒在獨立進行執行。程序切換需要消耗大量資源,執行緒切換消耗資源少。

3、執行緒發生上下文的切換,最重要的地方就是頻繁建立執行緒銷燬執行緒,那麼將會導致資源頻繁消耗。可能執行緒所攜帶的任務執行時間都沒有建立執行緒消耗的時間多,那麼將會導致頻繁浪費。所以推薦使用執行緒池來進行使用。

4、OS帶來的優勢及為什麼會有使用者空間、核心空間和使用者態以及核心態