當前位置:編程學習大全網 - 編程語言 - linux下線程屬性常用操作有哪些

linux下線程屬性常用操作有哪些

LinuxThread的線程機制

LinuxThreads是目前Linux平臺上使用最為廣泛的線程庫,由Xavier Leroy (Xavier.Leroy@inria.fr) 負責開發完成,並已綁定在GLIBC中發行。它所實現的就是基於核心輕量級進程的"壹對壹"線程模型,壹個線程實體對應壹個核心輕量級進程,而線程之間的 管理在核外函數庫中實現。

1.線程描述數據結構及實現限制

LinuxThreads定義了壹個struct _pthread_descr_struct數據結構來描述線程,並使用全局數組變量 __pthread_handles來描述和引用進程所轄線程。在__pthread_handles中的前兩項,LinuxThreads定義了兩個全 局的系統線程:__pthread_initial_thread和__pthread_manager_thread,並用 __pthread_main_thread表征__pthread_manager_thread的父線程(初始為 __pthread_initial_thread)。

struct _pthread_descr_struct是壹個雙環鏈表結構,__pthread_manager_thread所在的鏈表僅包括它 壹個元素,實際上,__pthread_manager_thread是壹個特殊線程,LinuxThreads僅使用了其中的errno、p_pid、 p_priority等三個域。而__pthread_main_thread所在的鏈則將進程中所有用戶線程串在了壹起。經過壹系列 pthread_create()之後形成的__pthread_handles數組將如下圖所示:

圖2 __pthread_handles數組結構

新創建的線程將首先在__pthread_handles數組中占據壹項,然後通過數據結構中的鏈指針連入以__pthread_main_thread為首指針的鏈表中。這個鏈表的使用在介紹線程的創建和釋放的時候將提到。

LinuxThreads遵循POSIX1003.1c標準,其中對線程庫的實現進行了壹些範圍限制,比如進程最大線程數,線程私有數據區大小等等。在 LinuxThreads的實現中,基本遵循這些限制,但也進行了壹定的改動,改動的趨勢是放松或者說擴大這些限制,使編程更加方便。這些限定宏主要集中 在sysdeps/unix/sysv/linux/bits/local_lim.h(不同平臺使用的文件位置不同)中,包括如下幾個:

每進程的私有數據key數,POSIX定義_POSIX_THREAD_KEYS_MAX為128,LinuxThreads使用 PTHREAD_KEYS_MAX,1024;私有數據釋放時允許執行的操作數,LinuxThreads與POSIX壹致,定義 PTHREAD_DESTRUCTOR_ITERATIONS為4;每進程的線程數,POSIX定義為64,LinuxThreads增大到1024 (PTHREAD_THREADS_MAX);線程運行棧最小空間大小,POSIX未指定,LinuxThreads使用 PTHREAD_STACK_MIN,16384(字節)。

2.管理線程

"壹對壹"模型的好處之壹是線程的調度由核心完成了,而其他諸如線程取消、線程間的同步等工作,都是在核外線程庫中完成的。在LinuxThreads 中,專門為每壹個進程構造了壹個管理線程,負責處理線程相關的管理工作。當進程第壹次調用pthread_create()創建壹個線程的時候就會創建 (__clone())並啟動管理線程。

在壹個進程空間內,管理線程與其他線程之間通過壹對"管理管道(manager_pipe[2])"來通訊,該管道在創建管理線程之前創建,在成功啟動 了管理線程之後,管理管道的讀端和寫端分別賦給兩個全局變量__pthread_manager_reader和 __pthread_manager_request,之後,每個用戶線程都通過__pthread_manager_request向管理線程發請求, 但管理線程本身並沒有直接使用__pthread_manager_reader,管道的讀端(manager_pipe[0])是作為__clone ()的參數之壹傳給管理線程的,管理線程的工作主要就是監聽管道讀端,並對從中取出的請求作出反應。

創建管理線程的流程如下所示:

(全局變量pthread_manager_request初值為-1)

圖3 創建管理線程的流程

初始化結束後,在__pthread_manager_thread中記錄了輕量級進程號以及核外分配和管理的線程id, 2*PTHREAD_THREADS_MAX+1這個數值不會與任何常規用戶線程id沖突。管理線程作為pthread_create()的調用者線程的 子線程運行,而pthread_create()所創建的那個用戶線程則是由管理線程來調用clone()創建,因此實際上是管理線程的子線程。(此處子 線程的概念應該當作子進程來理解。)

__pthread_manager()就是管理線程的主循環所在,在進行壹系列初始化工作後,進入while(1)循環。在循環中,線程以2秒為 timeout查詢(__poll())管理管道的讀端。在處理請求前,檢查其父線程(也就是創建manager的主線程)是否已退出,如果已退出就退出 整個進程。如果有退出的子線程需要清理,則調用pthread_reap_children()清理。

然後才是讀取管道中的請求,根據請求類型執行相應操作(switch-case)。具體的請求處理,源碼中比較清楚,這裏就不贅述了。

3.線程棧

在LinuxThreads中,管理線程的棧和用戶線程的棧是分離的,管理線程在進程堆中通過malloc()分配壹個THREAD_MANAGER_STACK_SIZE字節的區域作為自己的運行棧。

用戶線程的棧分配辦法隨著體系結構的不同而不同,主要根據兩個宏定義來區分,壹個是NEED_SEPARATE_REGISTER_STACK,這個屬 性僅在IA64平臺上使用;另壹個是FLOATING_STACK宏,在i386等少數平臺上使用,此時用戶線程棧由系統決定具體位置並提供保護。與此同 時,用戶還可以通過線程屬性結構來指定使用用戶自定義的棧。因篇幅所限,這裏只能分析i386平臺所使用的兩種棧組織方式:FLOATING_STACK 方式和用戶自定義方式。

在FLOATING_STACK方式下,LinuxThreads利用mmap()從內核空間中分配8MB空間(i386系統缺省的最大棧空間大小,如 果有運行限制(rlimit),則按照運行限制設置),使用mprotect()設置其中第壹頁為非訪問區。該8M空間的功能分配如下圖:

圖4 棧結構示意

低地址被保護的頁面用來監測棧溢出。

對於用戶指定的棧,在按照指針對界後,設置線程棧頂,並計算出棧底,不做保護,正確性由用戶自己保證。

不論哪種組織方式,線程描述結構總是位於棧頂緊鄰堆棧的位置。

4.線程id和進程id

每個LinuxThreads線程都同時具有線程id和進程id,其中進程id就是內核所維護的進程號,而線程id則由LinuxThreads分配和維護。

  • 上一篇:作為壹個前端開發,vscode有哪些值得安裝的插件
  • 下一篇:星球大戰:舊***和國武士2怎樣得到光劍
  • copyright 2024編程學習大全網