Linux系統編程——線程(1)

目錄

  • 線程概要
    • Linux內核線程實現原理
    • 線程的共享/不共享資源
    • 線程優缺點
  • 線程控制原語
    • pthread_self
    • pthread_create
    • pthread_exit
    • pthread_join
    • pthread_cancel
    • 終止線程方式
    • 控制原語對比

前情提要: Linux用戶級線程和內核級線程區別

線程概要

Linux內核線程實現原理

類Unix系統中,早期是沒有“線程”概念的,80年代才引入,借助進程機制實現出了線程的概念。因此在這類系統中,進程和線程關系密切。

  1. 輕量級進程(light-weight process),也有PCB,創建線程使用的底層函數和進程一樣,都是clone

  2. 從內核里看進程和線程是一樣的,都有各自不同的PCB,但是PCB中指向內存資源的三級頁表是相同的

  3. 進程可以蛻變成線程

  4. 線程可看做寄存器和棧的集合

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

查看線程命令:ps -elf|grep thread

1556722663332

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


線程的共享/不共享資源

線程共享資源線程不共享資源
文件描述符表線程id
每種信號的處理方式處理器現場和棧指針(內核棧)
當前工作目錄獨立的棧空間(用戶空間棧)
用戶ID和組IDerrno變量
內存地址空間(.text/.data/.bss/heap/共享庫)信號屏蔽字
調度優先級

線程優缺點

優點: 1. 提高程序并發性 2. 開銷小 3. 數據通信、共享數據方便

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

線程控制原語


pthread_self

獲取線程ID。其作用對應進程中 getpid() 函數。

? pthread_t pthread_self(void); 返回值:成功:0; 失敗:無!

? 線程ID:pthread_t類型,本質:在Linux下為無符號整數(%lu),其他系統中可能是結構體實現

? 線程ID是進程內部,識別標志。(兩個進程間,線程ID允許相同)


pthread_create

創建一個新線程。 其作用,對應進程中fork() 函數。

? int pthread_create(pthread_t thread, const pthread_attr_t attr, void (start_routine) (void ), void arg);

? 返回值:成功:0; 失敗:錯誤號 -----Linux環境下,所有線程特點,失敗均直接返回錯誤號。

參數:

? pthread_t:當前Linux中可理解為:typedef unsigned long int pthread_t;

參數1:傳出參數,保存系統為我們分配好的線程ID

? 參數2:通常傳NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數。

? 參數3:函數指針,指向線程主函數(線程體),該函數運行結束,則線程結束。

? 參數4:線程主函數執行期間所使用的參數。

練習:創建一個新線程,打印線程ID。注意:鏈接線程庫 -lpthread

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void *tfn(void *arg)
{printf("I'm thread, Thread_ID = %lu\n", pthread_self());return NULL;
}int main(void)
{pthread_t tid;pthread_create(&tid, NULL, tfn, NULL);sleep(1);printf("I am main, my pid = %d\n", getpid());return 0;
}

線程默認共享數據段、代碼段等地址空間,常用的是全局變量,或者傳參形式。而進程不共享全局變量,只能借助mmap。

全局變量:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>int var = 100;void *tfn(void *arg)
{var = 200;printf("thread\n");return NULL;
}int main(void)
{printf("At first var = %d\n", var);pthread_t tid;pthread_create(&tid, NULL, tfn, NULL);sleep(1);printf("after pthread_create, var = %d\n", var);return 0;
}

1556723666858

傳參:

#include <func.h>void *tfn(void *arg){int* var = (int*)arg;*var = 200;printf("thread\n");return NULL;
}int main()
{int var = 100;printf("At first var = %d\n", var);pthread_t tid;pthread_create(&tid, NULL, tfn, &var);sleep(1);printf("after pthread_create, var = %d\n", var);return 0;
}

1556723909222


pthread_exit

作用:將單個線程退出

? void pthread_exit(void *retval); 參數:retval表示線程退出狀態,通常傳NULL

線程中,**禁止使用exit函數,會導致進程內所有線程全部退出**。所以,多線程環境中,應盡量少用,或者不使用exit函數,取而代之使用pthread_exit函數,將單個線程退出。任何線程里exit導致進程退出,其他線程未工作結束,主控線程退出時不能return或exit。

另注意,pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者是用malloc分配的,不能在線程函數的棧上分配,因為當其它線程得到這個返回指針時線程函數已經退出了。


pthread_join

阻塞等待線程退出,獲取線程退出狀態 其作用,對應進程中 waitpid() 函數。

? int pthread_join(pthread_t thread, void **retval); 成功:0;失敗:錯誤號

? 參數:thread:線程ID (【注意】:不是指針);retval:存儲線程結束狀態。

? 對比記憶:

? 進程中:main返回值、exit參數-->int;等待子進程結束 wait 函數參數-->int *

? 線程中:線程主函數返回值、pthread_exit-->void *;等待線程結束 pthread_join 函數參數-->void **

調用該函數的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:

  1. 如果thread線程通過return返回,retval所指向的單元里存放的是thread線程函數的返回值。

  2. 如果thread線程被別的線程調用pthread_cancel異常終止掉,retval所指向的單元里存放的是常數PTHREAD_CANCELED。

  3. 如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳給pthread_exit的參數。

  4. 如果對thread線程的終止狀態不感興趣,可以傳NULL給retval參數。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>typedef struct {int a;int b;
} exit_t;void *tfn(void *arg)
{exit_t *ret;ret = malloc(sizeof(exit_t)); ret->a = 100;ret->b = 300;pthread_exit((void *)ret);
}int main(void)
{pthread_t tid;exit_t *retval;pthread_create(&tid, NULL, tfn, NULL);/*調用pthread_join可以獲取線程的退出狀態*/pthread_join(tid, (void **)&retval);      //wait(&status);printf("a = %d, b = %d \n", retval->a, retval->b);return 0;
}

1556724875987


pthread_cancel

殺死(取消)線程 其作用,對應進程中 kill() 函數。

? int pthread_cancel(pthread_t thread); 成功:0;失敗:錯誤號

? 【注意】:線程的取消并不是實時的,而有一定的延時。需要等待線程到達某個取消點(檢查點)。

? 類似于玩游戲存檔,必須到達指定的場所(存檔點,如:客棧、倉庫、城里等)才能存儲進度。殺死線程也不是立刻就能完成,必須要到達取消點。

? 取消點:是線程檢查是否被取消,并按請求進行動作的一個位置。通常是一些系統調用creat,open,pause,close,read,write..... 執行命令man 7 pthreads可以查看具備這些取消點的系統調用列表。也可參閱 APUE.12.7 取消選項小節。

可粗略認為一個系統調用(進入內核)即為一個取消點。如線程中沒有取消點,可以通過調用pthreestcancel函數自行設置一個取消點。

被取消的線程, 退出值定義在Linux的pthread庫中。常數PTHREAD_CANCELED的值是-1。可在頭文件pthread.h中找到它的定義:#define PTHREAD_CANCELED ((void *) -1)。因此當我們對一個已經被取消的線程使用pthread_join回收時,得到的返回值為-1。

終止線程的三種方法。注意“取消點”的概念。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>void *tfn1(void *arg)
{printf("thread 1 returning\n");return (void *)111; 
}void *tfn2(void *arg)
{printf("thread 2 exiting\n");pthread_exit((void *)222);
}void *tfn3(void *arg)
{while (1) {//printf("thread 3: I'm going to die in 3 seconds ...\n");//sleep(1);pthread_testcancel();   //自己添加取消點*/}return (void *)666;
}int main(void)
{pthread_t tid;void *tret = NULL;pthread_create(&tid, NULL, tfn1, NULL);pthread_join(tid, &tret);printf("thread 1 exit code = %d\n\n", (int)tret);pthread_create(&tid, NULL, tfn2, NULL);pthread_join(tid, &tret);printf("thread 2 exit code = %d\n\n", (int)tret);pthread_create(&tid, NULL, tfn3, NULL);sleep(3);pthread_cancel(tid);pthread_join(tid, &tret);printf("thread 3 exit code = %d\n", (int)tret);return 0;
}

1556725457481

終止線程方式

總結:終止某個線程而不終止整個進程,有三種方法:

  1. 從線程主函數return。這種方法對主控線程不適用,從main函數return相當于調用exit。

  2. 一個線程可以調用pthread_cancel終止同一進程中的另一個線程。

  3. 線程可以調用pthread_exit終止自己。


控制原語對比

? 進程 線程

? fork pthread_create

? exit pthread_exit

? wait pthread_join

? kill pthread_cancel

? getpid pthread_self 命名空間

轉載于:https://www.cnblogs.com/Mered1th/p/10801287.html

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

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

相關文章

TCP/IP(二):IP協議

IP協議處于OSI參考模型的第三層——網絡層&#xff0c;網絡層的主要作用是實現終端節點間的通信。IP協議是網絡層的一個重要協議&#xff0c;網絡層中還有ARP(獲取MAC地址)和ICMP協議(數據發送異常通知) 數據鏈路層的作用在于實現同一種數據鏈路下的包傳遞&#xff0c;而網絡層…

Ajax — 大事件項目(第四天)

分類管理 添加分類 初步使用彈出層 給 “添加分類” 綁定一個單擊事件單擊事件中&#xff0c;使用 layer.open() 實現一個彈出層 type: 1, 彈層的類型是頁面層title, “添加文字分類”content: ‘字符串&#xff0c;DOM’,area: [‘500px’, ‘250px’] // ---------------…

redis學習(四)

一、Redis 鍵(key) 1、Redis 鍵命令用于管理 redis 的鍵。 2、Redis 鍵命令的基本語法如下&#xff1a;redis 127.0.0.1:6379> COMMAND KEY_NAME 3、常用key命令 keys * 獲取所有的keyselect 0 選擇第一個庫move myString 1 將當前的數據庫key移動到某個…

TCP/IP(三):IP協議相關技術

在前兩篇文章中&#xff0c;我分別介紹了數據鏈路層和網絡層的IP協議。雖然這個系列教程的重點是搞定 TCP/IP&#xff0c;不過不用著急&#xff0c;本文簡要介紹完與 IP 協議相關的技術&#xff0c;下一篇文章就會正式、詳細的介紹 傳輸層與 TCP 協議。這篇文章會介紹 DNS、ARP…

Node — 第一天

Node-01 會 JavaScript&#xff0c;就能學會 Node.js&#xff01;&#xff01;&#xff01; **Node.js 的官網地址&#xff1a; ** Node.js 的學習路徑&#xff1a; JavaScript 基礎語法 Node.js 內置 API 模塊&#xff08;fs、path、http等&#xff09; 第三方 API 模塊&…

TCP/IP(四):TCP 與 UDP 協議簡介

從本章開始&#xff0c;我們開始介紹最重要的傳輸層。傳輸層位于 OSI 七層模型的第四層&#xff08;由下往上&#xff09;。顧名思義&#xff0c;傳輸層的主要作用是實現應用程序之間的通信。網絡層主要是保證不同數據鏈路下數據的可達性&#xff0c;至于如何傳輸數據則是由傳輸…

Node — 第二天

http模塊 搭建服務器的步驟 ① 導入 http 模塊 ② 創建 web 服務器實例 ③ 為服務器實例綁定 request 事件&#xff0c;監聽客戶端的請求 ④ 啟動服務器 // ① 導入 http 模塊 const http require(http);// ② 創建 web 服務器實例 const server http.createServer();/…

《學習之道》第九章不要突擊工作

靈感從天而降的時刻確實是存在的。 這樣少見的創造性突破&#xff0c;通常是在經歷了一番神經緊張的準備、竭盡全力的努力&#xff0c;甚至包括熬夜工作后才姍姍來遲。這與 數學 和 科學標準的一天學習 是大不相同的。 它更像體育運動&#xff1a;每隔一陣子會有一天的比賽&…

TCP/IP(五):TCP 協議詳解

上一節 中講過&#xff0c;TCP 協議是面向有連接的協議&#xff0c;它具有丟包重發和流量控制的功能&#xff0c;這是它區別于 UDP 協議最大的特點。本文就主要討論這兩個功能。 數據包重發 數據發送 丟包重發的前提是發送方能夠知道接收方是否成功的接收了消息。所以&#…

nodeJS — 學習的筆記

Node介紹 為什么要學習Node.js 企業需求 具有服務端開發經驗更改front-endback-end全棧開發工程師基本的網站開發能力 服務端前端運維部署 多人社區 [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MVqHkEIo-1588084625862)(C:\Users\A\AppData\R…

TCP/IP(六):HTTP 與 HTTPS 簡介

本文是準備面試過程中網絡部分總結整理的最后一篇文章&#xff0c;主要介紹以下知識&#xff1a; HTTP 協議概述POST 請求和 GET 請求Cookie 和 Session數據傳輸時的加密HTTPS 簡介 HTTP 協議 在 OSI 七層模型中&#xff0c;HTTP 協議位于最頂層的應用層中。通過瀏覽器訪問網…

Node — 第三天

模塊化 什么是模塊化 模塊化是指解決一個復雜問題時&#xff0c;自頂向下逐層把系統劃分成若干模塊的過程。 對于整個系統來說&#xff0c;模塊是可組合、分解和更換的單元。 生活中的模塊化 編程中的模塊化 編程領域中的模塊化&#xff0c;就是遵守固定的規則&#xff0c;…

FireDAC 中文字段過濾問題

當使用 FireDAC Filter 過濾數據的時候&#xff0c;通常這樣寫&#xff1a; FDMemTable.Filtered : False; FDMemTable1.Filter : 姓名 string(edtFilter.Text).QuotedString; FDMemTable.Filtered : True; 將會報錯&#xff1a;[FireDAC][Stan][Eval]-107. Invalid characte…

UIScrollViewDelegate-代理API詳解

在iOS編程中&#xff0c;經常用到UIScrollView控件。自然也會用到UIScrollViewDelegate協議。 今天就抽空&#xff0c;研究了一下UIScrollViewDelegate中所有的委托方法。 僅做Mark&#xff01;如有&#xff0c;錯誤之處&#xff0c;歡迎留言指正&#xff0c;交流&#xff01;貼…

License for package Android SDK Build-Tools 28.0.2 not accepted.(MAC)

https://blog.csdn.net/user11223344abc/article/details/83997907轉載于:https://www.cnblogs.com/dtdxrk/p/10812469.html

Express — 使用步驟

Express Express 介紹安裝搭建服務器的步驟express提供的新方法GET接口 獲取GET中的請求參數 POST接口 獲取POST請求提交的數據 中間件原理中間件語法中間件初體驗&#xff08;設置響應頭&#xff09;中間件開放靜態資源中間件接收POST請求體中間件返回404頁面 express 介紹 E…

大模型工具_Langchain-Chatchat

https://github.com/chatchat-space/Langchain-Chatchat 原Langchain-ChatGLM 1 功能 整體功能&#xff0c;想解決什么問題 基于 Langchain 與 ChatGLM 等LLM模型&#xff0c;搭建一套針對中文場景與開源模型&#xff0c;界面友好、可離線運行的知識庫問答解決方案。 當前解決…

iOS 鍵盤風格詳解UIKeyboardType

一、鍵盤風格 UIKit框架支持8種風格鍵盤。 [java] view plaincopy print?typedef enum { UIKeyboardTypeDefault, // 默認鍵盤&#xff1a;支持所有字符 UIKeyboardTypeASCIICapable, // 支持ASCII的默認鍵盤 UIKeyboardTypeNu…

MySQL Hardware--FIO壓測

FIO參數 filename/export/1.txt 支持文件系統或者裸設備&#xff0c;-filename/dev/sda2或-filename/dev/sdb direct1 測試過程繞過機器自帶的buffer&#xff0c;使測試結果更真實 rwrandwread 測試隨機讀的I/O rwrandwrite 測試隨機…

Node — 第四天(Promise與路由)

Promise - ES6新對象 Promise能夠處理異步程序。 回調地獄 JS中或node中&#xff0c;都大量的使用了回調函數進行異步操作&#xff0c;而異步操作什么時候返回結果是不可控的&#xff0c;如果我們希望幾個異步請求按照順序來執行&#xff0c;那么就需要將這些異步操作嵌套起來…