posix 線程(一):線程模型、pthread 系列函數 和 簡單多線程服務器端程序


posix 線程(一):線程模型、pthread 系列函數 和 簡單多線程服務器端程序

 

一、線程有3種模型,分別是N:1用戶線程模型,1:1核心線程模型和N:M混合線程模型,posix thread屬於1:1模型。


(一)、N:1用戶線程模型

“線程實現”建立在“進程控制”機制之上,由用戶空間的程序庫來管理。OS內核完全不知道線程信息。這些線程稱為用戶空間線程。這些線程都工作在“進

程競爭范圍”(process contention scope):各個線程在同一進程競爭“被調度的CPU時間”(但不直接和其他進程中的線程競爭)。



在N:1線程模型中,內核不干涉線程的任何生命活動,也不干涉同一進程中的線程環境切換。

在N:1線程模型中,一個進程中的多個線程只能調度到一個CPU,這種約束限制了可用的並行總量。

第二個缺點是如果某個線程執行了一個“阻塞式”操作(如read),那么,進程中的所有線程都會阻塞,直至那個操作結束。為此,一些線程的實現是為

這些阻塞式函數提供包裝器,用非阻塞版本替換這些系統調用,以消除這種限制。


(二)、1:1核心線程模型 pthread線程庫--NPTL(Native POSIX Threading Library)

在1:1核心線程模型中,應用程序創建的每一個線程(也有書稱為LWP)都由一個核心線程直接管理。OS內核將每一個核心線程都調到系統CPU上,

因此,所有線程都工作在“系統競爭范圍”(system contention scope):線程直接和“系統范圍”內的其他線程競爭。

這種線程的創建與調度由內核完成,因為這種線程的系統開銷比較大(但一般來說,比進程開銷小)




(三)、N:M混合線程模型  NGPT(Next Generation POSIX Threads)

N:M混合線程模型提供了兩級控制,將用戶線程映射為系統的可調度體以實現並行,這個可調度體稱為輕量級進程(LWP:light weight process),LWP
再一一映射到核心線程。如下圖所示。OS內核將每一個核心線程都調到系統CPU上,因此,所有線程都工作在“系統競爭范圍”。


據說一些類UNIX系統(如Solaris)已經實現了比較成熟的M:N線程模型, 其性能比起linux的線程還是有着一定的優勢,但不能利用SMP結構。

按照2003年3月NGPT官方網站上的通知,NGPT考慮到NPTL日益廣泛地為人所接受,為避免不同的線程庫版本引起的混亂,今后將不再進行進一步開發,而今進行支持性的維護工作。也就是說,NGPT已經放棄與NPTL競爭下一代Linux POSIX線程庫標准。


二、posix 線程概述

我們知道,進程在各自獨立的地址空間中運行,進程之間共享數據需要用進程間通信機制,有些情況需要在一個進程中同時執行多個控制流程,這時候

線程就派上了用場,比如實現一個圖形界面的下載軟件,一方面需要和用戶交互,等待和處理用戶的鼠標鍵盤事件,另一方面又需要同時下載多個文

件,等待和處理從多個網絡主機發來的數據,這些任務都需要一個“等待-處理”的循環,可以用多線程實現,一個線程專門負責與用戶交互,另外幾個線

程每個線程負責和一個網絡主機通信。


以前我們講過,main函數和信號處理函數是同一個進程地址空間中的多個控制流程,多線程也是如此,但是比信號處理函數更加靈活,信號處理函數的

控制流程只是在信號遞達時產生,在處理完信號之后就結束,而多線程的控制流程可以長期並存,操作系統會在各線程之間調度和切換,就像在多個進

程之間調度和切換一樣。由於同一進程的多個線程共享同一地址空間,因此Text Segment、Data Segment都是共享的,如果定義一個函數,在各線程

中都可以調用,如果定義一個全局變量,在各線程中都可以訪問到,除此之外,各線程還共享以下進程資源和環境:


文件描述符表

每種信號的處理方式(SIG_IGN、SIG_DFL或者自定義的信號處理函數)

當前工作目錄

用戶id和組id


但有些資源是每個線程各有一份的:

線程id

上下文,包括各種寄存器的值、程序計數器和棧指針

棧空間

errno變量

信號屏蔽字

調度優先級


我們將要學習的線程庫函數是由POSIX標准定義的,稱為POSIX thread或者pthread。在Linux上線程函數位於libpthread共享庫中,因此在編譯時要加上-lpthread選項。


注:linux 2.6 以后的線程就是由用戶態的pthread庫實現的.使用pthread以后在用戶看來每一個task_struct就對應一個線程而一組線程以及它們所共同引用的一組資源就是一個進程.在linux 2.6中, 內核有了線程組的概念, task_struct結構中增加了一個tgid(thread group id)字段. getpid(獲取進程ID)系統調用返回的也是tast_struct中的tgid, 而tast_struct中的pid則由gettid系統調用來返回。

當線程停止/繼續, 或者是收到一個致命信號時, 內核會將處理動作施加到整個線程組中。
比 如程序a.out運行時,創建了一個線程。假設主線程的pid是10001、子線程是10002(它們的tgid都是10001)。這時如果你kill 10002,是可以把10001和10002這兩個線程一起殺死的,盡管執行ps命令的時候根本看不到10002這個進程。如果你不知道linux線程背 后的故事,肯定會覺得遇到靈異事件了。
 
 

參考:

《linux c 編程一站式學習》

《UNP》

《APUE》

http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/index.html

http://hi.baidu.com/_kouu/item/282b80a933ccc3a829ce9dd9

http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/


免責聲明!

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



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