W...Y的主頁 😊
代碼倉庫分享💕?
?Linux線程概念
什么是線程
在一個程序里的一個執行路線就叫做線程(thread)。更準確的定義是:線程是“一個進程內部的控制序列”一切進程至少都有一個執行線程線程在進程內部運行,本質是在進程地址空間內運行
在Linux系統中,在CPU眼中,看到的PCB都要比傳統的進程更加輕量化透過進程虛擬地址空間,可以看到進程的大部分資源,將進程資源合理分配給每個執行流,就形成了線程執行流。
在Linux系統下,線程與進程的最終目的都是一樣的,都是想要讓程序并發執行,所以我們可以將代碼進行分塊執行,所以就要創建自己的tcb結構體來維護自己的上下文、優先級等等。但是Linux設計者認為pcb與tcb具有極度的相似性沒必要設計單獨設計數據結構和算法,直接復用既可,所以Linux中的線程也被稱為“輕量化進程”!!!
所以進程是一個或多個執行流加頁表加地址空間加代碼和數據。進程在內核角度為承擔分配系統資源的實體!!!
那多個執行流是怎么劃分代碼呢?
我們經常說申請內存和釋放內存,所以我們應該對內存做管理,其實物理內存也會被劃分為4kb(大部分劃分規則)的數據塊,而磁盤中的數據塊也是4kb的,他們被我們稱為頁框或頁幀。當我們內存與數據塊的交互時就是以數據塊為單位的交互。操作系統要管理內存,肯定要先描述再組織,所以內存有屬于自己的結構體struct page,其結構體的內容也應該是記錄一個數據塊的狀態,使用者是誰,是否被使用,是否可以交換等等。假如我們物理內存為4GB,其就有1048576個4kb數據塊,我們就可以創建一個是結構體數組進行存儲結構體內容。
其源碼一部分:
所以我們的頁表也不可能是所謂的kv關系,因為kv分別代表一個指針,再加上其對應權限可能有10個字節,但是如果是一一對應的關系其頁表可能會有40GB大小,所以頁表是怎么映射的呢?虛擬地址是32位二進制,劃分為三個部分10位10位12位,其第一個10位被稱為頁目錄,需要創建2^10個格子,第二個10位被稱為頁表,頁目錄保存的是二級頁表的地址,所以頁表應該有1024個,每個頁表中有1024個頁表項,每一個頁表項中存放的是4kb內存塊的起始地址(也就是頁框的物理地址)。而我們的后12位的大小為4kb,所以我們又知道其對應的起始地址,所以我們對應的起始物理地址+后12位偏移量就是最終所訪問的數據。所以我們的后12位被稱為頁內偏移。
我們也可以再頁表中加上標志位,讓CPU與頁表中的標志位進行比對,如果相同就可以訪問物理內存的內容。?
所以給不同的線程分配不同的區域,本質就是讓不同的線程各自看到全部頁表的子集。
那是怎么才能看到不同的頁表呢?這里我們得轉到應用層面來看,所以讓我們先了解一些系統調用函數:
第一個參數線程id,第二個參數線程屬性,第三個參數是返回值void*參數也是void*的函數指針,第四個參數是我們給第三個函數指針所指向的函數所傳遞的參數。
一般情況下我們的第二個參數可以寫nullptr。
注意:在我們使用pthread庫中函數時,在編譯時必須鏈接上pthread庫!!!
這樣我們就可以簡短寫一個代碼:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>void *newthreadrun(void *args)
{while (true){std::cout << "I am new thread, pid: " << getpid() << std::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, newthreadrun, nullptr);while (true){std::cout << "I am main thread, pid: " << getpid() << std::endl;sleep(1);}
}
這時我們的代碼就有兩個執行流,我們從main函數開始運行,當走到pthread_create函數時新線程創建成功會進入newthreadrun函數中去,主線程繼續往下執行即可。
?在這里我們只能看到一個進程,我們可以使用ps -aL查看輕量級進程就可以看到兩個進程了。
主線程的PID==LWP,但是其線程所有的PID都是相同的。所以我們在回歸到上述問題上每一句代碼都有其對應的虛擬地址,主線程擁有的是主函數的虛擬地址其余線程得到的是其他函數的虛擬地址,對應其頁表就可以訪問到不同的數據。?
所謂的Linux就根本沒有線程這個說法,只有輕量化進程,所以對應的Linux內核就沒有對線程的系統調用只有對輕量化進程的系統調用。而用戶只知道線程不知道輕量化進程,所以設計者在用戶和內核之間設計一個軟件層pthread庫——原生線程庫。這個庫的作用就是將Linux的輕量級進程系統調用進行封裝,轉化成線程相關接口提供給用戶。
輕量級進程也有直接的系統調用,但是太復雜一般沒有人想用!!!?
以上就是本次全部內容,感謝大家觀看!?