線程的概念

線程(LWP,light weight process)是輕量級的進程,本質仍是進程(在類unix環境下)。進程有獨立地址空間,擁有PCB;線程也有PCB,但沒有獨立的地址空間(共享)。在Linux下,線程是最小的執行單位;進程是最小分配資源單位,可看成是只有一個線程的進程。

1Linux內核線程實現原理

類Unix系統中,早期是沒有“線程”概念的,80年代才引入,借助進程機制實現出了線程的概念。因此在這類系統中,進程和線程關系密切。類unix操作系統和Windows操作系統的線程實現原理不一樣,類unix操作系統的線程是依賴于進程實現的。

輕量級進程(light-weight process),也有PCB,創建線程使用的底層函數和進程一樣,都是clone,即fork和pthread_creat調用的底層函數都是clone。剛fork之后,子進程與父進程的大多數內容都是相同的,遵從讀時共享,寫時復制的原則,即子進程有開辟屬于自己空間的能力,擁有自己獨立的進程地址空間;而進程在pthread_creat后,產生的線程卻無法從該地址空間中獨立出去,它們(同一個進程中的所有線程)共享這一個地址空間,當然它們所屬于自己的PCB,PCB是相互獨立的。同一個進程的所有線程的PCB都保存有相同的頁表,因此線程共享虛擬地址空間。

從內核里看進程和線程是一樣的,都有各自不同的PCB,但是PCB中指向內存資源的三級頁表是相同的,因此同一個進程中的多個線程在對同一個虛擬地址(線性地址)經過MMU變換為的物理地址都是同樣的,則它們共享同一個虛擬地址空間。但是,每個線程又必須有屬于自己的任務(指令集合,這些是各自獨立的),這些都保存在寄存器和棧中,它們又保存在各自的PCB中。所有的函數占據的地址空間都為棧空間(向下增長),整個棧空間都是由一個個棧幀組成。每一個函數有屬于自己的棧幀空間,棧幀空間里面存儲了函數的局部變量、函數的形參和臨時值。如進程中的某一個線程有屬于自己執行的函數:

int main ( )????????????

{

?? myprint( );

?? return 0;

}

void myprint( )

{

?? func( );

}

則main函數、myprint函數和func函數都位于棧空間(stack),每個函數都占據一個棧幀,如下:

三個棧幀空間連續分配,每個棧幀空間由ebp(頭)和esp(尾)共同決定,它們各自存儲在一個寄存器中,當main函數調用myprint函數時,此時兩個寄存器的值變為第二個棧幀的頭和尾,但是需要該函數調用完需要返回到main函數中,因此還需要把main函數的棧幀空間的頭尾保存起來,這個就是臨時值,保存在main函數的棧幀空間中。myprint函數調用func函數時同理,myprint棧幀空間的頭尾作為臨時值保存到myprint棧幀空間中。綜上線程可以看做是寄存器和棧的集合。

在內核空間也有一個棧空間,稱為內核棧,用于保存寄存器的值。如進程或者線程在切換時,需要保存寄存器的值,而這些值都保存在內核的棧空間中,因此每個線程也都有自己獨立的內核棧空間。局部變量都存儲于棧空間中(用戶棧空間),而全局變量存儲在寄存器中,切換時保存到了內核棧空間中。

進程可以蛻變成線程。一個進程創建一個線程時,自己本身也蛻變成為了一個線程。

在linux下,線程最是小的執行單位;進程是最小的分配資源單位。

三級映射:進程PCB→頁目錄(可看成數組,首地址位于PCB中)→頁表→物理頁面 →內存單元。

實際上,無論是創建進程的fork,還是創建線程的pthread_create,底層實現都是調用同一個內核函數clone。如果復制對方的地址空間,那么就產出一個“進程”;如果共享對方的地址空間,就產生一個“線程”。因此Linux內核是不區分進程和線程的。只在用戶層面上進行區分。所以,線程所有操作函數 pthread_* 是庫函數,而非系統調用。

2)線程號與線程ID

查看某一個進程對應的所有線程的線程號(LWP號):ps –Lf pid;如下圖所示。線程號是Linux內核劃分給線程CPU時間輪片的依據。UID為線程所屬用戶ID;PID為線程對應的進程ID,PPID進程的父進程ID,LWP為線程號,NLWP為該進程中線程的數目(下面3291號進程的線程數為3)。注意區分線程號與線程ID,兩者不是一個概念。Linux內核就是根據線程號來劃分CPU時間輪片的,而線程的ID用于區分線程。

線程ID是進程內部識別標志(兩個進程間,線程ID允許相同,但是線程號必須不同,因為線程號是CPU時間片劃分依據);另外,線程ID明顯比線程號和進程ID大得多(可通過ps –Lf pid查看),而線程號和進程ID數量級一樣,且線程號和進程ID都不能相同,線程ID在不同進程中可以相同。

[root@localhost 01_pthread_test]# ps -Lf 3291

UID?? ?PID? ??PPID ?LWP? ?C ?NLWP ?STIME ??TTY???? STAT?? TIME CMD

gdm??? 3291?? 3283?? 3291? ??0? ?3 14:18 ??? ?Sl?? ?0:00 /usr/libexec/ibus-dconf

gdm?? ?3291?? 3283?? 3297? ??0?? 3 14:18 ??? ?Sl??? 0:00 /usr/libexec/ibus-dconf

gdm?? ?3291?? 3283?? 3298? ??0 ??3 14:18 ???? Sl??? 0:00 /usr/libexec/ibus-dconf

3)線程共享資源

1.文件描述符表(共享打開的文件);

2.每種信號的處理方式;

3.當前工作目錄;

4.用戶ID和組ID;

5.內存地址空間 (.text/.data/.bss/heap(堆)/共享庫),即用戶空間中除了棧空間stack部分;

6.共享頁表。

4)線程非共享資源

1.線程id;

2.處理器現場(寄存器)和棧指針(指向內核棧);

3.獨立的棧空間(用戶空間棧),即函數運行所需要的空間;

4.errno變量(全局變量),注意雖然位于.data段,但是非共享;

5.信號屏蔽字;

6.調度優先級。

5)線程優缺點

優點:提高程序并發性;開銷小;數據通信、共享數據方便。

缺點:庫函數,不穩定;調試、編寫困難、gdb不支持;對信號支持不好。

優點相對突出,缺點均不是硬傷。Linux下由于實現方法導致進程、線程差別不是很大。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385305.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385305.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385305.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

1001. 害死人不償命的(3n+1)猜想 (15)

卡拉茲(Callatz)猜想: 對任何一個自然數n,如果它是偶數,那么把它砍掉一半;如果它是奇數,那么把(3n1)砍掉一半。這樣一直反復砍下去,最后一定在某一步得到n1。卡拉茲在1950年的世界數學家大會上公布了這個猜…

海量數據處理 (一)

現有海量日志數據保存在一個超級大的文件中,該文件無法直接讀入內存,要求從中提取某天出訪問百度次數最多的那個IP。 從這一天的日志數據中把訪問百度的IP取出來,逐個寫入到一個大文件中;注意到IP是32位的,最多有2^32個IP。同樣可…

線程控制原語之pthread_self和pthread_create函數

注意:使用線程庫函數用gcc編譯時,要加參數:-lpthread(libpthread.so),因為線程庫函數屬于第三方c庫函數,不是標準庫函數(/lib、/usr/lib或者/usr/local/lib)。 &#xf…

1005. 繼續(3n+1)猜想 (25)

卡拉茲(Callatz)猜想已經在1001中給出了描述。在這個題目里,情況稍微有些復雜。 當我們驗證卡拉茲猜想的時候,為了避免重復計算,可以記錄下遞推過程中遇到的每一個數。例如對n3進行驗證的時候,我們需要計算3、5、8、4、2、1&#…

C指針深度解析

(1)指針的概念 指針是一種數據類型,而內存地址是這種數據類型具體的值(注意區分兩者的概念)。先說一下什么是內存地址:假設CPU的尋址方式是以字節尋址的,即每一個字節對應一個地址編號&#xf…

1007. 素數對猜想

讓我們定義 dn 為&#xff1a;dn pn1 - pn&#xff0c;其中 pi 是第i個素數。顯然有 d11 且對于n>1有 dn 是偶數。“素數對猜想”認為“存在無窮多對相鄰且差為2的素數”。 現給定任意正整數N (< 105)&#xff0c;請計算不超過N的滿足猜想的素數對的個數。 輸入格式&…

線程共享全局變量(.data和.bbs)

線程默認共享數據段、代碼段等地址空間&#xff0c;常用的是全局變量。而進程不共享全局變量&#xff0c;只能借助mmap。 //代碼示例 #include <string.h> #include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h> …

1008 數組元素循環右移問題 (20)

一個數組A中存有N&#xff08;N&gt0&#xff09;個整數&#xff0c;在不允許使用另外數組的前提下&#xff0c;將每個整數循環向右移M&#xff08;M>0&#xff09;個位置&#xff0c;即將A中的數據由&#xff08;A~0~ A~1~……A~N-1~&#xff09;變換為&#xff08;A~N-…

C++設計模式之策略模式(Strategy)

Strategy策略模式作用&#xff1a;定義了算法家族&#xff0c;分別封裝起來&#xff0c;讓他們之間可以互相替換&#xff0c;此模式讓算法的變化&#xff0c;不會影響到使用算法的客戶。 UML圖&#xff1a; 代碼實現 #include <iostream> using namespace std;class St…

pthread_exit函數

void pthread_exit(void *retval); 參數&#xff1a;retval表示線程退出狀態&#xff0c;通常傳NULL。 作用&#xff1a;將單個線程退出。 注意幾點&#xff1a; return的作用是返回到函數的調用點&#xff0c;如果是main函數中的return&#xff0c;則代表該進程結束&#x…

C++面試常見問題

背景色yellow 1 1. c如何防止一個類被其他類繼承 >- 如果是僅僅為了達到這個目的可以直接把這個類的構造函數設置成私有的&#xff0c;這樣就杜絕了其他類的繼承。也相當于毀掉了這個類&#xff08;無法再創造出自己的對象&#xff09;。 那么怎么樣既要保證這個類的完整性…

pthread_join函數

int pthread_join(pthread_t thread, void **retval); 作用&#xff1a;阻塞等待線程退出&#xff0c;獲取線程退出狀態。其作用對應進程中 waitpid() 函數。 成功&#xff1a;0&#xff1b;失敗&#xff1a;錯誤號 strerror函數 參數&#xff1a;thread&#xff1a;線程I…

【C++ Priemr | 15】派生類向基類轉換的可訪問性

1. 只有當D公有繼承B時&#xff0c;用戶代碼才能使用派生類向基類的轉換&#xff1b;如果D私有繼承B的方式是受保護的或者私有的&#xff0c;則用戶代碼不能使用該轉換。 class A {}&#xff1b; class B : public A {}void function(const A&) {}int main() {B b;functio…

pthread_detach函數

int pthread_detach(pthread_t thread); 成功&#xff1a;0&#xff1b;失敗&#xff1a;錯誤號 作用&#xff1a;從狀態上實現線程分離&#xff0c;注意不是指該線程獨自占用地址空間。 線程分離狀態&#xff1a;指定該狀態&#xff0c;線程主動與主控線程斷開關系。線程…

【C++ Primer | 15】面試問題

在成員函數中調用虛函數 #include <iostream> using namespace std; class CBase { public:void func1(){func2();}virtual void func2() {cout << "CBase::func2()" << endl;} }; class CDerived:public CBase { public:virtual void func2() {…

pthread_cancel、pthread_equal函數

&#xff08;1&#xff09;pthread_cancel函數 int pthread_cancel(pthread_t thread); 成功&#xff1a;0&#xff1b;失敗&#xff1a;錯誤號 作用&#xff1a;殺死(取消)線程&#xff0c;其作用對應進程中 kill() 函數。 注意&#xff1a;線程的取消并不是實時的&…

STL源碼剖析面試問題

當vector的內存用完了&#xff0c;它是如何動態擴展內存的&#xff1f;它是怎么釋放內存的&#xff1f;用clear可以釋放掉內存嗎&#xff1f;是不是線程安全的&#xff1f; vector內存用完了&#xff0c;會以當前size大小重新申請2* size的內存&#xff0c;然后把原來的元素復制…

線程與進程的控制原語對比

線程與進程的控制原語對比 fork pthead_create exit( int ) pthead_exit(void *); wait(int *) pthread_join&#xff08; ,void **&#xff09; 阻塞 ;分離 22 &#xff1b;cancel -1 kill() pthread_cancel(); 取消點(檢查點)&#xff1a;系統調用 getpid() pthrea…

ptmalloc堆內存管理機制(主要討論Linux x86下32位系統)

bin&#xff08;chunk容器&#xff09; ptmalloc將相似大小的 chunk 用雙向鏈表鏈接起來&#xff0c;這樣的一個鏈表被稱為一個 bin。 Ptmalloc 一共維護了 128 個 bin&#xff0c;并使用一個數組來存儲這些 bin&#xff0c;這個數組被成為bin數組。 bin數組結構如下&#xf…

線程屬性的修改

&#xff08;1&#xff09;線程屬性 Linux下線程的屬性是可以根據實際項目需要&#xff0c;進行設置&#xff0c;之前我們討論的線程都是采用線程的默認屬性&#xff0c;默認屬性已經可以解決絕大多數開發時遇到的問題。如我們對程序的性能提出更高的要求那么需要設置線程屬性…