[TOC]
## 概念說明
### 1. 內核空間、用戶空間
操作系統的核心是內核,獨立于其他應用程序,可以訪問底層會保護的硬件,Linux**為了防止用戶進程直接操作內核**,將虛擬地址空間,分成了用戶空間和內核空間,用戶空間就是用戶進程所在的空間。
### 2. 進程切換
為了控制進程的執行,內核必須有能力掛起正在CPU上運行的進程,并恢復以前掛起的某個進程的執行。這種行為被稱為進程切換。因此可以說,任何進程都是在操作系統內核的支持下運行的,是與內核緊密相關的
從一個進程的運行轉到另一個進程上運行,這個過程中經過下面這些變化:
> 1. 保存處理機上下文,包括程序計數器和其他寄存器。
> 2. 更新PCB信息。
> 3. 把進程的PCB移入相應的隊列,如就緒、在某事件阻塞等隊列。
> 4. 選擇另一個進程執行,并更新其PCB。
> 5. 更新內存管理的數據結構。
> 6. 恢復處理機上下文。
### 3. 進程的阻塞
> 正在執行的進程,由于期待的某些事件未發生,如請求系統資源失敗、等待某種操作的完成、新數據尚未到達或無新工作做等,則由系統自動執行阻塞原語(Block),使自己由運行狀態變為阻塞狀態。可見,進程的阻塞是進程自身的一種主動行為,也因此只有處于運行態的進程(獲得CPU),才可能將其轉為阻塞狀態。`當進程進入阻塞狀態,是不占用CPU資源的`。
### 4. 進程緩存區、內核緩沖區
緩沖區的出現是為了減少頻繁的系統調用,由于系統調用需要保存之前的進程數據和狀態等信息,而結束調用之后回來還需要回復之前的信息,為了減少這種耗時耗性能的調用于是出現了緩沖區。在linux系統中,每個進程有自己獨立的緩沖區,叫做**進程緩沖區**,而系統內核也有個緩沖區叫做**內核緩沖區**。
**操作系統使用read函數把數據從內核緩沖區復制到進程緩沖區,write把數據從進程緩沖區 復制到內核緩沖區中**
### 5. 文件描述符fd
文件描述符(File descriptor)是計算機科學中的一個術語,`是一個用于表述指向文件的引用的抽象化概念`。 文件描述符在形式上是一個非負整數。實際上,`它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表`。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞著文件描述符展開
## Linx/Unix 5種IO模型
當一個io發生時候的,涉及到的步驟和對象
以網絡socket的 read為例子。
涉及到的對象
- 一個是調用這個IO的process (or thread) (用戶進程)
- 一個就是系統內核(kernel)
經歷的步驟
- 等待數據準備,比如accept(), recv()等待數據
- 將數據從內核拷貝到進程中, 比如 accept()接受到請求,recv()接收連接發送的數據后需要復制到內核,再從內核復制到進程**用戶空間**
### 阻塞IO

> 當用戶進程調用了recvfrom這個系統調用,kernel就開始了IO的第一個階段:準備數據(對于網絡IO來說,很多時候數據在一開始還沒有到達。比如,還沒有收到一個完整的UDP包。這個時候kernel就要等待足夠的數據到來)。這個過程需要等待,也就是說數據被拷貝到**操作系統內核的緩沖區**中是需要一個過程的。而在用戶進程這邊,整個進程會被阻塞(當然,是進程自己選擇的阻塞)。當kernel一直等到數據準備好了,它就會**將數據從kernel中拷貝到用戶內存**,然后kernel返回結果,用戶進程才解除block的狀態,重新運行起來。
### 非阻塞IO

當用戶進程發出read操作時,如果kernel中的數據還沒有準備好,**那么它并不會block用戶進程,而是立刻返回一個error**。從用戶進程角度講 ,它發起一個read操作后,并不需要等待,而是馬上就得到了一個結果。用戶進程判斷結果是一個error時,它就知道數據還沒有準備好,于是它可以再次發送read操作。一旦kernel中的數據準備好了,并且又再次收到了用戶進程的system call,那么它馬上就將數據拷貝到了用戶內存,然后返回
### I/O 多路復用( IO multiplexing)
IO multiplexing就是我們說的select,poll,epoll,有些地方也稱這種IO方式為event driven IO。select/epoll的好處就在于單個process就可以同時處理多個網絡連接的IO。它的基本原理就是select,poll,epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有數據到達了,就通知用戶進程。

在一個調用中阻塞`select`,等待數據報套接字可讀。當`select`?返回套接字可讀時,我們然后調用`recvfrom`?將數據報復制到我們的應用程序緩沖區中 .使用`select`需要兩次系統調用而不是一次
在IO multiplexing Model中,實際中,**對于每一個socket,一般都設置成為non-blocking,因為只有設置成non-blocking 才能使單個線程/進程不被阻塞(或者說鎖住),可以繼續處理其他socket。如上圖所示,整個用戶的process其實是一直被block的。只不過process是被select這個函數block,而不是被socket IO給block。**
### 異步 I/O

用戶進程發起read操作之后,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到一個asynchronous read之后,首先它會立刻返回,所以不會對用戶進程產生任何block。然后,kernel會等待數據準備完成,然后將數據拷貝到用戶內存,當這一切都完成之后,kernel會給用戶進程發送一個signal,告訴它read操作完成了
### 異步、同步、阻塞、非阻塞
同步就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成后,依賴的任務才能算完成,這是一種可靠的任務序列
異步是不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什么工作,依賴的任務也立即執行,只要自己完成了整個任務就算完成了
阻塞調用是指調用結果返回之前,當前線程會被掛起,一直處于等待消息通知,不能夠執行其他業務
非阻塞調用指在不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回
異步、同步是發生在用戶空間內,當用戶發起一個IO的調用的時候,同步的時候,如果這個操作比較耗時間,會阻塞后面的流程
```php
file_get_contents("http://www.qq.com/");
echo "end";
```
調用read的操作的時候。后面的操作echo 會等待上面的結果完成,才能繼續。
```php
aysnc_read("http://www.qq.com",function($data){
echo $data;
})
echo "end";
```
這個aysnc_read 是一個異步讀的操作,當讀的時候,底下的操作不會阻塞住,會先輸出end。當數據到達的時候,再echo $data;
阻塞、非阻塞、發送在內核和用戶空間之間。阻塞是指操作系統會掛起進程,直到數據準備好,非阻塞、操作系統不阻塞,當前進程可以繼續執行。
舉例說明
- 阻塞io
張三去書店買書,然后問書店問老板,有沒有《紅樓夢》,老板說我查下,這個查詢的時間,比較長,然后張三啥都不能干,就在等著。直到老板告訴它,找到了。然后買了這個書,走了。張三的操作都是同步阻塞的,必須等待老板的結果,下面的操作才能執行。
- 非阻塞IO
還是張三去買書,老板去查詢。這是時候,張三可以玩手機,然后隔段時間問,找到了沒有,張三的進程沒有被阻塞。但是這個任務是同步的,必須等待這個結果。就是老板沒有告訴張三結果,張三是不能離開干其他的事。這個過程是同步非阻塞的。
- 異步IO
張三去買書。然后去書店問老板有沒有了。老板需要查詢,張三告訴老板自己的手機號,找到了打電話給我,然后就去干其他的事了。這個過程是異步的。張三的進程沒有被阻塞在這個買書的環節上。這就是異步非阻塞。
- PC
- IO模型
- Inode介紹
- Linux
- Linux基本操作命令
- Linux網絡相關命令
- Crontab計劃任務
- Shell
- Sed命令
- Awk命令
- LAMP/LNMP
- PHP
- 基本語法
- 面向對象
- 錯誤和異常處理
- 命名空間
- PHP7
- 正則表達式
- Hashtable
- 變量的內部實現
- PHP-FPM
- PHP運行原理
- swoole
- mysql
- SQL標準
- mysql三范式
- 存儲引擎
- Mysql事務
- Mysql索引
- Mysql優化
- Explain
- MySQL索引原理及慢查詢優化
- MongoDb
- 計算機網絡
- IP協議
- TCP(傳輸控制協議)
- UDP(用戶數據報協議)
- HTTP 協議
- HTTPS
- HTTP的基本優化
- Websocket協議
- 版本控制器
- Git
- Svn
- 數據結構
- 數組
- 鏈表
- 算法