線程概念,控制

一、線程概念

線程概念進程內部的一個執行流,輕量化

觀點:進程是系統分配資源的基本單位,線程是CPU調度的基本單位

在理解線程之前,我們在談一下虛擬地址空間

我們都知道進程是通過頁表將虛擬地址轉化為物理地址的,對于PCB,file我們已經了解了,所以,我們主要談頁表

虛擬地址和物理地址之間映射時,是通過字節映射的嗎如果進程大小是4GB,那么一共就會有 4 * 1024 * 1024 * 1024個字節,如果是按照字節映射,那么頁表的大小(虛擬地址和物理地址各占4字節,其它不考慮)就是 8 * 4 * 1024 * 1024 * 1024個字節,也就是32GB,一個進程的頁表就這么大,這是不可能的。所以,肯定不是通過字節映射的

我們都知道磁盤和物理內存之間是通過4KB進行IO的,在邏輯上我們認為物理內存也是以4KB劃分的物理內存上4KB劃分的空間,我們把它叫做頁框或者頁幀如何理解頁框呢

在物理內存中會有許多這樣的4KB空間,有些空間被使用,有些未被使用…,那么OS要不要對這些空間進行管理呢?答案是要的,先描述在組織

在OS有一個 struct page就是用來描述物理內存4KB空間的,一個4KB空間對應一個 struct page,那么對空間已經描述了,那么該怎么組織呢?只需要用一個 struct page pages[]數組來管理就可以了,對物理內存的管理就轉變為了對數組的增刪查改

既然如此,在OS內部還需要保存物理地址這樣的概念嗎

不需要了,這個數組中每個內存塊的大小是固定的(4KB),那么物理塊的起始地址 = 數組下標 * 4KB,申請一個物理內存塊,本質只要申請到 struct page,知道 struct page的下標,那么物理內存塊的所有地址就都知道了

那么OS如何得知所有物理內存塊的地址呢OS只需要得到 page數組的起始地址即可

結論文件,進程和物理內存之間的關系就轉化為了 file,task_struct 和 page之間的關系了

在32位系統下,OS采用的是二級頁表,從虛擬地址轉換到物理地址,默認是沒有直接轉化到字節的。虛擬地址一共32個 bit,從左往右依次劃分10個 bit,10個 bit,12個 bit,根據CR3寄存器里存儲的頁表起始地址,使用前10個 bit用來索引一級頁表,一級頁表中存在1024個頁表項(存儲的是下一級頁表的起始地址),中間的10個 bit用來索引二級頁表,二級頁表存儲的是物理頁的起始地址,這樣就可以找到物理頁框的起始地址了,最后12個 bit用來做頁內偏移

查頁表只需要幫我們找到要訪問的是哪一個頁框就可以了

真正的物理地址 = 頁框起始地址 + 頁內偏移

頁表的大小 = 4 * 1024,就是4KB的大小,每一個頁表都是4KB大小,那么二級頁表一共(1024 + 1)* 4KB的大小,對比于32GB,那可真是小太多了

細節1CR3寄存器保存的是當前進程頁表的基地址,物理地址

細節2虛擬地址高20位相同,一定是連續存放在一個頁框的,因為索引的時候訪問的都是同一個頁表的同一個位置

細節3如果知道任意一個虛擬地址,如何得到所處的頁框

addr & 1111 1111 1111 1111 1111 0000 0000 0000

那如何得到 page結構體呢page 存儲在一個結構體數組里,只需要得到數組下標就可以了,數組下標 = 頁框號 / 4KB

細節4進程首次加載磁盤塊的時候,OS做什么

內存管理,申請內存就是申請 page,得到 page的數組下標,進而得到頁框的物理地址,填充頁表

細節5如果訪問的是 int呢?一個結構體呢?一個類變量呢所有變量只有一個地址,開辟空間時最小字節的地址

頁表轉換的時候,只能拿到第一個字節的地址,所以語言中存在一個類型的概念。起始地址 + 偏移量的方式就可以訪問了

細節6如何理解寫時拷貝

OS內,申請和管理內存是以4KB為單位的,寫時拷貝也是以4KB為單位的,申請一個新的頁表,更改映射關系

細節7我們用 new,malloc申請,怎么申請的時候1,4,n字節隨意申請的呢

new,malloc底層一定要調用系統調用(brk,mmap),只有OS才能訪問硬件,調用系統調用是有成本的,所以C,C++自己在語言層,會有自己的內存管理機制,類似STL中的空間配置器

現在,再來理解什么是線程。

//thread線程標示符,類似于進程pid,輸出型參數
//attr,線程的屬性,通常設置為nullptr
//start_routine回調函數,函數指針類型
//arg作為回調函數的參數
//成功返回0,失敗返回錯誤碼
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, 
void*(*start_routine)(void*), void* arg); //創建線程,執行指定的回調函數


在這里插入圖片描述

在這里插入圖片描述

大家有沒有發現問題呢?一個單進程代碼,竟然同時讓兩個死循環跑起來了

一個可執行程序,一個進程有一套頁表,那么,進程頁表的本質是什么?是進程看到資源的"窗口",通過虛擬地址與物理地址的映射,看到內存當中的代碼和數據

一個進程,兩個死循環同時跑起來了,是讓不同的線程執行不同的函數,本質是讓不同的線程,通過擁有不同區域的虛擬地址,擁有不同的資源,通過函數編譯的方式,進行了進程內部的"資源劃分"

Linux中多線程的實現:

一個進程中可以有一個執行流,那么可以有兩個,多個執行流嗎?當然是可以的那么這些執行流(線程)也是需要被OS管理,OS需要對這些線程分配新的頁表,文件,調度算法等資源嗎?答案是不需要的只需要給線程分配PCB就可以了,線程是進程內部的一個執行流,執行的是進程內部的一部分代碼資源沒有必要浪費這么多的資源為線程分配新的頁表等

Linux中,一個線程在進程內部運行,是如何運行的呢線程在進程的虛擬地址空間中運行,線程和進程共享同一個虛擬地址,頁表等資源(體現了線程是進程內部的一個執行流)

如何體現線程的輕量化呢讓不同的線程訪問虛擬地址空間中的一部分資源

那么,要如何才能做到,讓不同的線程看到自己的代碼資源呢?以代碼區為例:

讓不同的線程未來執行不同的入口函數即可(函數編譯的方式,進行進程內部資源的劃分)

在Linux中,線程的實現是用進程模擬的,復用了進程代碼和結構

那么,今天我們要如何理解進程和線程呢?

以前我們說 進程 = PCB + 自己的代碼和數據可是今天進程里有許多的PCB,這要如何理解呢

以前我們講的進程是內部只有一個執行流的進程,也叫做單線程的進程,而今天,我們需要對進程重新定義。

進程 = OS分配的所有 task_struct + 自己的代碼和數據 + 頁表、文件等資源

所以,我們說進程是承擔分配系統資源的基本實體

在CPU的角度,是不區分線程和進程的,它只拿著 task_struct 進行資源的調度,所以,執行流我們把它叫做輕量級進程

線程(task_struct)自然而然就是CPU調度的基本單位了

驗證:

ps -aL //查看所有的輕量級進程

在這里插入圖片描述
在這里插入圖片描述

Linux中不存在線程概念,只存在輕量級進程的概念,所以,Linux系統給用戶提供系統調用,只能提供輕量級進程的系統調用

在這里插入圖片描述

所有創建進程或者線程的系統調用底層都對 clone 進行了封裝

但是這個系統調用使用起來非常麻煩,所以創建線程時需要使用pthread庫,這個庫對clone這個函數做了封裝

CPU在獲取物理地址時其實并不是直接通過MMU查找頁表得到物理地址的,而是通過TLB(快表,其實就是緩存),如果TLB有虛擬地址到物理地址的映射就給CPU,否則就去查找頁表,在頁表中找到之后,把物理地址給CPU,同時把這條虛擬地址和物理地址的映射給TLB,進行緩存

線程的優點

1.創建一個新線程比創建一個新進程的代價小得多(進程需要創建PCB,虛擬地址空間,文件資源,頁表等,線程只需要創建PCB,共享進程的其它資源)

2.與進程之間的切換相比,線程之間的切換需要OS做的工作要少很多

. CPU內有CR3寄存器,保存的是頁表的基地址,進程間切換需要更新CR3寄存器的內容,線程間切換不需要,因為,同一個進程里所有線程擁有的是同一個頁表

. TLB就是緩存虛擬地址和物理地址的映射關系,線程間切換TLB不需要更新(線程共享進程的虛擬地址空間),進程切換TLB需要更新

. CPU內有一個 cache 硬件,這個硬件就是用來緩存代碼和數據的,在CPU訪問內存中的代碼和數據時,并不是不斷的進行虛擬地址到物理地址之間的映射訪問的,而是通過 cache 硬件訪問的,cache 硬件會預先加載一部分代碼和數據,線程切換時,cache 是不需要更新的,進程切換需要重新加載新的代碼和數據

在這里插入圖片描述
在這里插入圖片描述

可以看到,cache 大小還是挺大的(這與系統有關,也有MB的)

3.線程占用的資源比進程少很多(線程擁有進程的完整資源,但線程不需要重復再分配共享資源,如虛擬地址空間,頁表等,線程只需要維護少量資源,比如局部變量,函數調用,寄存器狀態)

線程的缺點

1.性能損失(過多的線程使用同一個處理器,增加了線程調度,而可用資源不變)

2.健壯性降低(多個線程共享了不該共享的變量,缺乏保護)

3.缺乏訪問控制(調用某些OS函數對整個進程造成影響)

線程獨有的數據

線程ID

寄存器(線程上下文數據)

進程間多線程共享

同一地址空間(代碼段,數據段...)

文件描述符表

每種信號的處理方式

當前工作目錄

用戶id,組id

寫一段程序驗證一下。

在這里插入圖片描述
在這里插入圖片描述

可以看到,線程之間是共享全局變量,函數和堆空間的。當然了,這只是一部分,畢竟線程是共享進程的虛擬地址空間的

二、線程控制

在這里插入圖片描述
在這里插入圖片描述

主線程運行3秒后結束,新線程10秒后才終止,但是主線程一旦退出,所有的線程都退出了,表示進程終止了

這是因為,進程創建時OS需要分配PCB等資源,那么當進程退出時,所有的資源也應該都要進行回收,所以,所有的線程都退出了

一般情況下,主線程應該最后退出,線程也需要等待,類似進程的 wait。要對新線程進行等待,否則,也會造成類似僵尸進程的問題

//成功返回0,失敗返回錯誤碼
//thread表明等待哪一個線程
//retval獲取新線程退出時的退出信息
//阻塞等待,main thread最后退出,自動解決新線程的內存泄漏問題(僵尸問題)
int pthread_join(pthread_t thread, void** retval);

在這里插入圖片描述
在這里插入圖片描述

可以看到,在多線程等待時,一旦只要有一個線程崩潰,所有的線程都崩潰了,而進程之間具有獨立性,即便是父子進程,子進程崩潰也不會影響父進程。所以說多線程的缺點是健壯性低

線程終止

. return

. exit

在這里插入圖片描述
在這里插入圖片描述

exit是用來終止進程的,變相導致所有的線程退出

. pthread_exit

void pthread_exit(void* retval);//線程退出

在這里插入圖片描述
在這里插入圖片描述

可以看到,使用系統調用退出線程,只會讓調用該函數的線程退出,不會影響到其它線程

. pthread_cancel

//成功返回0,失敗返回非0的錯誤碼
//thread取消目標線程的線程標識符
//線程退出,退出信息設置為-1(PTHREAD_CANCELED,是一個宏值)
int pthread_cancel(pthread_t thread); //取消線程

在這里插入圖片描述
在這里插入圖片描述

通常用于主線程取消其它線程

那么,線程自己可不可以取消自己呢?

先認識一個系統調用。

//返回的是調用線程的id
pthread_t pthread_self(void);

在這里插入圖片描述
在這里插入圖片描述

可以看到,主線程創建新線程的 tid 與新線程獲取自己的線程標識符是一樣的

在這里插入圖片描述
在這里插入圖片描述

可以看到,線程自己取消自己也是可以的。但是,這里為什么會將這條語句打印兩次呢

這是因為,pthread_cancel函數發送取消請求,對應的線程收到取消請求之后會在合適的點終止自己,不是立即終止

最佳實踐取消線程的方法在主線程中使用 pthread_cancel,本來就是主線程取消其它線程的

線程的傳參和返回值問題

前面我們介紹了 pthread_join 函數它的第二個參數就是將線程退出時的退出信息帶出來,現在,我們就要聊聊這個參數了,它是怎么通過這個參數把線程的退出信息帶出來的,畢竟我們只是使用了兩個系統調用而已。

還記得C語言中的 fopen函數嗎,它的返回類型是 FILE*類型的文件指針。那么這個FILE是什么呢?它有在哪里

這個前面我們是說過的,FILE是一個結構體,它在C標準庫里,fopen函數返回文件指針的時候,就必然創建了一個FILE對象那么這個對象在哪里呢它應該就在fopen函數內部申請的,然后通過 return 返回

那么,在多線程這里,線程的概念是誰提供的pthread庫提供的

那么,將來我們可以在一個進程中創建很多線程,在多個進程中呢?就會有更多的線程,所以,線程需不需要被管理呢?那些線程在被調度,那些線程退出了?答案是需要的

那么,就應該對線程進行先描述在組織像進程一樣有一個結構體 struct tcb,那么,這個結構體在哪里呢?不要忘了,前面說了,線程的概念是pthread庫提供的,所以,這個結構體應該在 pthread庫里面。這個結構體中就會有線程的各種屬性

將來線程退出時,return 將數據寫入到結構體中,主線程在等待時,將等待線程的標示符 tid傳入進去,就可以找到指定的線程了(結構體),然后通過第二個參數將結構體中的退出信息拷貝出來,不就拿到指定線程的退出信息了嗎

分離線程

默認情況下,新創建的線程是 joinable 的,線程退出后,需要對其進行 pthread_join操作,否則,會造成類似僵尸進程的問題(資源泄露)

如果不關心線程的返回值,我們可以告訴系統當線程退出時,自動釋放線程資源。這個時候就不需要進行 pthread_join了。

//成功返回0,失敗返回錯誤碼
//thread分離線程的線程標示符
int pthread_detach(pthread_t thread);

分離線程可以自己分離自己,也可以是其它線程分離目標線程

在這里插入圖片描述
在這里插入圖片描述

線程分離之后再去等待線程,就會出錯

在這里插入圖片描述
在這里插入圖片描述

這個時候可能有人要問了,主線程把新線程分離之后,如果是主線程先退出呢。那進程都終止了,新線程不是也會終止嗎?這個問題不用擔心,因為真正的軟件都是死循環的,新線程執行完自己的代碼就會退出,主線程是最后退出的

接下來再聊一下,pthread_cancel函數取消目標線程之后得到的退出信息

在這里插入圖片描述
在這里插入圖片描述

三、線程ID及虛擬地址空間布局

在這里插入圖片描述

我們說過,線程是由 pthread 庫提供的,所以線程是依賴于 pthread 庫的,將來 pthread 庫也要被加載到內存里。那么,一個進程中可以有許多線程,也可以加載許多進程啊這些進程中都會包含許多子線程,那么這些進程也是需要將 pthread 庫映射到自己的虛擬地址空間中的,調用mmap 系統調用實現的

在這里插入圖片描述

前面我們說過,線程有幾部分資源是獨占的,線程id、一組寄存器(線程的硬件上下文數據)、棧。描述線程的結構體是由 pthread庫維護的,線程棧并不是在虛擬地址空間中的棧區上的,而是在共享區上,由 pthread 庫在共享區上申請的一塊固定的內存空間,主線程的棧是在虛擬地址空間上的

那么,什么是線程局部存儲

前面我們說過,全局變量也是多線程之間共享的,那么如果我們要使線程之間獨自私有呢?

在這里插入圖片描述
在這里插入圖片描述

像這樣的就是線程局部存儲

今天的內容分享就到這里了,覺得不錯的給個一鍵三連吧。

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

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

相關文章

RabbitMQ 高可用實戰篇(Mirrored Queue + Cluster + 持久化整合)

RabbitMQ 高可用實戰篇(Mirrored Queue Cluster 持久化整合)1. 前言 在生產環境中,單節點 RabbitMQ 容易因故障導致消息丟失或業務中斷。 通過高可用隊列、集群部署和持久化策略,可以保證 消息可靠性、節點容錯和持續服務。 本文…

支持向量機:從理論到實踐

支持向量機:從理論到實踐 文章目錄支持向量機:從理論到實踐一。理論概述1. 線性可分支持向量機1.1 基本概念與數學形式1.2 函數間隔與幾何間隔1.3 間隔最大化與優化問題1.4 拉格朗日對偶理論與求解1.5 支持向量與決策函數2. 近似線性可分數據&#xff08…

LVS與Keepalived詳解(二)LVS負載均衡實現實操

文章目錄前言一、LVS-DR 模式詳解1.1 數據包流向分析1.2 DR 模式的特點二、LVS-DR 集群部署實戰2.1 環境準備2.2 配置負載調度器(Director Server)2.3 配置節點服務器(Real Server)2.4 測試驗證三、前期回顧3.1 LVS 三種工作模式及…

歸一化實現原理

歸一化(Normalization)是一種將數據轉換到相同尺度的預處理技術,它通常用于讓不同特征(或數據項)具有相同的量綱或范圍。在聯邦學習中,歸一化可以用來處理非獨立同分布(Non-IID)**數…

企業級實戰:構建基于Qt、C++與YOLOv8的模塊化工業視覺檢測系統

一、概述 在追求高效與精密的現代制造業中,自動化光學檢測(AOI)已成為保障產品質量的核心技術。傳統的質檢流程往往受限于人工效率與主觀判斷,難以滿足大規模、高精度的生產需求。本文旨在研發一套完整的、企業級的工業視覺異常檢…

【目標檢測】metrice_curve和loss_curve對比圖可視化

代碼如下: import warnings warnings.filterwarnings(ignore)import os import pandas as pd import numpy as np import matplotlib.pylab as pltpwd os.getcwd()names [model1, model2, model3,ours]plt.figure(figsize(10, 10))plt.subplot(2, 2, 1) for i in …

【LeetCode hot100|Week2】滑動窗口,子串

筆記用于個人復習和鞏固,題解非原創,參考LeetCode官方題解以及各個大佬的解法,希望給大家帶來幫助,同時筆記也能督促我學習進步 這周主要把滑動窗口和子串的題目刷了一遍 文章目錄Week2D1 滑動窗口209. 長度最小的子數組713. 乘積…

vue2純前端對接海康威視攝像頭實現實時視頻預覽

vue2純前端對接海康威視攝像頭實現實時視頻預覽一、環境準備二、代碼集成1.1 準備webrtcstreamer.js,粘貼即用,不用做任何修改1.2 封裝視頻組件,在需要視頻的地方引入此封裝的視頻組件即可,也是粘貼即用,注意其中impor…

Android 設置禁止截圖和禁止長截圖

1.禁止截圖 在 Activity 代碼中 , 可以在調用 setContentView 函數之前 ,為 Window 窗口對象 設置 LayoutParams.FLAG_SECURE 標志位 , 可以禁止對本界面進行截屏 ,Window 窗口對象 , 可通過 getWindow 方法獲取 ,核心代碼如下 :getWindow().setFlags(LayoutParams.FLAG_SECUR…

AR 巡檢在工業的應用|阿法龍XR云平臺

AR 巡檢的應用覆蓋電力、石油化工、智能制造、軌道交通、冶金等對設備可靠性和安全性要求極高的行業,具體場景包括:電力行業變電站內設備的狀態檢查:通過 AR 眼鏡掃描設備,實時顯示設備額定參數、歷史故障記錄、實時傳感器數據&am…

【C++】STL詳解(七)—stack和queue的介紹及使用

? 堅持用 清晰易懂的圖解 代碼語言, 讓每個知識點都 簡單直觀 ! 🚀 個人主頁 :不呆頭 CSDN 🌱 代碼倉庫 :不呆頭 Gitee 📌 專欄系列 : 📖 《C語言》🧩 《…

深度學習周報(9.8~9.14)

目錄 摘要 Abstract 1 LSTM相關網絡總結與對比 1.1 理論總結 1.2 代碼運行對比 2 量子計算入門 3 總結 摘要 本周首先總結了LSTM、Bi-LSTM與GRU的區別與優缺點,對比了三者實戰的代碼與效果,還另外拓展了一些循環神經網絡變體(包括窺視…

Quat 四元數庫使用教程:應用場景概述

基礎概念 四元數是一個包含四個元素的數組 [x, y, z, w],其中 x,y,z表示虛部,w 表示實部。單位四元數常用于表示3D空間中的旋轉。 1. 創建和初始化函數 create() - 創建單位四元數 應用場景:初始化一個新的四元數對象,通常作為其他…

【Java后端】Spring Boot 多模塊項目實戰:從零搭建父工程與子模塊

如何用 Spring Boot 搭建一個父工程 (Parent Project),并在其中包含多個子模塊 (Module),適合企業級項目或者需要分模塊管理的場景。Spring Boot 多模塊項目實戰:從零搭建父工程與子模塊在日常開發中,我們經常會遇到這樣的需求&am…

企業級AI會議系統技術實現:快鷺如何用AI重構會議全流程

摘要 本文深度解析快鷺AI會議系統的核心技術架構,重點探討其在語音識別、自然語言處理、數據集成和安全防護等方面的技術實現。通過對比傳統會議系統的技術痛點,分析快鷺AI如何通過技術創新實現會議籌備時間減少67%、數據調取速度提升100倍的顯著效果。…

【CSS學習筆記3】css特性

1css三大特性 1.1層疊性:就近原則,最新定義的樣式 1.2繼承性:子標簽集成父標簽的樣式,如文本和字號 行高的繼承:不加單位指的是當前文字大小的倍數 body {font: 12px/1.5 Microsoft YaHei;color: #be1313;} div {…

[C語言]常見排序算法①

1.排序的概念及常見的排序算法排序在咱們日常生活中十分的常見,就好比是網上購物的時候通常能夠選擇按照什么排序,比如價格、評論數量、銷量等。那么接下來咱們就來了解一些關于排序的概念。排序:所謂排序,就是使一串記錄&#xf…

文獻閱讀筆記:RS電子戰測試與測量技術文檔

信息來源:羅德與施瓦茨(Rohde & Schwarz)公司關于電子戰(Electronic Warfare, EW)測試與測量解決方案專業技術文檔。 該文檔由臺灣地區應用工程師Mike Wu撰寫,核心圍繞電子戰基礎、雷達系統、實戰應用及…

別再糾結 Postman 和 Apifox 了!這款開源神器讓 API 測試更簡單

別再糾結 Postman 和 Apifox 了!這款開源神器讓 API 測試更簡單🔥 作為一名開發者,你是否還在為選擇 API 測試工具而糾結?Postman 太重、Apifox 要聯網、付費功能限制多?今天給大家推薦一款完全免費的開源替代方案 ——…

微調神器LLaMA-Factory官方保姆級教程來了,從環境搭建到模型訓練評估全覆蓋

1. 項目背景 開源大模型如LLaMA,Qwen,Baichuan等主要都是使用通用數據進行訓練而來,其對于不同下游的使用場景和垂直領域的效果有待進一步提升,衍生出了微調訓練相關的需求,包含預訓練(pt)&…