Linux多線程編程和Linux 2.6下的NPTL

2021-01-08 電子產品世界

這幾天由於工作需要,琢磨了一下Linux下的多線程的相關資料。Linux下最常用的多線程支持庫為 Pthread庫,它是glibc庫的組成部分。但是關於Pthread的說明文檔非常缺乏,特別是對POSIX多線程規範的介紹以及pthread庫中多線程實現方式的介紹實在是少之又少。而多線程編程對於系統程式設計師而言是必須掌握的技術,因此總是讓學習中的程式設計師覺得頭痛不以。我自己也沒有太多多線程編程的經驗,在這裡只是把自己收集到的一些關於Linux上多線程還算新的資料進行匯總來拋磚引玉,以便相互學習交流。

本文引用地址:http://www.eepw.com.cn/article/148957.htm

這裡順便提一下市面上有的一本介紹多線程的書《Posix 多線程編程》,它是英文版《Programming with POSIX Muiltthread》中譯本,這也是半年前我所能找到的唯一專題介紹多線程編程的書。我個人感覺這本書的前面1/3之一的內容寫的還是不錯的,但是後面的東西就非常晦澀並且有很多明顯的文字錯誤。看看這本書的翻譯者是好幾個人,估計每個人的翻譯能力不同造成了這本書的虎頭蛇尾。因此我不建議大家去買這本書作為聖經收藏。這本書前半步的內容主要圍繞Posix的多線程,介紹的比較精彩的就是幾個多線程編程模型,把多線程的互斥和同步機制介紹的挺酣暢的,推薦一看。這些內容並非這本書首創,早在《UNIX網絡編程》第二卷進程間通信就有了這些經典的介紹,但是能系統的把這些機制結合到多線程編程中來還是有可圈可點之處的。此外畢竟《UNIX網絡編程》兩卷內容太老,書也太厚了,並不是大多數程式設計師所能坐下來細細看的。這裡我還想表達一下對微軟在技術上的不足斥責。在 msdn中platform sdk部分中的windows多線程編程的內容真是簡陋的可笑,只有傻兮兮的建立和退出線程的函數,關於互斥,條件的介紹一概全無。只能在它的 sample代碼中自己去找,sample代碼裡面的線程同步方式居然是做一個死循環來死等,也不知道它把windows賣這麼多錢是幹什麼吃的。 MFC中多線程的封裝倒是看上去像那麼一回事情了,但是我想像不出在如此簡陋的系統api上微軟到底是如何實現出MFC上線程功能的。擁護windows 的人不要在這裡砸雞蛋,最好也能寫一篇windows上的多線程介紹除了。這比砸雞蛋來得有意義多了。 好了,書歸正傳繼續說Linux上的多線程。

在Linux 上,從內核角度而言,基本沒有什麼線程和進程的區別--大家都是進程。一個進程的多個線程只是多個特殊的進程他們雖然有各自的進程描述結構,卻共享了同一個代碼上下文。在Linux上,這樣的進程稱為輕量級進程Light weight process。致此,就是關於線程的總體概念了,我們往往就在了解這個概念的情況下開始我們的多線程編程之旅。這對於多線程編程入門已經足夠了,然而事實上線程卻要複雜的多。首先多線程間的優先級調度,內存資源(棧)分配和信號投遞就不是簡單的共享同一個進程代碼上下文所能所能解決的。其次,效率的問題:如何有效的使用多 cpu資源(2.4內核的多線程就無法使用多個cpu,一個進程的線程都被限制在同一個cpu上運行)。因此多線程庫Pthread的實現並不是一件簡單的事情,它建立在特有的線程模型之上。

在Linux 2.4內核中, Linux內核中使用了一個內核線程來處理用戶態進程中的多個線程的上下文切換(線程切換)。由於內核中並沒有什麼線程組的概念,即一個進程的多個線程,因此必須依靠在pthread庫中實現一個額外的線程來管理其他用戶線程(即用戶程序生成的線程)的建立,退出,資源分配和回收以及線程的切換。由於當時硬體並沒有線程寄存器之類的冬冬來支持多線程,因此線程的切換性能和低下,並且需要引入複雜的機制在進程的棧中為各個線程劃分出各自的棧數據所在位置,並且在切換時進行棧數據拷貝。而最大的問題是內核中缺乏對線程間的同步機制的支持,因此pthread庫不得不在底層依靠信號方式來實現同步,因此線程互斥中的互斥量操作和條件量操作都轉換為進程的信號操作。pthread的實現中充斥了極其複雜的信號操作。大家都知道信號本身是低速的通信方式,因此勢必拖慢了線程的實際性能。最後的問題就是信號處理,還有由於內核對線程的無知,必須由管理線程來接收信號後投遞給相應的線程,一方面是效率低,另外一方面由於信號產生的不確定性(比如讀取一個文件的時候突然出錯了),要準確投遞所有的信號給正確的線程難以保證。

而在IA-32硬體結構中,出現了對線程寄存器的支持,因此Pthread的線程上下文切換速度有了很大提高。但是由於硬體限制局限,線程的數量必須小於8192個,反正我是覺得已經很多了。

於是從2.5代碼開始Linux內核採用了NPTLNative Posix Thread Library)方式。NPTL的設計思想初稿可參考nptl-design.pdf(http://people.redhat.com/drepper/nptl-design.pdf)

首先在IA-32和x86-64位體系結構上能實現任意數量的線程數量。通過引入了TLS系統調用可以建立多個GDT全局描述符表,每個cpu維護一個描述符表,每個表項存放一個線程。

其次,clone系統調用優化了線程的建立和結束功能。也不再需要額外的調度線程的幫助就可以回收線程資源了。

其三,信號投遞由內核完成,而不再需要額外的用戶態管理線程的幫助,而嚴重錯誤信號之間結束整個進程。

其四,引入了新的退出系統調用exit_group()。原來的exit保留用於退出單個線程,exit_group用於退出整個進程。

其五, 新的exec調用會先結束到一個進程中的所有線程後再載入新程序的執行,而不是只結束調用的線程。

其六,所有線程的資源使用情況(cpu資源,內存資源)會報告給整個進程,而不再是只報告給初始化線程

其七,proc文件系統中只顯示初始化線程的信息,而不再是所有線程的信息(上萬個線程會把proc文件系統拖死)

其八, 支持線程脫離, 執行Pthread_join的線程不需要再執行no wait。

其九,由內核來維護初始化線程(變成內核線程了),並在proc文件系統中顯示其狀態,並維護直到所有線程退出來保證信號的投遞。

其十,內核支持無限制的線程數量。

最後,允許pthread_join在子線程已死之後返回,即pthread_join的返回和子線程狀態變成異步的了,提高了性能。

根據報告,NPTL中線程的啟動和中止時間消耗只有Linuxthread的大約1/8,當線程數量急遽增加的時候,消耗時間的差異更加明顯。

在線程間同步試驗中,頻繁進出臨界區的時間消耗只有原來的一半。

更多的用戶測試報告可以看 http://kerneltrap.org/node/422

至於如何在開發中使用NPTL可參考Migrating to Linux kernel 2.6 -- Part 5: Migrating apps to the 2.6 kernel and NPTL(http://linuxdevices.com/articles/AT6753699732.html)。需要做的事情有這麼幾件。

1:使用2.6的內核的系統平臺

2:確定你的gcc支持NPTL

用# getconf GNU_LIBPTHREAD_VERSION命令來查看gcc的編譯時的對多線程的支持方式

如果返回的是linuxthreads-0.10,說明你的gcc不支持NPTL

如果返回的是nptl-0.60這樣的信息,說明你的gcc能用來編譯新的NPTL

3:重新在這樣的系統環境中編譯你的程序,不需要改變程序中對pthread的調用(但是某些函數被取消了)

相關焦點

  • linux多線程之線程資源的釋放
    一般來說,對一段運行代碼進行加鎖然後解鎖,如下所示:pthread_mutex_lock(&mutex);//運行代碼;pthread_mutex_unlock(&mutex);如果在運行代碼這塊發生錯誤,有異常,導致這個線程異常退出,那麼怎麼辦,pthread_unlock
  • Linux下C編程基礎之:本章小結與思考與練習
    3.8 本章小結本章是Linux中進行C語言編程的基礎,首先講解了C語言編程的關鍵點,這裡關鍵要了解編輯器、編譯連結器、調試器及項目管理工具等概念。
  • 從RTOS到Linux的應用移植
    本文介紹了整體的設計思路和一些關鍵問題的實現細節。1 RTOS到Linux的移植分析  幾乎所有的RTOS都有一個簡單的編程模型,它由多線程的執行(通常稱為任務)構成,包含在單一的地址空間中。在RTOS中,單一主程序下多任務同時運行,具有很高的實時響應能力。
  • Linux系統的Linux應該怎麼讀?正確讀法在這裡,很多人都讀錯了!
    1、linux發音五花八門版本頗多,見到和聽到的不下10種。根據linux的創始人Linus Torvalds的說法,Linux的發音和「Minix」是押韻的。2、依照國際音標應該是/'linэks/——類似於「裡訥克斯」。但是,由於Linus Torvalds本人是芬蘭人,所以他的Linux讀音不是/'linэks/,而是/'liniks/.。
  • Linux2.6內核驅動移植參考
    作者:晏渭川 隨著Linux2.6的發布,由於2.6內核做了教的改動,各個設備的驅動程序在不同程度上要 進行改寫。為了方便各位Linux愛好者我把自己整理的這分文檔share出來。該文當列舉 了2.6內核同以前版本的絕大多數變化,可惜的是由於時間和精力有限沒有詳細列出各個 函數的用法。
  • linux下安裝虛擬機,完美在linux系統下運行通達信軟體
    現在越來越多的人使用linux系統,現在很多的國產作業系統都是基於linux內核上的。雖說不少的軟體都可以運行在linux的系統上。但是對於股票軟體來說在linux上的使用是一個硬傷。能夠運行在linux下的國內股票軟體少之又少。
  • 從串口驅動到Linux驅動模型,想轉Linux的必會!
    Linux是一套免費使用和自由傳播的類Unix作業系統,是一個基於POSIX和UNIX的多用戶、多任務、支持多線程和多CPU的作業系統。它能運行主要的UNIX工具軟體、應用程式和網絡協議。它支持32位和64位硬體。Linux繼承了Unix以網絡為核心的設計思想,是一個性能穩定的多用戶網絡作業系統。
  • linux下SCP指令的使用
    通常在Linux下執行遠程拷貝文件使用,他和cp指令類似,只不過cp是本機使用,而SCP則是跨機器使用。SCP傳輸是基於SSH的加密傳輸,也就是說知道ssh的帳密就可以上下載文件了,因此比較安全。SCP常用實例(在linux的centos環境下為例,從192.168.200.10現在一個文件到本地)1、從遠程伺服器下載文件到本地伺服器。scp 用戶名@IP位址:/home/1.txt .
  • Linux下內存洩漏工具
    儘管優秀的編程實踐可以確保最少的洩漏,但是根據經驗,當使用大量的函數對相同的內存塊進行處理時,很可能會出現內存洩漏。  內存洩露可以分為以下幾類:  1. 常發性內存洩漏。發生內存洩漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存洩漏。  2. 偶發性內存洩漏。發生內存洩漏的代碼只有在某些特定環境或操作過程下才會發生。
  • Linux下如何掛載新磁碟:mount
    前言在linux系統伺服器上插了一個U盤,結果系統顯示不出來,是什麼原因導致了系統不能識別U盤呢?經過一番搜索,發現新的硬體設備插在linux系統上時,如果不能正常顯示,則需要通過掛載讓U盤顯示出來,這裡就用到了我們今天要介紹的命令:mount。
  • Linux-Centos下之RabbitMQ快速安裝
    RabbitMQ伺服器是用Erlang語言編寫的,而群集和故障轉移是構建在開放電信平臺框架上的。所有主要的程式語言均有與代理接口通訊的客戶端庫。下載Erlang和RabbitMQ安裝包:rlang-21.1-1.el7.centos.x86_64.rpm和rpm -ivh rabbitmq-server-3.7.8-1.el7.noarch.rpm。
  • 一張圖看懂Linux內核中Percpu變量的實現
    我們在使用各種程式語言進行多線程編程時,經常會用到thread local變量。所謂thread local變量,就是對於同一個變量,每個線程都有自己的一份,對該變量的訪問是線程隔離的,它們之間不會相互影響,所以也就不會有各種多線程問題。正確的使用thread local變量,能極大的簡化多線程開發。所以不管是c/c++/rust,還是java/c#等,都內置了對thread local變量的支持。
  • 17 個 Linux 下用於 C/C++ 的最好的 IDE
    讓我們來看看關於它的特性:C/C++ 編輯器很好的整合了多線程的 GNU GDB 調試工具支持代碼協助支持 C++11 標準它支持多平臺,可以在 Linux、Mac OSX、FreeBSD、OpenBSD、Solaris 和 Windows 上運行,同時支持包括 C/C++ 在內的眾多程式語言。
  • 嵌入式linux新手入門手記-準備qt5.4.1開發平臺
    這裡需要注意一點,我們在交叉編譯qt5.4.1的工具包時,涉及到2種不同的編譯:第一種是使用ubuntu安裝的GCC和G++編譯qmake工具,這些qmake工具將運行在ubuntu下,用於將後續自己編寫的qt應用程式的原始碼編譯成能夠運行在arm平臺上的目標可執行程序;第二種是使用arm-linux-gnueabihf編譯器,將qt5.4.1的庫編譯成運行於arm平臺的靜態庫或者共享庫。
  • Linux下常用的研發工具和環境
    但以上三種UML工具都只能在Windows環境下運行。Linux下,目前常用的UML工具為UMbrello。三、集成開發環境:當前Linux上,已有兩款優秀的集成開發環境KDevelop和Eclipse。Eclipse是目前最著名的一款開源集成開發環境。KDevelop目的是為KDE提供一個易用的集成開發環境(官網:www.kdevelop.org).
  • linux下ntp時鐘同步的搭建和使用(超詳細)
    在生產和學習過程中,是一個最容易被忽略卻又很重要的協議。一、安裝NTP1、linux下NTP的安裝方式有很多種,yum、wget、rpm都可以,選擇自己習慣的方式即可,這裡不再贅述!本人習慣用rpm方式安裝,在伺服器上就需要安裝如下兩個文件。
  • Linux下文件(文件夾)的壓縮和解壓
    前言在linux下,當我們上傳一個較大的文件或者要安裝一個軟體(如tomcat、mysql等)時,我們需要先將官網下載的壓縮包在linux伺服器上進行解壓,再進行安裝。如果是由程序包生成的一個目錄內容較多或較大時,我們還可能需要對某個文件夾需要壓縮,這就需要用到我們本節要介紹的內容,文件(文件夾)的壓縮和解壓。
  • 嵌入式Linux的GDB調試環境建立
    嵌入式Linux的GDB調試環境由Host和Target兩部分組成,Host端使用arm-linux-gdb,Target Board端使用gdbserver。
  • 「linux專欄」嘔心瀝血兩天,就為linux中安裝拼音輸入法
    幾經百度,自我實踐,最後發現最有效的辦法就是使用yum源安裝ibus的拼音輸入法,當然ibus也是眾多linux用戶反響比較穩定的一款輸入法。當然我們也有必要為大家說明一下,就目前來看,linux中常用的中文輸入法平臺有IBus、fcitx和scim。scim現在維護滯後,不推薦使用。
  • 嵌入式Linux開發環境的搭建之:嵌入式開發環境的搭建
    如優龍的FS2410開發光碟裡就附帶了2.95.3和3.3.2兩個版本的交叉編譯器,其中前一個版本是用於編譯Linux2.4內核的,而後一個版本是用於編譯Linux2.6版本內核的。由於這是廠商測試通過的編譯器,因此可靠性會比較高,而且與開發板能夠很好地吻合。所以推薦初學者直接使用廠商提供的編譯器。當然,由於時間滯後的原因,這個編譯器往往不是最新的版本,若需要更新時希望讀者另外查找相關資料學習。