操作系统的线程和进程的区别_进程,线程,协程,有何区别?
進程
? cpu是計算機的核心,主要處理計算機的計算任務。操作系統是計算機的管理員,它負責任務的調度,資源的管理和分配,統一管理計算機的硬件資源。應用程序則是具有某種功能的程序,應用程序運行于操作系統之上。
? 進程是一個具有特定功能的程序運行在一個數據集上的一次動態過程。是操作系統進行資源分配和調度的一個獨立單位,是應用程序運行的載體。
進程一般由這幾部分組成:
程序:用于描述進程要完成的功能,是控制進程執行的指令集
數據集合:程序執行時所需的數據和工作空間
程序控制塊:包含進程的描述信息和控制信息,組成進程的唯一標志
進程間通信的幾種方式
管道
? 一般用于單向數據流轉,只有父子進程進行通信,特點是容量小,數據傳輸慢。
消息隊列
? 消息隊列是由消息組成的鏈表,存放在內核中并由消息隊列標識符標識。消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩沖區大小受限等缺點。
信號量
? 不能用來傳遞復雜消息,只能用來同步
共享內存
? 利用內存緩沖區直接交換信息,無須復制,快捷、信息量大是其優點。共享內存塊提供了在任意數量的進程之間進行高效雙向通信的機制。每個使用者都可以讀取寫入數據,但是所有程序之間必須達成并遵守一定的協議,以防止諸如在讀取信息之前覆寫內存空間等競爭狀態的出現。
套接字
? 套解字也是一種進程間通信機制,與其他通信機制不同的是,它可用于不同機器間的進程通信。
進程間通信方式的選擇
- 管道用來實現進程間相互發送非常短小的、頻率很高的消息,這種方式通常適用于兩個進程間的通信
- 共享內存用來實現進程間共享的、非常龐大的、讀寫操作頻率很高的數據;這種方法適用于多進程間的通信
- 其他考慮用socket。主要應用在分布式開發中
線程上下文切換
由于中斷處理,用戶態切換,多任務處理等原因會導致cpu從一個線程切換到另一個線程時,切換過程需要保存當前線程的狀態并且恢復另一個線程的狀態。
上下文切換的代價是高昂的,上下文切換的延遲取決于不同的因素,一次切換大概的耗時為60~100納秒,每個CPU核心平均每納秒能執行12條指令,所以一次上下文切換會耗費720~1200條指令的延遲時間。而且在實際程序中,上下文切換會占用大量程序執行的時間。
如果存在跨核上下文切換,會導致cpu緩存失效(cpu從緩存訪問數據的大致成本在3到40個時鐘周期,從內存訪問數據的成本在100到300個時間周期),這時的切換成本會更加高昂。
協程
Golang 從語言級別支持并發,通過輕量級協程 Goroutine 來實現程序并發運行。
goroutine非常輕量,主要體現在這兩方面:
上下文切換代價小:上下文切換只涉及3個寄存器的值修改。線程上下文切換需要從用戶態切換到內核態,并且需要修改20多個寄存器的值。
內存空間占用小:一個線程棧空間通常是2M,Goroutine最小棧空間為2K。
GO調度器實現機制
go程序通過調度器來調度goroutine在內核線程上執行,但不是直接使用內核線程(Machine)來直接執行goroutine,而是由scheduler中的processor來作為獲取內核線程的【中介】。
Go 調度器模型我們通常叫做G-P-M 模型,他包括 4 個重要結構,分別是G、P、M、Sched:
- G: Goroutine,每個 Goroutine 對應一個 G 結構體,G 存儲 Goroutine 的運行堆棧、狀態以及任務函數,可重用。G 并非執行體,每個 G 需要綁定到 P 才能被調度執行。
- P: Processor,表示邏輯處理器,對 G 來說,P 相當于 CPU 核,G 只有綁定到 P 才能被調度。對 M 來說,P 提供了相關的執行環境(Context),如內存分配狀態(mcache),任務隊列(G)等。P 的數量決定了系統內最大可并行的 G 的數量(前提:物理 CPU 核數 >= P 的數量)。P 的數量由用戶設置的 GoMAXPROCS 決定,但是不論 GoMAXPROCS 設置為多大,P 的數量最大為 256。
- M: Machine,OS 內核線程抽象,代表著真正執行計算的資源,在綁定有效的 P 后,進入 schedule 循環;而 schedule 循環的機制大致是從 Global 隊列、P 的 Local 隊列以及 wait 隊列中獲取。M 的數量是不定的,由 Go Runtime 調整,為了防止創建過多 OS 線程導致系統調度不過來,目前默認最大限制為 10000 個。M 并不保留 G 狀態,這是 G 可以跨 M 調度的基礎。
- Sched:Go 調度器,它維護有存儲 M 和 G 的隊列以及調度器的一些狀態信息等。調度器循環的機制大致是從各種隊列、P 的本地隊列中獲取 G,切換到 G 的執行棧上并執行 G 的函數,調用 Goexit 做清理工作并回到 M,如此反復。
在 Go 程序里我們通過下面的圖示來展示 G-P-M 模型:
P代表可以并行運行的邏輯處理器,每個P被分配到一個系統線程M。G代表協程(Goroutine)。Go調度器有兩個不同的調度隊列,全局隊列(GRQ)和每個P都有一個本地隊列(LRQ),LRQ用于管理分配給P中上下文執行的G,這些Gorotine輪流被和P綁定的M執行上下文切換。GRQ適用于那些還未分配P的goroutine。
從上圖可以看出,G 的數量可以遠遠大于 M 的數量,換句話說,Go 程序可以利用少量的內核級線程來支撐大量 Goroutine 的并發。多個 Goroutine 通過用戶級別的上下文切換來共享內核線程 M 的計算資源,但對于操作系統來說并沒有線程上下文切換產生的性能損耗。
為了更加充分利用線程的計算資源,Go 調度器采取了以下幾種調度策略:
任務竊取(work-stealing)
當某個P的運行負載比較低時,調度器允許從GRQ或者別的P的LRQ獲取G執行。(使各個P的運行壓力均衡)
減少阻塞
場景一
由于一些原子,互斥或者通道操作導致Goroutine阻塞,調度器會把當前阻塞的G切換出去,從LRQ獲取一個新的G執行。
場景二
由于網絡操作和IO操作導致Goroutine阻塞,這種情況如何優化呢?
Go程序提供了網絡輪訓器(netpoller)來處理網絡請求和IO操作的阻塞問題。netpoller底層通過IO多路復用技術來實現,通過netpoller來進行網絡系統調用,調度器可以防止G在進行這些系統調用時阻塞M。將網絡事件的監聽交給netpoller來處理,從而可以使M去獲取LRQ中的另一個G執行,不用再創建一個新的內核線程M,有助于減少操作的調度負載。
(1)G1 正在 M 上執行,還有 3 個 Goroutine 在 LRQ 上等待執行。網絡輪詢器空閑著,什么都沒干。
(2)G1執行一個網絡操作,G1被移動到網絡輪訓器并且處理異步網絡系統調用。M從LRQ獲取一個新的G2執行。
(3)異步網絡系統調用完成,G1重新加入到LRQ,一旦G1再次被M上下文切換,會繼續執行G1負責的相關代碼。這里最大的優勢是網絡系統的調用不需要創建額外的系統線程M。并且Go網絡輪訓器底層使用非阻塞IO+IO多路復用機制實現了一個高性能的網絡編程機制。
場景三
進行一些系統調用會導致阻塞時,網絡輪訓器此時無法使用,這時的解決方式是,將原來的內核線程M1和這個阻塞G1移除出去,給原來的P1創建一個新的內核線程M2,由M2來執調度LRQ的G。G1阻塞的系統調用完成之后,G1會重新放入到P1的LRQ中,M1會暫時閑置等待后面再次被使用。
場景四
如果Goroutine去執行了一個sleep()操作,導致M阻塞了。這時后臺會有一個監控線程syscom,它監控那些長時間運行的G任務然后設置可搶占標識,別的Goroutine就可以搶行進來執行。
總結:
進程是具有特定功能的程序運行在數據集上的一次動態過程。是操作系統分配資源的最小單位,是應用程序運行的載體。一個進程可以有多個線程,進程之間相互獨立,都有一塊自己的內存空間。
線程是程序執行的最小單位,一個進程由一個或者多個線程組成,線程是進程中代碼執行的不同路線。同一個進程中的各個線程共享程序的內存空間(包括代碼段,堆和數據集,打開的文件),每個線程還會有自己的棧和寄存器,這些空間是每個線程獨占的。
協程是一種用戶態的輕量級線程,協程的切換完全由用戶控制,協程間切換只需保存任務的上下文,沒有內核的消耗。
總結
以上是生活随笔為你收集整理的操作系统的线程和进程的区别_进程,线程,协程,有何区别?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python自动化测试脚本后端_基于 p
- 下一篇: propertysource注解 找不到