進程與線程--原理


        所謂白話即是將事物的原理用通俗易懂的語言表達出來,接下來我們就說一說我們平時用到的進程與線程在操作系統中是如何被管理以及調度的。

        其實操作系統本質上的意義就是如何讓我們更方便的來使用這些如 cpu、內存、網卡 等物理設施,給我們的生活帶來便利或更優質的生活享受。如我們打開電腦后,啟動操作系統,安裝應用就可以在線看電視或者打游戲。或者對於我們技術人員來講,在我們的開發過程中,假如我們要讀取硬盤中的數據,我們直接調用read系統調用就可以,我們無需去關心磁頭的移動與柱面扇區如何移動才能讀出數據。或者我們分配內存,我們直接調用malloc系統調用就可以分配內存,我們也無需關心內存條到底還有多少空閑。

        所以操作系統就是將我們從那些瑣碎的重復的勞動中解放出來,方便我們的生活與工作。 那么我們開始今天的話題,先說一下進程,然后再說一線程,最后再總結一下進程與線程的區別與應用方面的考慮。

        什么是進程?

        我們的計算機要為我們的生活服務,我們一邊寫着博客,同時還得聊着QQ,還得幫我收取郵件,我們希望計算機同時能幫我們做到這些事情,那么就有了進程。進程就是在操作系統中做某件事情的一段程序的實例,現在我的電腦里就運行着瀏覽器進程、QQ客戶端進程、郵件進程還有其他操作系統的一些基礎進程,所以計算機能在我寫博客的時候收取郵件還能發送QQ消息,當然對於cpu來講,某一個時刻只會運行一個進程,但是我們的操作系統有任務調度程序,讓cpu根據調度來在這些進程之間不斷的切換。在一秒鍾之內,操作系統就會在不同的進程中切換很多次,這也是對於我們人來講,感覺就像是計算機在同時做這幾件事情,但對於cpu來講他是在不同進程之間的執行來回切換。

      進程在操作系統中是如何管理的?

      我們通過上面知道了,操作系統中運行着各種不同任務的進程,有收發郵件的、有聊天的、有瀏覽器的,但是操作系統是如何讓這些進程不斷的來回切換並且是如何切換的呢。

      我們知道其實計算機的就是在不斷的進行計算,對數據進行處理。cpu發送指令從內存中讀取程序指令然后通過總線放入不同的寄存器,然后再從寄存器中讀入cpu進行運算然后再放入寄存器或寫入內存。

      其實一切的程序都是如此運行,當在操作系統中運行一個程序就是啟動一個進程,在操作系統中運行一個進程的時候,如果是第一次運行,操作系統就會為此進程分配內存空間,這些空間包括不可更改的指令空間(文本段),就是存儲要運行的程序的指令的;數據段,變量的值都存在這里;還有棧(堆棧)空間,這里存放在調用函數時局部變量在計算過程中的變化以及結果,隨着函數調用或結束,這里的空間也在增加或縮小;還有堆空間,程序中動態分配的內存空間將分配在這里。而后操作系統將指令空間的內存地址放入寄存器,由cpu開始讀取執行,沿着代碼段執行,在執行過程中為了保存結果或執行進度將變量的值或函數的調用過程放入相應的空間以及指令的執行進度放入相應的寄存器。

       在操作系統中系統維護着一張進程表,這里存儲着系統中運行的所有進程,每一項進程都占用一個進程表項。這個表項數據結構中存儲着進程的很多重要信息,如在系統調度讓cpu運行下一的進程時,把當前進程運行時的鏡像全部保存下來,為了當再次運行此進程時恢復當時運行的場景。比如 程序計算器(程序執行到哪) 堆棧指針(執行過程中的變相值) 各寄存器中的值 文件的打開狀態 等一切運行時的信息,還有進程本身的一些信息 比如運行此進程的帳號以及優先級之類的屬性信息。

       所以,操作系統是根據調度程序來決定運行哪個進程,當要運行某個進程時,會把當時正在運行的進程時的一起用於恢復當時場景的數據都保存在系統維護的進程表中對應的進程信息存儲的數據結構中,當調度程序再次運行此進程時,將保存的這些信息改放入寄存器的放入對應的起存期,恢復到堆棧的恢復到堆棧,繼續執行上一次運行指令的下一次指令。

      什么是線程?

     之所以會有線程,是因為在進程中運行多種活動時,當進行某一項活動時比如讀取磁盤數據,此時進程就處於阻塞狀態,整個事情的進展就處於暫時的停止狀態,如果我們將進程中的多種工作分派給不同的線程去處理,比如一個線程在向內存中寫入數據,一個線程在分析數據,這樣就不會在讀取數據的時候無法分析數據了。這就是為什么會有線程的概念。

     其次線程可以共享進程中的資源比如打開的文件、全局變量等公共資源,另外線程比進程更輕量級,創建和銷毀比進程要快10-100倍。這兩個也是線程之所以存在的原因。

     線程在進程的內存空間中有自己的內存空間用來臨時保存自己的堆棧或寄存器內容或程序計數器,用來恢復繼續運行,進程中不同的線程不像不同的進程之間存在着很大的獨立性,所有線程都有完全一樣的地址空間,這意味着線程之間共享全局變量。由於各個線程都可以訪問進程地址空間中的任意內存地址,所以一個線程可以修改讀取甚至刪除另一線程的堆棧,線程之間是沒有保護的。因為不同的線程肯定來自同一進程也就是同一用戶,他們之間不會有敵意,他們之間還可以共享打開的文件集、子進程、以及相關信號。而不同的線程之間的通信就復雜的多,同一進程中的多個線程是相互信任的,而不同的進程之間是不信任的。

其實線程就是進程中的又一執行單位,進程中不同的線程只是運行的進度不同,其他都是可以共享的。

進程空間地址存儲的內容 線程空間地址中存在的內容
地址空間 程序計數器
全局變量 寄存器
打開的文件集 堆棧
子進程 狀態
賬戶信息  
優先級  
信號  

     那么什么時候該使用線程?因為我們知道線程是在進程中的運行時不斷交替運行的單位,當某一線程遇到io阻塞,這個時候讓當前線程等待總線上的數據,保存當前線程的現場(堆棧和寄存器),運行另一線程。所以當某項工作大多時進行的都是cpu運算,那么多線程顯然毫無意義,而且還會增加線程之間切換保存線程的額外工作,所以cpu密集型的工作不適合用線程。但是當工作中有運算和iO處理工作時,顯然將這些工作分配給多線程是可以不浪費cpu寶貴的運算時間,讓cpu忙起來提高工作效率的好辦法。

    那么操作系統如何管理線程的呢?我們應該如何利用這種機制呢?

    第一種是操作系統不知道進程中運行着多線程,我們在用戶空間調用庫函數在進程中運行和調度多個線程,通過維護一張線程表來管理這些線程,操作系統拿這個進程就當是普通的進程。

     這種方式的優點就是操作系統計算不支持多線程機制,我們也可以照樣使用多線程來高效的完成我們的工作。我們可以在進程中通過自己的算法來調度線程的運行,而且在進程中創建或銷毀線程比在操作系統內核空間中要節約資源,無需像在內核中發生大量的上下文切換以及現場保存工作。

     這種方式的缺點就是由於操作系統並不知道進程中的多線程,所以當進程中的某一個線程發生系統調用陷入系統內核中時,操作系統會將這個進程阻塞,運行其他進程,它不知道也不會管該進程中是否有其他線程在運行着。還有就是在用戶空間中運行多線程,當某一個多線程一直占用cpu時,在當前進程的運行時刻,由於用戶空間中沒有時鍾機制,所以其他線程只能等待,無能為力。

     第二種就是操作系統支持多線程,那么進程中的線程就交由操作系統來管理,當進程中某一線程發生阻塞時,操作系統會選擇此進程中的另一線程繼續運行,而且當某一線程占用cpu時間過長時,操作系統也會根據時鍾阻塞線程,這也算是相對於用戶空間來說的優點。但是畢竟是在內核中由操作系統來管理線程,無論線程的創建還是管理消耗的資源都比在用戶空間大,這也是內核管理多線程的缺點。

     前面兩種模式都各有優缺點,如果能將其結合到一起,各取其優點,應該會更好。

     一種做法是使用內核級線程,然后將用戶級線程與內核級線程多路復用起來。這樣,用戶就可以決定有多少個用戶級線程和多少內核級線程多路復用,這樣會更加靈活。

      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM