国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

這篇具有很好參考價值的文章主要介紹了『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

「前言」文章是關(guān)于Linux多線程方面的知識,講解會比較細(xì),下面開始!

「歸屬專欄」Linux系統(tǒng)編程

「主頁鏈接」個人主頁

「筆者」楓葉先生(fy)

「楓葉先生有點文青病」「每篇一句」?

我與春風(fēng)皆過客,

你攜秋水?dāng)埿呛印?/span>
——網(wǎng)絡(luò)流行語,詩詞改版

用現(xiàn)在的話來說:我不再喜歡你了

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

目錄

一、??進程地址空間的第三次理解

二、線程概念

2.1 理解線程概念

2.2 線程控制---線程的創(chuàng)建

2.3 Linux進程 VS 線程

2.4?線程的優(yōu)點

2.5 線程的缺點

2.6?線程異常

2.7?線程用途


一、??進程地址空間的第三次理解

在談線程之前,需要再次理解進程地址空間,為后序講解線程做準(zhǔn)備。

  • 進程地址空間在進程概念篇章已經(jīng)說過一部分,這是進程地址空間的第一次理解,點擊穿越
  • 之后進程地址空間在進程信號篇章已經(jīng)說過一部分,這是進程地址空間的第二次理解,點擊穿越

在這里,是關(guān)于進程地址空間的第三次理解,重點: 虛擬地址到物理地址的轉(zhuǎn)換

如何看待地址空間和頁表?

  • 地址空間是進程能看到的資源窗口
  • 而頁表決定進程正真擁有資源的情況

所以,合理的對地址空間 + 頁表進行資源劃分,我們就可以對一個進程所有的資源進行分類,合理對資源進行分類后(堆區(qū),共享區(qū)、棧區(qū)等)就可以通過頁表進行映射到不同的物理內(nèi)存區(qū)域

下面對頁表進行介紹:

頁表中除了要有虛擬地址和與其映射的物理地址以外,實際上還有存儲著其他的信息:

  • 是否命中
  • RWX權(quán)限(讀寫執(zhí)行權(quán)限)
  • U/K權(quán)限,U:user(用戶級權(quán)限),K:kernel(內(nèi)核級權(quán)限)

比如我們之前在進程地址空間的第二次理解時,所說的用戶級頁表和內(nèi)核級頁表,實際就是通過 U/K 權(quán)限進行區(qū)分的,U就是用戶級頁表,K就是內(nèi)核級頁表,頁表的每一行可以稱為一個條目,如圖:

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

以32位平臺為例,在32位平臺下一共有?2^32 個地址,如果頁表單純只是映射物理內(nèi)存,那么這張表就需要建立 2^32 個虛擬地址和物理地址之間的映射關(guān)系,即這張表一共有 2^32 個映射條目

假設(shè)物理地址為4字節(jié),是否命中、RWX權(quán)限、U/K權(quán)限各占一個字節(jié),加起來最少都有 7個字節(jié)了,就按8字節(jié)算

這張頁表一共有 2^32 個映射條目,就意味著存儲這張頁表我們需要用 2^32 * 8 個字節(jié),2^32 是 4GB,最后結(jié)果是 32GB。而在32位平臺下,我們的內(nèi)存是 4GB,壓根就存不下這張頁表。也就是說,頁表映射并不是這樣簡單的映射計算

下面講解虛擬內(nèi)存到物理內(nèi)存的轉(zhuǎn)換規(guī)則(以32位平臺為例)

?每個虛擬地址都是 32個比特位,即 0000 0000 0000 0000 0000 0000 0000 0000,頁表映射的過程為:

  1. 選擇虛擬地址的前10個比特位在頁目錄當(dāng)中進行查找,找到對應(yīng)的頁表(頁目錄用于索引相應(yīng)的頁表),2^10字節(jié) = 1KB,存儲頁目錄所需的內(nèi)存是 1KB,且頁目錄只有一張
  2. 再選擇虛擬地址中間的10個比特位在對應(yīng)的頁表當(dāng)中進行查找,找到物理內(nèi)存中對應(yīng)頁框的起始地址,每張頁表的大小也是?2^10字節(jié) = 1KB,頁表是有多張的
  3. 最后將虛擬地址中剩下的12個比特位作為偏移量從對應(yīng)頁框的起始地址處向后進行偏移,找到物理內(nèi)存中某一個對應(yīng)的字節(jié)數(shù)據(jù),2^12字節(jié) = 4KB,與物理內(nèi)存的頁框?qū)?yīng)

說明:

  • 之前的篇章也談過,物理內(nèi)存是被劃分為以 4KB 為大小的塊,這個塊稱為頁框;磁盤的也是被劃分為以 4KB 為大小的塊,這個塊稱為頁幀
  • 我們編寫好的可執(zhí)行程序,也是被劃分為以 4KB 為單位進行存儲,加載到內(nèi)存也是以 4KB 為單位進行加載

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

  • 4KB 實際上就是 2^12 個字節(jié),一個頁框的大小也是?2^12 個字節(jié)
  • 訪問內(nèi)存的基本大小是1字節(jié),因此一個頁框中就有 2^12 個地址,
  • 我們將后 12個比特位作為偏移量,從頁框的起始地址處開始向后進行偏移(頁表中存著每個頁框的起始地址),從而找到物理內(nèi)存對應(yīng)的物理地址
  • 頁目錄可以存儲 2^10 個頁表的地址,也就是說最多有 2^10 個頁表
  • 頁目錄為一級頁表,頁目錄存儲的每張頁表的地址,這些頁表稱為二級頁表

?實際上,我們需要用到頁表時,OS才會為我們創(chuàng)建相應(yīng)的頁表,不用的話就不創(chuàng)建,這就大大節(jié)省了內(nèi)存空間,即便全部加載全部的頁表,也不會占用太多的內(nèi)存空間

虛擬內(nèi)存到物理內(nèi)存映射過程,都是由 MMU(Memory Management Unit)這個硬件完成的,該硬件是集成在CPU內(nèi)的。頁表是一種軟件,MMU是一種硬件,所以計算機進行虛擬地址到物理地址的轉(zhuǎn)化采用的是軟硬件結(jié)合的方式

這就是虛擬內(nèi)存到物理內(nèi)存的映射規(guī)則

修改常量字符串為什么會觸發(fā)段錯誤??

當(dāng)我們要修改一個字符串常量時,虛擬地址必須經(jīng)過頁表映射找到對應(yīng)的物理內(nèi)存,而在查表過程中發(fā)現(xiàn)其權(quán)限是只讀的,此時你要對其進行修改就會在MMU內(nèi)部觸發(fā)硬件錯誤,操作系統(tǒng)在識別到是哪一個進程導(dǎo)致的之后,就會給該進程發(fā)送信號對其進行終止

二、線程概念

2.1 理解線程概念

線程概念如下:?

  • 在一個程序里的一個執(zhí)行路線(執(zhí)行流)就叫做線程(thread)或者是線程是進程內(nèi)的一個執(zhí)行流。更準(zhǔn)確的定義是:線程是 “一個進程內(nèi)部的控制序列”
  • 一切進程至少都有一個執(zhí)行線程
  • 線程在進程內(nèi)部運行,本質(zhì)是在進程地址空間內(nèi)運行
  • 在Linux系統(tǒng)中,在CPU眼中,看到的PCB都要比傳統(tǒng)的進程更加輕量化
  • 透過進程虛擬地址空間,可以看到進程的大部分資源,將進程資源合理分配給每個執(zhí)行流,就形成了線程執(zhí)行流

再談進程,以進程為切入點理解這些概念

  • 之前的篇章我們所講的:進程 = 內(nèi)核數(shù)據(jù)結(jié)構(gòu) + 進程對應(yīng)的代碼和數(shù)據(jù)
  • 一個進程的創(chuàng)建實際上伴隨著PCB(進程控制塊,Linux是 task_struct)、地址空間(mm_struct)、頁表的創(chuàng)建
  • 每個進程都有自己獨立的 task_struct 和 mm_struct、頁表
  • 虛擬內(nèi)存決定了進程所能看到的 “資源”

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

下面只創(chuàng)建 task_struct,并要求創(chuàng)建出來的 task_struct 和原來的 task_struct 共享進程地址空間和頁表,創(chuàng)建的結(jié)果如下:

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

事實上,我們所創(chuàng)建的其實就是三個線程,只不過這三個線程共用一張 mm_struct 和 一張頁表,這三個 task_struct 就是三個不同的執(zhí)行流

  • 線程被創(chuàng)建之后,我們可以虛擬地址空間 + 頁表的方式對原先的進程進行資源劃分,劃分相應(yīng)的資源給線程,所以這里的其中的一個 “進程” 的執(zhí)行力度,一定要比之前所談的一個進程的執(zhí)行力度要細(xì)
  • 之前的篇章所談的一個進程是獨占一個虛擬地址空間和頁表和與之對應(yīng)的物理內(nèi)存,現(xiàn)在在這里,這個進程的對應(yīng)的虛擬地址空間、頁表和與之對應(yīng)的物理內(nèi)存資源被幾個 task_struct 所劃分(被線程劃分),所以在這里的一個 “進程” 的執(zhí)行力度要比之前獨占全部資源的進程執(zhí)行力度要細(xì)

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

思考:

拋開上面的,假設(shè) OS 要真的設(shè)計 “線程” 這個概念,那 OS 就必須對 “線程” 進行管理,如何管理?先描述,再組織。先描述就是為線程設(shè)計專門的數(shù)據(jù)結(jié)構(gòu) TCB(Task Control Block,線程控制塊),用來表示線程對象

專門設(shè)計線程這個概念,常見的系統(tǒng)有:Windows,專門設(shè)計線程概念的結(jié)果是:除了要維護進程與進程之間的關(guān)系,還要維護進程與線程之間的關(guān)系,還要維護線程與線程之間的關(guān)系,并且它們代碼之間的耦合度極高,帶來的結(jié)果就是它們之間的關(guān)系極其復(fù)雜,維護成本極高

  • 一個線程被創(chuàng)建的目的是:被執(zhí)行被調(diào)度,以此衍生:線程一定要有(id,狀態(tài),優(yōu)先級,上下文,棧等等)相關(guān)的概念
  • 可以看出,單單就 “線程” 被調(diào)度而言,線程與進程就存在許多的重疊(id,狀態(tài),優(yōu)先級,上下文,棧等等)
  • 所以,Linux的工程師,不想給 “線程” 設(shè)計相應(yīng)的數(shù)據(jù)結(jié)構(gòu),而是直接復(fù)用 PCB,用 PCB 來表示 Linux 內(nèi)部的 “線程”

所以,在Linux中創(chuàng)建線程,只需創(chuàng)建相應(yīng)的 PCB 即可,所以在Linux中,線程是在進程的內(nèi)部 “運行”,線程在該進程的地址空間內(nèi) “運行”,擁有該進程的一部分資源

那現(xiàn)在應(yīng)如何看待進程?

之前談的進程是:進程 = 內(nèi)核數(shù)據(jù)結(jié)構(gòu) + 進程對應(yīng)的代碼和數(shù)據(jù),現(xiàn)在要以全新的視角看待進程:內(nèi)核視角

以內(nèi)核的視角看待進程:進程是承擔(dān)分配系統(tǒng)資源的基本實體

  • Linux進程 = 大量的task_struct + 一個虛擬地址空間 + 頁表 + 一部分的物理內(nèi)存
  • 我們之前篇章所談的進程 = 一個task_struct + 一個虛擬地址空間 + 頁表 + 一部分的物理內(nèi)存
  • 一個進程的創(chuàng)建:必定要花費相應(yīng)的資源

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

那如何看待我們之前篇章所學(xué)習(xí)的進程概念,與今天的所講的進程沖突嗎??

答案是不沖突,是互相補充的

  • 之前所談的進程也是承擔(dān)分配系統(tǒng)資源的基本實體,只不過,該進程的內(nèi)部只有一個執(zhí)行流(一個 task_struct)
  • 而現(xiàn)在所談的進程也是承擔(dān)分配系統(tǒng)資源的基本實體,只不過,該進程內(nèi)部有多個執(zhí)行流(多個 task_struct)
  • 進程的內(nèi)部允許只有自己一個執(zhí)行流,也可以允許有多個執(zhí)行流,我們之前所學(xué)的進程,內(nèi)部只有一個執(zhí)行流
  • 以前所講的 “進程” 只是一個子集,今天所講的進程才是全貌

??在Linux中,什么是線程?線程是CPU調(diào)度的基本單位

站在CPU的角度,歷史調(diào)度的進程 VS 今天調(diào)度的進程?

  • 歷史調(diào)度的進程,也就是我們之前篇章所談的被調(diào)度的進程,被CPU調(diào)度的是:一個進程
  • 今天調(diào)度的進程,被CPU調(diào)度的是:進程內(nèi)部的一個分支

實際上,CPU并不關(guān)心你是進程(只有一個執(zhí)行流)還是線程(進程內(nèi)部有多個執(zhí)行流中的一個執(zhí)行流),只要你給了 task_struct,CPU都是無腦執(zhí)行,所以站在CPU的角度:看待它們都是一個輕量級進程??!CPU看來沒有所謂進程、線程,CPU眼里只有輕量級進程

概念總結(jié)?

(0)以例子比喻線程:一個家庭(進程),家庭成員(線程)?,線程是進程的一個子集

(1)在Linux內(nèi)核中有沒有真正意義上的線程?

答案是沒有,Linux是用進程PCB 來模擬線程的,是一種完全屬于自己的一套方案?

(2)站在CPU的視角,每一個 PCB,都可以稱之為輕量級線程

(3)Linux線程是CPU調(diào)度的基本單位,而進程是承擔(dān)分配系統(tǒng)資源的基本單位

(3)進程用來整體申請資源,線程向進程申手要資源

Linux是用進程PCB 來模擬線程有什么好處?簡單,維護成本大大降低,可靠性高效

  • 與之相反,真正意義上設(shè)置了線程概念的Windows系統(tǒng),它的維護成本極高,進程與線程之間的關(guān)系極其復(fù)雜,它伴隨的就是問題多,可靠性降低。
  • 帶來的結(jié)果就是:Linux服務(wù)器開幾年都不用關(guān)閉,依舊流暢,而Windows開機一段時間后,很可能就會卡死,很大原因是這個

Linux內(nèi)核中有沒有真正意義上的線程的缺點

OS 只認(rèn)線程,用戶(程序員)也只認(rèn)線程,沒有輕量級進程的概念,所以Linux無法直接提供線程操作的系統(tǒng)調(diào)用接口,而只能給我們提供輕量級進程的接口。

  • 但是用戶只認(rèn)線程,所以Linux給我們提供了一個線程庫,這個庫是用戶級線程庫,它底層是調(diào)用輕量級進程的接口的,這個線程庫對這些接口進行封裝,上層用戶使用這個庫看起來像是Linux擁有線程一樣
  • 這個線程庫的名字叫 pthread,是用戶級線程庫
  • 任何的Linux系統(tǒng),必須要提供這個庫,這個線程庫是默認(rèn)攜帶的,這個線程庫也稱為原生線程庫
  • 所以用戶需要關(guān)心這個線程庫所提供的接口,不用關(guān)心底層的接口

下面進行對線程的測試,先見一下線程是什么樣子的

2.2 線程控制---線程的創(chuàng)建

創(chuàng)建的線程需要用到的函數(shù)是 pthread_create,man 3?pthread_create 進行查看:

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

解釋:?

函數(shù):pthread_create

作用: pthread_create - create a new thread(創(chuàng)建一個新線程)


頭文件:#include <pthread.h>

函數(shù)原型
 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

參數(shù)
    第一個參數(shù)thread,代表線程ID,是一個輸出型參數(shù),pthread_t是一個無符號整數(shù)
    第二次參數(shù)attr,用于設(shè)置創(chuàng)建線程的屬性,傳入空表示使用默認(rèn)屬性
    第三個參數(shù)start_routine,是一個函數(shù)的地址,該參數(shù)表示新線程啟動后要跳轉(zhuǎn)執(zhí)行的代碼
    第四個參數(shù)arg,是start_routine函數(shù)的參數(shù),用于傳入

返回值
成功返回0,失敗返回錯誤碼

?第三個參數(shù)說明:void *(*start_routine) (void *)

  • 該參數(shù)是一個函數(shù)指針,用于設(shè)置一個回調(diào)函數(shù)start_routine
  • 該函數(shù)的返回值是 void*,
  • 函數(shù)參數(shù)是 void*,該參數(shù)由第四個參數(shù) arg 傳入

測試代碼:

展示主線程和新線程同時運行,主線程創(chuàng)建新線程,主線程創(chuàng)建完新線程后繼續(xù)執(zhí)行,新線程也繼續(xù)執(zhí)行

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

//新線程
void* start_routine(void* args)
{
    while(1)
    {
        cout << "我是新線程,我正在運行..." << endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, start_routine, (void*)"thread one");//該參數(shù)的內(nèi)容 thread one 傳遞給args

    //主線程
    while(1)
    {
        cout << "我是主線程,我正在運行..." << endl;
        sleep(1);
    }

    return 0;
}

?編譯結(jié)果如下,找不到該函數(shù)

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

原因是我們編譯是沒有鏈接線程庫 pthread,從這里也說明了Linux沒有線程相關(guān)的系統(tǒng)調(diào)用,Linux沒有線程概念,我們需要鏈接該線程庫才可以使用庫中所提供的函數(shù),如何鏈接?在編譯時需要加上 -lpthread

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

動靜態(tài)庫如何使用,在基礎(chǔ)IO篇章已經(jīng)介紹過了

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

?添加如下:

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

再次編譯就可以了

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

ldd查看可執(zhí)行程序鏈接的庫,確實鏈接了 pthread.so庫,.so是動態(tài)庫

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

進行運行,發(fā)現(xiàn)主線程和新線程在同時運行

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

ps 查看,發(fā)現(xiàn)只有一個進程的PID

ps axj | head -1 && ps axj | grep mytest | grep -v grep

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

對進程發(fā)送9號信號,發(fā)現(xiàn)主線程和新線程都被終止了。信號是直接跟進程直接關(guān)聯(lián),與線程沒有直接關(guān)系。進程只要收到信號,假設(shè)信號是終止信號,進程里面的線程都會與進程一起被終止

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

上面我們看到的只有一個進程,要查看輕量級進程如何進行查看??

查看輕量級線程相關(guān)信息的命令:ps -aL?

運行程序,進行查看。其中,LWP(Light Weight Process)就是所謂的輕量級進程

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

  • 主線程的PID與輕量級進程ID是一樣的
  • 每個輕量級進程的PID都是一樣的
  • ?每個輕量級進程LWP的ID都是不一樣的

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

所以,CPU在調(diào)度的時候,是以 LWP 的ID作為唯一的標(biāo)識符用來標(biāo)識一個執(zhí)行流的,并不是使用PID?

現(xiàn)在把線程的代碼注釋掉,只留下主線程代碼,主線程代碼就是我們以前寫的代碼,內(nèi)部只有一個執(zhí)行流,單進程

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

編譯運行,ps -aL 進行查看,PID = LWP,這就是我們以前寫的單執(zhí)行流的代碼,只有一個執(zhí)行流的時候,PID和LWP是等價的

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

注意:信號是整體給進程發(fā)送的,不能單獨發(fā)給一個 LWP?

下面進行證明,pthred_create 的第四個參數(shù)是給第三個參數(shù)發(fā)送的

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

//新線程
void* start_routine(void* args)
{
    //接收參數(shù)
    const char* name = (const char*)args;
    while(1)
    {
        cout << "我是新線程,我正在運行...  " << name << endl;
        sleep(1);
    }
}

int main()
{
     pthread_t tid;
     int n = pthread_create(&tid, nullptr, start_routine, (void*)"thread one");//該參數(shù)的內(nèi)容 thread one 傳遞給args

    //主線程
    while(1)
    {
        cout << "我是主線程,我正在運行..." << endl;
        sleep(1);
    }

    return 0;
}

?編譯運行,新線程確實接收到了該參數(shù),所以我們想給新線程傳參的話,可以寫到第四個參數(shù)里面

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

pthread_create 函數(shù)的第一個參數(shù)是一個輸出型參數(shù),返回的是線程ID,pthread_t 是一個無符號整數(shù)。下面進行驗證,該參數(shù)輸出的是什么

typedef unsigned long int pthread_t;

修改代碼

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

//新線程
void* start_routine(void* args)
{
    //接收參數(shù)
    const char* name = (const char*)args;
    while(1)
    {
        cout << "我是新線程,我正在運行...  " << name << endl;
        sleep(1);
    }
}

int main()
{
    //typedef unsigned long int pthread_t;
     pthread_t tid;//用于接受返回的線程ID
     int n = pthread_create(&tid, nullptr, start_routine, (void*)"thread one");//該參數(shù)的內(nèi)容 thread one 傳遞給args

    //主線程
    while(1)
    {
        char tidbuffer[64];
        snprintf(tidbuffer, sizeof(tidbuffer), "0x%x", tid);
        cout << "我是主線程,我正在運行... " << "返回新線程的tid是:" << tid << "  對它取地址:" << tidbuffer << endl;
        sleep(1);
    }

    return 0;
}

編譯運行,十進制打印的結(jié)果無意義,這個 tid 是其實一個地址,與我們在系統(tǒng)里面查的 LWP 的 tid 不一樣,所以這個 tid 與 LWP的 tid 沒有關(guān)系,至于這里的 tid 到底是什么,下面進程控制再談

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

這就是線程控制之線程創(chuàng)建

2.3 Linux進程 VS 線程

進程是承擔(dān)分配系統(tǒng)資源的基本實體,線程是調(diào)度的基本單位。線程共享進程數(shù)據(jù),但也擁有自己的一部分?jǐn)?shù)據(jù)

?那什么資源是線程私有的呢?(第2、3點最重要)

  1. 線程ID,PCB屬性私有
  2. 一組寄存器(上下文數(shù)據(jù))
  3. 獨立棧結(jié)構(gòu)(每個線程都有臨時的數(shù)據(jù),需要壓棧出棧)(注:這個后序詳細(xì)解釋)
  4. errno(C語言提供的全局變量,每個線程都有自己獨立errno)
  5. 信號屏蔽字
  6. 調(diào)度優(yōu)先級

進程的多個線程共享同一地址空間,因此Text Segment(代碼段)、Data Segment(數(shù)據(jù)段)都是共享的.

  • 如果定義一個函數(shù),在各線程中都可以調(diào)用;
  • 如果定義一個全局變量,在各線程中都可以訪問到

除此之外,各線程還共享以下進程資源和環(huán)境:

  • 文件描述符表(進程打開一個文件后,其他線程也能夠看到)
  • 每種信號的處理方式(SIG_ IGN、SIG_ DFL或者自定義的信號處理函數(shù))
  • 當(dāng)前工作目錄
  • 用戶id和組id

進程和線程的關(guān)系如下圖:

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

說明:

  • 沒有學(xué)線程之前,我們所寫的代碼都是單線程進程

與進程之間的切換相比,線程之間的切換需要操作系統(tǒng)做的工作要少很多,少哪些?

  • ?進程切換:需要切換頁表 && 切換虛擬地址空間 && 切換PCB && 切換上下文數(shù)據(jù)
  • 線程切換:需要切換PCB && 切換上下文數(shù)據(jù)
  • 線程切換cache 不用更新太多,但是進程切換需要全部更新cache(主要體現(xiàn)在這點)
  • cache是集成在CPU里面的,是一個硬件,是CPU很重要的組成部分,它具有數(shù)據(jù)保存的功能,它的緩存速度比寄存器慢,比內(nèi)存快
  • 寄存器讀取數(shù)據(jù)是直接在 cache 里面讀取的,不是直接從內(nèi)存讀取
  • 一個進程只有運行一段時間后,cache 里面才會緩存大量的熱點數(shù)據(jù)
  • 熱點數(shù)據(jù)就是:進程經(jīng)常使用、經(jīng)常訪問、經(jīng)常命中的數(shù)據(jù)(需要進程跑一段時間才會存在大量的熱點數(shù)據(jù)),熱點數(shù)據(jù)是被整個進程共享的
  • 線程切換的時候,cache內(nèi)緩存的數(shù)據(jù)不用切換,線程需要用到新的數(shù)據(jù)直接緩存進cache即可
  • 而進程切換,cache內(nèi)緩存的數(shù)據(jù)需要全部切換,新切換的進程需要重新緩存數(shù)據(jù),這樣效率就比線程慢得多了

2.4?線程的優(yōu)點

  • 創(chuàng)建一個新線程的代價要比創(chuàng)建一個新進程小得多
  • 與進程之間的切換相比,線程之間的切換需要操作系統(tǒng)做的工作要少很多
  • 線程占用的資源要比進程少很多
  • 能充分利用多處理器的可并行數(shù)量
  • 在等待慢速I/O操作結(jié)束的同時,程序可執(zhí)行其他的計算任務(wù)
  • 計算密集型應(yīng)用,為了能在多處理器系統(tǒng)上運行,將計算分解到多個線程中實現(xiàn)
  • I/O密集型應(yīng)用,為了提高性能,將I/O操作重疊。線程可以同時等待不同的I/O操作。

說明:

  • 計算密集型:執(zhí)行流的大部分任務(wù),主要以計算為主。比如加密解密、大數(shù)據(jù)查找、算法等
  • IO密集型:執(zhí)行流的大部分任務(wù),主要以IO為主。比如刷磁盤、訪問數(shù)據(jù)庫、訪問網(wǎng)絡(luò)等
  • 現(xiàn)代多核CPU一般指:CPU內(nèi)部集成了多個運算器
  • CPU的核數(shù)決定了線程的個數(shù),CPU的個數(shù)決定了進程的個數(shù)

2.5 線程的缺點

  • 性能損失:一個很少被外部事件阻塞的計算密集型線程往往無法與共它線程共享同一個處理器。如果計算密集型線程的數(shù)量比可用的處理器多,那么可能會有較大的性能損失,這里的性能損失指的是增加了額外的同步和調(diào)度開銷,而可用的資源不變。
  • 健壯性降低:編寫多線程需要更全面更深入的考慮,在一個多線程程序里,因時間分配上的細(xì)微偏差或者因共享了不該共享的變量而造成不良影響的可能性是很大的,換句話說線程之間是缺乏保護的。
  • 缺乏訪問控制:進程是訪問控制的基本粒度,在一個線程中調(diào)用某些OS函數(shù)會對整個進程造成影響。
  • 編程難度提高:編寫與調(diào)試一個多線程程序比單線程程序困難得多

線程的健壯性降低,下面進行測試

測試代碼,主線程正常運行,新線程發(fā)生空指針異常

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

//新線程
void* start_routine(void* args)
{
    //static_cast 安全的進行強制類型轉(zhuǎn)換,C++11
    string name = static_cast<const char*>(args);
    while(1)
    {
        cout << "new thread create success, name: " << name << endl;
        sleep(1);
        //一個線程出問題,會影響其他線程么?
        int *p = nullptr;
        *p = 0;//空指針異常
    }
}

int main()
{
     pthread_t tid;//用于接受返回的線程ID
     int n = pthread_create(&tid, nullptr, start_routine, (void*)"thread new");//該參數(shù)的內(nèi)容 thread one 傳遞給args

    //主線程
    while(1)
    {
        cout << "new thread create success, name: main thread" << endl;
        sleep(1);
    }

    return 0;
}

編譯運行,線程全部終止了,ps -aL 查看也沒有了,所以線程出異常,會直接影響其他線程的運行,說明線程的健壯性或魯棒性較差

『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換

注意:信號是叫做進程信號,是整體發(fā)給進程的

2.6?線程異常

  • 單個線程如果出現(xiàn)除零,野指針問題導(dǎo)致線程崩潰,進程也會隨著崩潰
  • 線程是進程的執(zhí)行分支,線程出異常,就類似進程出異常,進而觸發(fā)信號機制,終止進程,進程終止,該進程內(nèi)的所有線程也就隨即退出

2.7?線程用途

  • 合理的使用多線程,能提高CPU密集型程序的執(zhí)行效率
  • 合理的使用多線程,能提高IO密集型程序的用戶體驗(如生活中我們一邊寫代碼一邊下載開發(fā)工具,就是多線程運行的一種表現(xiàn))

線程概念完結(jié),下一篇進入線程控制

--------------------- END ----------------------文章來源地址http://www.zghlxwxcb.cn/news/detail-439523.html

「 作者 」 楓葉先生
「 更新 」 2023.4.27
「 聲明 」 余之才疏學(xué)淺,故所撰文疏漏難免,
          或有謬誤或不準(zhǔn)確之處,敬請讀者批評指正。

到了這里,關(guān)于『Linux』第九講:Linux多線程詳解(一)_ 線程概念 | 線程控制之線程創(chuàng)建 | 虛擬地址到物理地址的轉(zhuǎn)換的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 第九講 單片機驅(qū)動彩色液晶屏 控制RA8889軟件:顯存操作

    第九講 單片機驅(qū)動彩色液晶屏 控制RA8889軟件:顯存操作

    目錄 第一講 單片機最小系統(tǒng)STM32F103C6T6通過RA8889驅(qū)動彩色液晶屏播放視頻 第二講 單片機最小系統(tǒng)STM32F103C6T6控制RA8889驅(qū)動彩色液晶屏硬件框架 第三講 單片機驅(qū)動彩色液晶屏 控制RA8889軟件:如何初始化 第四講 單片機驅(qū)動彩色液晶屏 控制RA8889軟件:繪圖 第五講 單片機驅(qū)動彩色液

    2024年01月16日
    瀏覽(19)
  • 【Linux】Linux線程概念和線程控制

    【Linux】Linux線程概念和線程控制

    線程是進程內(nèi)的一個執(zhí)行流。 我們知道,一個進程會有對應(yīng)的PCB,虛擬地址空間,頁表以及映射的物理內(nèi)存。所以我們把這一個整體看做一個進程,即進程=內(nèi)核數(shù)據(jù)結(jié)構(gòu)+進程對應(yīng)的代碼和數(shù)據(jù)。我們可以這樣看待虛存:虛擬內(nèi)存決定了進程能夠看到的\\\"資源\\\"。因為每一個進

    2024年02月04日
    瀏覽(31)
  • 【Linux】多線程 --- 線程概念 控制 封裝

    【Linux】多線程 --- 線程概念 控制 封裝

    從前種種,譬如昨日死。從后種種,往如今日生。 1.1 進程資源如何進行分配呢?(地址空間+頁表) 1. 首先我們來看一個現(xiàn)象,當(dāng)只有第一行代碼時,編譯是能通過的,但會報warning,當(dāng)加了第二行代碼時,編譯無法通過,報error。 第一行代碼能編過的原因是權(quán)限縮小,雖然

    2024年02月03日
    瀏覽(29)
  • 【Linux】多線程1——線程概念與線程控制

    【Linux】多線程1——線程概念與線程控制

    ?? 個人主頁 :超人不會飛) ?? 本文收錄專欄 :《Linux》 ?? 如果本文對您有幫助,不妨 點贊、收藏、關(guān)注 支持博主,我們一起進步,共同成長! ??理解線程需要和進程的概念緊密聯(lián)系。 線程是一個執(zhí)行分支,執(zhí)行粒度比進程更細(xì),調(diào)度成本更低; 進程是分配系統(tǒng)資源的

    2024年02月12日
    瀏覽(47)
  • Linux復(fù)習(xí) / 線程相關(guān)----線程概念與控制 Q&A梳理

    Linux復(fù)習(xí) / 線程相關(guān)----線程概念與控制 Q&A梳理

    本篇博客梳理關(guān)于線程相關(guān)的QA,包括了線程概念與線程的控制。若讀者也在復(fù)習(xí)這塊知識,或者正在學(xué)習(xí)這塊知識,可以通過這些QA檢測自己的知識掌握情況。此外,思維導(dǎo)圖已經(jīng)更新至我的gitee,QA之外的體系梳理還請移步思維導(dǎo)圖。 線程概念 Q:線程和進程的區(qū)別?(為

    2023年04月14日
    瀏覽(46)
  • 【探索Linux】—— 強大的命令行工具 P.19(多線程 | 線程的概念 | 線程控制 | 分離線程)

    【探索Linux】—— 強大的命令行工具 P.19(多線程 | 線程的概念 | 線程控制 | 分離線程)

    在當(dāng)今信息技術(shù)日新月異的時代,多線程編程已經(jīng)成為了日常開發(fā)中不可或缺的一部分。Linux作為一種廣泛應(yīng)用的操作系統(tǒng),其對多線程編程的支持也相當(dāng)完善。本文將會介紹關(guān)于Linux多線程相關(guān)的知識,其中包括了線程的概念、線程控制、線程分離等方面的內(nèi)容。如果你希望

    2024年02月05日
    瀏覽(40)
  • 【Linux學(xué)習(xí)】多線程——頁表詳解 | 線程概念 | 線程理解

    【Linux學(xué)習(xí)】多線程——頁表詳解 | 線程概念 | 線程理解

    ??作者:一只大喵咪1201 ??專欄:《Linux學(xué)習(xí)》 ??格言: 你只管努力,剩下的交給時間! 我們在之前一直都提到頁表,知道它的作用是將虛擬地址映射到物理地址,但是它具體怎么映射的,它的結(jié)構(gòu)是什么樣的,并沒有提及過。 上訴代碼,會在運行時報錯,原因是str指向

    2024年02月05日
    瀏覽(25)
  • Linux系統(tǒng)編程5(線程概念詳解)

    Linux系統(tǒng)編程5(線程概念詳解)

    線程同進程一樣都是OS中非常重要的部分,線程的應(yīng)用場景非常的廣泛,試想我們使用的視頻軟件,在網(wǎng)絡(luò)不是很好的情況下,通常會采取下載的方式,現(xiàn)在你很想立即觀看,又想下載,于是你點擊了下載并且在線觀看。學(xué)過進程的你會不會想,視頻軟件運行后在OS內(nèi)形成一個

    2024年02月10日
    瀏覽(19)
  • MySQL第九講·索引怎么提高查詢的速度?

    MySQL第九講·索引怎么提高查詢的速度?

    你好,我是安然無虞。 在超市信息系統(tǒng)剛剛開始運營的時候,因為數(shù)據(jù)量很少,每一次的查詢都能很快拿到結(jié)果。但是,系統(tǒng)運轉(zhuǎn)時間長了以后,數(shù)據(jù)量不斷地累積,變得越來越龐大,很多查詢的速度就變得特別慢。這個時候,我們就采用了 MySQL 提供的高效訪問數(shù)據(jù)的方法

    2024年02月04日
    瀏覽(19)
  • Git第九講 Git stash暫存消息

    git stash 是一個在 Git 中用于臨時保存未提交的更改的命令。它可以幫助你在切換分支或處理緊急任務(wù)時,將當(dāng)前工作目錄中的修改保存起來,以便稍后恢復(fù)。 git stash 的使用方法如下: 當(dāng)你想要暫存當(dāng)前的修改時,運行以下命令: 可選地,你可以提供一個簡短的描述性消息,

    2024年02月08日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包