[TOC]
## 3、對于操作系統而言進程、線程以及Goroutine協程的區別
進程、線程、協程實際上都是為并發而生。
但是他們的各自的模樣是完全不一致的,下面我們來分析一下他們各自的特點和關系。
> 本文不重點介紹什么是進程和線程,而是提煉進程、線程、協程干貨。且是基于Linux下的進程、線程解釋
### 一、進程內存
進程,可執行程序運行中形成一個獨立的內存體,這個內存體**有自己獨立的地址空間(Linux會給每個進程分配一個虛擬內存空間32位操作系統為4G, 64位為很多T),有自己的堆**,上級掛靠單位是操作系統。**操作系統會以進程為單位,分配系統資源(CPU時間片、內存等資源),進程是資源分配的最小單位**。

### 二、線程內存
**線程,有時被稱為輕量級進程(Lightweight Process,LWP),是操作系統調度(CPU調度)執行的最小單位**。

多個線程共同“寄生”在一個進程上,除了擁有各自的棧空間,其他的內存空間都是一起共享。所以由于這個特性,使得線程之間的內存關聯性很大,互相通信就很簡單(堆區、全局區等數據都共享,需要加鎖機制即可完成同步通信),但是同時也讓線程之間生命體聯系較大,比如一個線程出問題,到底進程問題,也就導致了其他線程問題。
### 三、執行單元
對于Linux來講,不區分進程還是線程,他們都是一個單獨的執行單位,CPU一視同仁,均分配時間片。

所以,如果一個進程想更大程度的與其他進程搶占CPU的資源,那么多開線程是一個好的辦法。
如上圖,進程A沒有開線程,那么默認就是`1個線程`,對于內核來講,它只有1個`執行單元`,進程B開了`3個線程`,那么在內核中,該進程就占有3個`執行單元`。CPU的視野是只能看見內核的,它不知曉誰是進程和誰是線程,誰和誰是一家人。時間片輪詢平均調度分配。那么進程B擁有的3個單元就有了資源供給的優勢。
### 四、切換問題與協程
我們通過上述的描述,可以知道,線程越多,進程利用(或者)搶占的cpu資源就越高。

那么是不是線程可以無限制的多呢?
答案當然不是的,我們知道,當我們cpu在內核態切換一個`執行單元`的時候,會有一個時間成本和性能開銷

其中性能開銷至少會有兩個開銷
* 切換內核棧
* 切換硬件上下文
> 這兩個切換,我們沒必要太深入研究,可以理解為他所帶來的后果和影響是
> ---
* 保存寄存器中的內容
將之前執行流程的狀態保存。
* CPU高速緩存失效
頁表查找是一個很慢的過程,因此通常使用Cache來緩存常用的地址映射,這樣可以加速頁表查找,這個cache就是TLB.當進程切換后頁表也要進行切換,頁表切換后TLB就失效了,cache失效導致命中率降低,那么虛擬地址轉換為物理地址就會變慢,**表現出來的就是程序運行會變慢**。
> ---
綜上,我們不能夠大量的開辟,因為`線程執行流程`越多,cpu在切換的時間成本越大。很多編程語言就想了辦法,既然我們不能左右和優化cpu切換線程的開銷,那么,我們能否讓cpu內核態不切換`執行單元`, 而是在用戶態切換執行流程呢?
很顯然,我們是沒權限修改操作系統內核機制的,那么只能在用戶態再來一個`偽執行單元`,那么就是`協程`了。

### 五、協程的切換成本
協程切換比線程切換快主要有兩點:
(1)協程切換**完全在用戶空間進行**線程切換涉及**特權模式切換,需要在內核空間完成**;
(2)協程切換相比線程切換**做的事情更少**,線程需要有內核和用戶態的切換,系統調用過程。
#### 協程切換成本:
協程切換非常簡單,就是把**當前協程的 CPU 寄存器狀態保存起來,然后將需要切換進來的協程的 CPU 寄存器狀態加載的 CPU 寄存器上**就 ok 了。而且**完全在用戶態進行**,一般來說一次協程上下文切換最多就是**幾十ns** 這個量級。
#### 線程切換成本:
系統內核調度的對象是線程,因為線程是調度的基本單元(進程是資源擁有的基本單元,進程的切換需要做的事情更多,這里占時不討論進程切換),而**線程的調度只有擁有最高權限的內核空間才可以完成**,所以線程的切換涉及到**用戶空間和內核空間的切換**,也就是特權模式切換,然后需要操作系統調度模塊完成**線程調度(task***struct),*而且除了和協程相同基本的 CPU 上下文,還有線程私有的棧和寄存器等,說白了就是上下文比協程多一些,其實簡單比較下 task_strcut 和 任何一個協程庫的 coroutine 的 struct 結構體大小就能明顯區分出來。而且特權模式切換的開銷確實不小,隨便搜一組測試數據 [3],隨便算算都比協程切換開銷大很多。
**進程占用多少內存**
4g
**線程占用多少內存**
線程跟不同的操作系統版本有有差異
```bash
$ulimit -s
8192
```
單位`kb`
但線程基本都是維持Mb的量級單位,一般是4~64Mb不等, 多數維持約10M上下
**協程占用多少內存**
測試環境
```bash
$ more /proc/cpuinfo | grep "model name"
model name : Intel(R) Core(TM) i7-5775R CPU @ 3.30GHz
model name : Intel(R) Core(TM) i7-5775R CPU @ 3.30GHz
(2個CPU )
$ grep MemTotal /proc/meminfo
MemTotal: 2017516 kB
(2G內存)
$ getconf LONG_BIT
64
(64位操作系統)
$ uname -a
Linux ubuntu 4.15.0-91-generic #92-Ubuntu SMP Fri Feb 28 11:09:48 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
```
測試程序
```go
package main
import (
"time"
)
func main() {
for i := 0; i < 200000; i++ {
go func() {
time.Sleep(5 * time.Second)
}()
}
time.Sleep(10 * time.Second)
}
```
程序運行前
```bash
top - 00:16:24 up 7:08, 1 user, load average: 0.08, 0.03, 0.01
任務: 288 total, 1 running, 218 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2017516 total, 593836 free, 1163524 used, 260156 buff/cache
KiB Swap: 969960 total, 574184 free, 395776 used. 679520 avail Mem
```
free的mem為1163524,
程序運行中
```bash
top - 00:17:12 up 7:09, 1 user, load average: 0.04, 0.02, 0.00
任務: 290 total, 1 running, 220 sleeping, 0 stopped, 0 zombie
%Cpu0 : 4.0 us, 1.0 sy, 0.0 ni, 95.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 8.8 us, 1.4 sy, 0.0 ni, 89.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2017516 total, 89048 free, 1675844 used, 252624 buff/cache
KiB Swap: 969960 total, 563688 free, 406272 used. 168812 avail Mem
```
free的mem為1675844,
所以**20萬個協程占用了約 50萬KB****平均一個協程占用約2.5KB**
那么,go的協程切換成本如此小,占用也那么小,是否可以無限開辟呢?
- 封面
- 第一篇:Golang修養必經之路
- 1、最常用的調試 golang 的 bug 以及性能問題的實踐方法?
- 2、Golang的協程調度器原理及GMP設計思想?
- 3、Golang中逃逸現象, 變量“何時棧?何時堆?”
- 4、Golang中make與new有何區別?
- 5、Golang三色標記+混合寫屏障GC模式全分析
- 6、面向對象的編程思維理解interface
- 7、Golang中的Defer必掌握的7知識點
- 8、精通Golang項目依賴Go modules
- 9、一站式精通Golang內存管理
- 第二篇:Golang面試之路
- 1、數據定義
- 2、數組和切片
- 3、Map
- 4、interface
- 5、channel
- 6、WaitGroup
- 第三篇、Golang編程設計與通用之路
- 1、流?I/O操作?阻塞?epoll?
- 2、分布式從ACID、CAP、BASE的理論推進
- 3、對于操作系統而言進程、線程以及Goroutine協程的區別
- 4、Go是否可以無限go? 如何限定數量?
- 5、單點Server的N種并發模型匯總
- 6、TCP中TIME_WAIT狀態意義詳解
- 7、動態保活Worker工作池設計