作業系統建立執行緒
Unix程序的設計思想,實現了 fork exec wait exit 四個精巧的系統呼叫來支援對程序的靈活管理。
父程序程序透過 fork 系統呼叫建立自身的副本(子程序);
稱為“子程序”的副本可呼叫 exec 系統呼叫用另一個程式覆蓋其記憶體空間,這樣就可以執行新程式了;
子程序執行完畢後,可透過呼叫 exit 系統呼叫來退出並通知父程序;
父程序透過呼叫 wait 系統呼叫來等待子程序的退出
我們為什麼不設計一個系統呼叫介面同時實現建立一個新程序並載入給定的可執行檔案兩種功能
拆分為兩個系統呼叫後,可以靈活地支援 重定向 (Redirection) 等功能
但DOS/WINDOWS還有spawn類函式,
因為DOS是單任務的系統,它只能將"父程序"駐留在機器內再執行"子程序",這就是spawn類的函式
Windows 中, CreateProcess 函式用來建立一個新的程序和它的主執行緒,透過這個新程序執行指定的可執行檔案
posix-spawn
POSIX 標準的 posix_spawn 系統呼叫則類似 Windows 的 CreateProcess 函式,不過對引數進行了簡化
posix-spawn是基於POSIX標準的程序建立方法,可以方便地替代傳統的fork()和exec()呼叫
開發語言建立執行緒-併發
倉頡語言中透過關鍵字 spawn 建立倉頡執行緒-即使用者態協程
fork:除了必要的啟動資源外,其他變數,包,資料等都繼承自父程序,並且是copy-on-write的,也就是共享了父程序的一些記憶體頁,因此啟動較快,
但是由於大部分都用的父程序資料,所以是不安全的程序
spawn:從頭構建一個子程序,父程序的資料等複製到子程序空間內,擁有自己的Python直譯器,
所以需要重新載入一遍父程序的包,因此啟動較慢,由於資料都是自己的,安全性較高
POSIX標準中的 posix_spawn 或者
Linux 上的 clone 系統呼叫
###程序方式
執行緒的並行: 獨立的地址空間 獨立的資源 相互隔離
執行緒的併發:程序的併發性指的是多個程序在同一時間段內交替執行的能力
程序
1.程序本身-組成:程序內部由哪些部分構成 Info
程序是動態--需要相應硬體資源支援--時間和空間的生命週期- 程序狀態轉換功能。
即硬體/虛擬資源 進行 動態繫結和解綁
程序ID 程序描述資料塊PCB 程序實體(程式段-資料段和程序控制塊Process Control Block)
生命週期狀態::包括 建立程序、就緒狀態、執行狀態、阻塞程序、終止程序等 程序資源回收機制
2.程序相互制約--通訊_多個程序之間的組織方式問題
同步
互斥
臨界資源和臨界區 訊號量和P/V值 共享資源
競態條件 死鎖:
程序通訊-程序之間的資訊交換
1.共享儲存-透過對這片共享空間進行寫/讀操作實現程序之間的資訊交換
2.訊息傳遞:程序透過系統提供的傳送訊息和接收訊息兩個原語進行資料交換
3.管道通訊:管道通訊是訊息傳遞的一種特殊方式 (互斥、同步和確定對方的存在) I/O重定向 與 管道(pipe)
4.訊號(Signals) 忽略 捕獲 終止
方式: 順序方式 連結方式 索引方式
連結方式和索引方式: 執行指標 就緒佇列指標 阻塞佇列指標
程序控制: 建立新程序、撤銷已有程序、實現程序狀態轉換等功能 執行具有原子性
用“關中斷指令”和“開中斷指令”這兩個特權指令實現原子性
3.程序排程-排程(scheduling)
指標: 週轉時間(turn around),即程序完成時間(completion)與程序到達時間(arrival)的差值
排程機制
最短完成時間優先 STCF—_Shortest Time to Complet First
先來先服務FIFO
可搶佔特性
基於時間片的輪轉
最早截止時間優先(Earliest Deadline First,EDF)演算法
作業系統透過硬體中斷機制,支援分時多工和搶佔式排程機制
4.實現:
設計一些資料結構包含的內容及介面
基於應用名的應用連結、載入器、程序識別符號 PidHandle、任務控制塊 TaskControlBlock、工作管理員 TaskManager、處理器管理結構 Processor
執行緒的資料不一致問題和競態條件問題的根本原因是 排程的不可控性
作業系統抽象: 地址空間 、 程序 以及 檔案
檔案 -檔案描述符
核心提供的 sys_open 系統呼叫讓該檔案在程序的檔案描述符表中佔一項,並得到作業系統的返回值–檔案描述符
管道: 新的系統呼叫 sys_pipe 和 sys_close 基於檔案的管道
程序能夠透過一個統一的介面來讀寫核心管理的持久儲存裝置,這樣資料可以方便地被長久儲存。
程序控制塊中有一個 檔案描述符表 ,在表中儲存著多個 檔案 記錄資訊。
每個檔案描述符是一個非負的索引值,即對應檔案記錄資訊的條目在檔案描述符表中的索引
基於檔案的統一I/O抽象,將標準輸入/標準輸出的訪問改造為基於檔案描述符,然後同樣基於檔案描述符實現一種父子程序之間的通訊機制——管
將程序對於標準輸入輸出的訪問修改為基於檔案抽象的介面
檔案操作
開啟(open) :程序只有開啟檔案,作業系統才能返回一個可進行讀寫的檔案描述符給程序,程序才能基於這個值來進行對應檔案的讀寫;
讀(read):程序可以基於檔案描述符來讀檔案內容到相應記憶體中;
寫(write):程序可以基於檔案描述符來把相應記憶體內容寫到檔案中;
關閉(close) :程序基於檔案描述符關閉檔案後,就不能再對檔案進行讀寫操作了,這樣可以在一定程度上保證對檔案的合法訪問;
核心中支援命令列引數的解析和傳遞
IO-裝置操作標識
讓應用能便捷地訪問外設
備驅動程式完成哪些功能:裝置掃描/發現 裝置初始化 通知裝置 接收裝置通知 解除安裝裝置驅動時回收裝置驅動資源
裝置的硬體規範:
作業系統重點關注的是如何對I/O裝置進行管理,軟體程式設計師則主要關注I/O裝置為軟體提供的介面(interface)
件工程師看來,I/O裝置就是一堆晶片、電源和其他電路的組
Python 多程序
根據不同的平臺, multiprocessing 支援三種啟動程序的方法
1.在 Windows 和 macOS 上: spawn 父程序會啟動一個新的 Python 直譯器程序
2.在 POSIX 系統上可用 : fork POSIX 上為預設值父程序使用 os.fork() 來產生 Python 直譯器分叉(除 macOS 之外的)
fork 的程式碼應當顯式地透過 get_context() 或 set_start_method() 來指定
3.支援透過 Unix 管道傳遞檔案描述符的 POSIX 平臺上可用,例如 Linux
當程式啟動並選擇 forkserver 啟動方法時,將產生一個伺服器程序
multiprocessing.set_start_method('spawn')
或者
ctx = multiprocessing.get_context('spawn')
multiprocessing 支援程序之間的兩種通訊通道 :佇列和管道: Queue 類是一個近似 queue.Queue 的克隆 Pipe() 函式返回一個由管道連線的連線對
Pool 物件,它提供了一種快捷的方法,賦予函式並行化處理一系列輸入值的能力,可以將輸入資料分配給不同程序處理(資料並行)
程序池: concurrent.futures.ProcessPoolExecutor
生成程序:
建立一個 Process 物件然後呼叫它的 start() 方法來生成程序
程序通訊:
佇列和管道
程序間同步
共享記憶體: 可以使用 Value 或 Array 將資料儲存在共享記憶體對映中
服務程序
Manager() 返回的管理器物件控制一個服務程序,該程序儲存Python物件並允許其他程序使用代理操作它們
name pid is_alive()
start() run()
join()
terminate() kill() close()
Python 多執行緒
_thread --- 低層級多執行緒 API
threading ---高層級多執行緒 API
sched 模組定義了一個實現通用事件排程程式的類
sched模組還支援處理一些常見的用例,如定時任務、週期性任務和時間限制的任務
例項化定時器--enter() 方法執行定時任務
延遲執行
固定時間執行
執行多個任務--不同優先順序執行不同任務
popen 的返回值是個標準 I/O 流 pclose 來終止
popen相當於是先建立一個管道,fork,關閉管道的一端,執行exec,返回一個標準的io檔案指標。
system相當於是先後呼叫了fork, exec,wait來執行外部命令
popen本身是不阻塞的,要透過標準io的讀取使它阻塞
system本身就是阻塞的。
I/O模型
程式語言的io介面,其實是依賴底層的作業系統的實現
IO從概念上來說,總共有5種:
(1)阻塞IO (blocking I/O)
(2)非阻塞IO (nonblocking I/O)
(3)IO多路複用 (I/O multiplexing (select and poll))
(4)事件驅動IO (signal driven I/O (SIGIO))
(5)非同步IO (asynchronous I/O (the POSIX aio_functions))
準備資料到核心的緩衝區0--從緩衝區複製到使用者空間
select,poll屬於第三種-IO多路複用模型,
iocp屬於第5種非同步io模型
epoll,kqueue epoll和kqueue是更先進的IO複用模型
I/O多路複用模型就是透過一種機制,
一個程序可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。
POSIX API
Windows API
程序管理介面:
exit:程序終止
fork:建立子程序
wait:等待子程序結束
檔案管理介面:
open:開啟檔案
close:關閉檔案
read:讀取檔案內容
write:寫入檔案內容
目錄管理介面:
opendir:開啟目錄
readdir:讀取目錄項
closedir:關閉目錄
網路通訊介面:
socket:建立套接字
bind:繫結套接字到地址
listen:監聽傳入連線
accept:接受傳入連線
執行緒和同步介面:
pthread_create:建立執行緒
pthread_join:等待執行緒結束
pthread_mutex_lock:加鎖互斥量
pthread_cond_signal:傳送條件變數訊號
訊號處理介面:POSIX訊號庫
signal:註冊訊號處理函式
kill:向程序傳送訊號
sigaction:設定訊號處理動作
時間和日期介面:
time:獲取當前時間
clock:獲取時鐘時間
strftime:格式化時間
共享記憶體介面:
shmget:獲取共享記憶體識別符號
shmat:連線到共享記憶體
shmdt:分離共享記憶體
ioctl 介面:
ioctl:控制裝置操作
用 fcntl() 和ioctl() 的介面。
epoll 是Linux核心中實現的一種可擴充套件的I/O事件通知機制,
unix為kqueue)
FreeBSD中類似的實現是kqueue
種IO複用模式之select,poll,epoll,kqueue,iocp分析
POSIX
POSIX 是 可移植作業系統介面 Portable Operating System Interface
系統服務應用程式程式設計介面
POSIX主要分為四個部分:Base Definitions、System Interfaces、Shell and Utilities和Rationale
程式設計
裸機程式設計、 Linux 驅動程式設計以及 Linux 應用程式設計
Linux應用程式設計:
核心態和使用者態,應用程式執行在使用者態、而核心則執行在核心態。
系統呼叫是核心提供給應用層的程式設計介面,屬於系統核心的一部分
庫函式是屬於應用層-執行在使用者空間
開發 Linux 應用程式,
透過呼叫核心提供的系統呼叫
或使用 C 庫函式來開發具有相應功能的應用程式
Linux系統呼叫介面非常精簡(只有250個左右),
Linux 3.2.0版本提供了380個系統呼叫,而FreeBSD 8.0提供的系統呼叫超過450個
併發程式設計
Producer-Consumer模式、Pipeline模式和Future模式
Future模型是將非同步請求和代理模式結合的產物
future 本質上表達一個還沒有準備好的值
future、promise、delay 和 deferred 是指用於在某些併發程式語言中同步程式執行的構造。
future和promise起源於函數語言程式設計和相關範例,
目的是將值(future)與其計算方式(promise)分離
future最大的好處是將函式的同步呼叫轉換為非同步呼叫,適用於一個交易需要多個子呼叫且這些子呼叫沒有依賴的場景
C++ 非同步程式設計
std::promise 是一個物件,可以儲存某一型別 T 的值,該值在未來某個時間點可以被檢索。
std::future 代表一個非同步操作的結果
C++20中引入了coroutine,從語言層面而不僅是實現方法層面實現非同步操
在Cangjie 倉頡程式語言
倉頡執行緒會返回一個Future<T>型別的結果
Future 和 Thread 型別
在Rust中,Future作為一個介面在rust 1.36加入標準庫中
async:產生一個 Future 物件
在Java中 Future 模式是一種在併發程式設計中廣泛使用的設計模式,用於表示一個可能還沒有完成的非同步操作的結果
在Java中,Future介面在java.util.concurrent包
在Go
程式設計中經常遇到在一個流程中需要呼叫多個子呼叫的情況,Go標準庫並未直接提供Future或Promise這樣的抽象概念
這些子呼叫相互之間沒有依賴,如果序列地呼叫,則耗時會很長,此時可以使用Go併發程式設計中的future模式
Python
future物件,一般不會直接使用,它是task類的基類,task一部分的功能是future提供的,其是更低階的介面
concurrent.futures 模組
Future物件是submit任務(即帶有引數的functions)到executor的例項
current.Futures提供了兩種Executor的子類,各自獨立操作一個執行緒池和一個程序池。這兩個子類分別是:
concurrent.futures.ThreadPoolExecutor(max_workers)
concurrent.futures.ProcessPoolExecutor(max_workers)
在Scala中,可以使用Futures進行並行網路呼叫。
Future是一個表示非同步計算結果的抽象,
bind和callback,類似與boost庫的bind和function
語言的併發程式設計基礎和併發設計模式
併發是指同時執行多個任務的能力,可以透過多執行緒或多程序來實現
併發程式設計基礎
併發設計模式
非同步是指一個任務的完成不會阻塞其他任務的執行
非同步操作可以利用非阻塞的方式處理任務,
當任務在執行時,可以繼續執行其他任務,
當任務完成時,可以透過回撥或事件通知來處理任務的結果
某些情況下,併發和非同步可以結合在一起使用-非同步通常表現為回撥的形式
響應式程式設計是一種處理非同步資料流和事件流的模式。它強調對變化的響應和資料流的處理
Reactor模式 透過事件驅動和回撥機制,簡化了非同步程式設計的複雜性,使得開發者能夠更加專注於業務邏輯的實現,
而不需要過多關注底層的執行緒管理和同步機制
Reactive 程式設計分為資料來源準備、資料流建模、排程者分配三個基本設計步驟,才能實現非同步併發執行
Reactive響應式(反應式)程式設計是一種新的程式設計風格,
其特點是非同步或併發、事件驅動、推送PUSH機制以及觀察者模式的衍生
生產者 消費者 資料處理 back pressire:背壓
併發設計模式
Producer-Consumer模式(生產者-消費者) 非常經典的多執行緒併發協作的模型 解藕,非同步,平衡速度差異
Thread Pool模式(執行緒池): 這個模式就是為減少開啟與關閉執行緒帶來的開銷
Master-Slave模式(主僕)
Pipeline模式(流水線)
Promise模式幫我們不需要等待耗時的求值操作,而是拿一個憑證。我們可以先去做些別的,最後再回來取值
mmutable Object模式(不可變物件): 過將物件變為只讀的形式來保證執行緒安全,是比較好的無鎖實現
參考
https://man7.org/linux/man-pages/man2/syscalls.2.html
https://github.com/JnuSimba/LinuxSecNotes/tree/master