日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

【Linux】线程

發(fā)布時間:2023/12/31 linux 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Linux】线程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

目錄
1.Linux下的線程概念
2.Linux線程控制:pthread線程庫
在單執(zhí)行流的進(jìn)程中,此執(zhí)行流獨占了進(jìn)程的所有資源

在一個進(jìn)程內(nèi)部,有時不一定只有一個執(zhí)行流,在多執(zhí)行流下,多個執(zhí)行流共享了進(jìn)程的地址空間,我們把“一個程序內(nèi)部的控制序列”叫做線程

線程本質(zhì)是在進(jìn)程的地址空間內(nèi)運行
進(jìn)程的切換涉及到頁表映射的切換,而線程的切換只是切換了指令序列而在同一個地址空間中進(jìn)行

那么我們給出下面兩個重要概念

  • 進(jìn)程是操作系統(tǒng)分配資源的基本實體
  • 線程是進(jìn)程內(nèi)部的一個執(zhí)行流

舉個栗子:
在現(xiàn)實生活中,假如我們把社會資源的基本單位看作是家庭,比如我們經(jīng)常以家庭年收入統(tǒng)計社會財富的分配狀況,那么此時:

  • 操作系統(tǒng)—>社會
  • 進(jìn)程—>家庭
  • 線程—>家庭成員

家庭成員共享了家庭的資源,家庭成員之間有共享的資源,也有私人的小秘密。

透過進(jìn)程虛擬地址空間,可以看到進(jìn)程的大部分資源,將進(jìn)程資源合理分配給每個執(zhí)行流,就形成了線程執(zhí)行流

線程可以被創(chuàng)建、等待、終止、控制
家庭有生老病死、家規(guī)等等…

1.Linux下的線程概念

在Linux下,其實沒有真正意義上的線程概念,是用進(jìn)程來模擬的

Linux的進(jìn)程叫做輕量級進(jìn)程

LWP是輕量級進(jìn)程,在Linux下進(jìn)程是資源分配的基本單位,線程是cpu調(diào)度的基本單位,而線程使用進(jìn)程pcb描述實現(xiàn),操作系統(tǒng)在創(chuàng)建線程時給每個線程都創(chuàng)建一個pcb結(jié)構(gòu)體,并且同一個進(jìn)程中的所有pcb共用同一個虛擬地址空間,因此相較于傳統(tǒng)進(jìn)程更加的輕量化有了更多執(zhí)行流之后。進(jìn)程變成了分配資源的基本實體,進(jìn)程一旦被創(chuàng)建好之后,里面可能有多個執(zhí)行流

與進(jìn)程相比,線程在CPU執(zhí)行時可能更加輕量化:pcb上下文肯定要切換,但是線程的地址空間、頁表不用換CPU調(diào)度時,看到的是LWP,也就是輕量級進(jìn)程Light Weight Process

1.1 線程的優(yōu)點

  • 創(chuàng)建一個新線程的代價要比創(chuàng)建一個新進(jìn)程小得多
  • 與進(jìn)程之間的切換相比,線程之間的切換需要OS的工作量更少
  • 線程占用的資源比進(jìn)程少得多

1.2 線程能夠看到進(jìn)程的所有資源,因為所有PCB都共享地址空間

  • 好處:線程間通信成本低
  • 壞處:存在大量的臨界資源,勢必需要使用各種互斥和同步機(jī)制保證臨界資源的安全

1.3 線程異常

線程是進(jìn)程的一個執(zhí)行分支,當(dāng)發(fā)生野指針/除0等異常操作導(dǎo)致線程退出的同時,也意味著進(jìn)程觸發(fā)了該錯誤,操作系統(tǒng)會給進(jìn)程發(fā)送信號,終止進(jìn)程。這體現(xiàn)了多線程下魯棒性降低了。

1.4 線程的共享與獨享

線程共享

  • 文件描述符表
  • 每種信號的處理方式
  • 工作目錄
  • 用戶id組id

獨有:

  • 上下文數(shù)據(jù)(寄存器):體現(xiàn)了多個線程是可以切換的
  • 獨立的棧結(jié)構(gòu):體現(xiàn)了線程是獨立運行的,各自的上下文數(shù)據(jù)不會互相影響

2.Linux線程控制:pthread線程庫

首先要強(qiáng)調(diào)一點:pthread庫并不是系統(tǒng)庫,而是Linux下為了模擬線程而采用的第三方庫。本質(zhì)是封裝了對于輕量級進(jìn)程的某些操作。

鏈接這些線程函數(shù)庫時要使用編譯器命令的“-lpthread”選項
接口:
pthread_create()
pthread_join()
pthread_cancel()
pthread_exit()
pthread_self()

2.1線程的創(chuàng)建

功能: 創(chuàng)建一個新的線程
原型

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

4個參數(shù)

  • thread:返回線程ID,這是進(jìn)程地址空間的共享區(qū)的地址
  • attr:設(shè)置線程的屬性,attr為NULL表示使用默認(rèn)屬性
  • start_routine:是個函數(shù)地址,線程啟動后要執(zhí)行的函數(shù)
  • arg:傳給線程啟動函數(shù)的參數(shù),如果需要傳入多個參數(shù),可以用結(jié)構(gòu)體封裝

返回值: 成功返回0;失敗返回錯誤碼

讓我們來玩一玩線程的創(chuàng)建,這邊我們在main函數(shù),也就是主線程創(chuàng)建了新線程,又在新線程中創(chuàng)建了另外5個更新的線程,使用ps -aL 命令查看輕量級進(jìn)程

#include <iostream> #include <pthread.h> #include <unistd.h> using namespace std; void* routine(void* args) {while(true) {cout << "I am a thread" << endl;sleep(1);}return nullptr; } void* ThreadRoute(void* args) {pthread_t tids[5];for(int i = 0; i < 5; i++) {pthread_create(tids+i, nullptr, routine, nullptr);}for(int i = 0; i < 5; i++) {pthread_join(tids[i], nullptr);}return nullptr; } int main() {pthread_t tid; int thread_id = pthread_create(&tid, nullptr, ThreadRoute, (void*)"I am thread");while(true) {std::cout << "I am main process" << std::endl;sleep(2);}return 0; }

2.2 線程的終止

  • 從自己的例程中return,線程退出
  • 主線程退出,進(jìn)程退出

有三種方法終止某個線程而不終止進(jìn)程:

  • 從線程函數(shù)return。這種方法對主線程不適用,從main函數(shù)return相當(dāng)于調(diào)用exit。
  • 線程可以調(diào)用pthread_ exit終止自己。
  • 一個線程可以調(diào)用pthread_ cancel終止同一進(jìn)程中的另一個線程
  • 線程一般終止之后,main thread等待,不等待會造成僵尸

    為防內(nèi)存泄漏,要保證主線程最后退出,讓新線程正常結(jié)束

    retral從pcb中提取退出碼

    • 已經(jīng)退出的線程,其空間沒有被釋放,仍然在進(jìn)程的地址空間內(nèi)。
    • 創(chuàng)建新的線程不會復(fù)用剛才退出線程的地址空間。
    #include <iostream> #include <pthread.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> using namespace std;void* ThreadRoute(void* args) {int a = 0;int b = 10;b = b/a;return nullptr; }int main() {if(fork() == 0) {pthread_t tid; int thread_id = pthread_create(&tid, nullptr, ThreadRoute, (void*)"I am thread");exit(11);}int status;pid_t id = waitpid(-1, &status, 0);cout << "exit code: " << ((status>>8)&0xff) << endl;cout << "sig: " << ((status)&0x7f) << endl;return 0; }

    我們精心設(shè)計了除0錯誤:

    在進(jìn)程exit code中,存有退出碼+信號,而信號是針對進(jìn)程的,線程崩潰進(jìn)程隨之崩潰

    子進(jìn)程創(chuàng)建的線程的除0錯誤導(dǎo)致OS給子進(jìn)程發(fā)送信號,子進(jìn)程的主線程崩潰,退出碼收不到

    關(guān)于pthread_cancel

    • sleep新線程被創(chuàng)建了但是沒有被調(diào)度你就cancel了,建議一定要讓新線程被調(diào)度跑起來
    • cancel具有一定的延時性,并不一定立即執(zhí)行
    • cancel建議不要再開頭或結(jié)尾使用

    2.3 線程等待

    為什么要等待?

    • 已經(jīng)退出的線程,其空間沒有被釋放,仍然在進(jìn)程的地址空間內(nèi)。
    • 創(chuàng)建新的線程不會復(fù)用剛才退出線程的地址空間。

    功能:等待線程結(jié)束
    原型

    int pthread_join(pthread_t thread, void **value_ptr);

    參數(shù)

    • thread:線程ID
    • value_ptr:它指向一個指針,后者指向線程的返回值

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

    調(diào)用該函數(shù)的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的方法終止,通過pthread_join得到的
    終止?fàn)顟B(tài)是不同的,總結(jié)如下:

  • 如果thread線程通過return返回,value_ ptr所指向的單元里存放的是thread線程函數(shù)的返回值。
  • 如果thread線程被別的線程調(diào)用pthread_ cancel異常終掉,value_ ptr所指向的單元里存放的是常數(shù)PTHREAD_ CANCELED。
  • 如果thread線程是自己調(diào)用pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的參數(shù)。
  • 如果對thread線程的終止?fàn)顟B(tài)不感興趣,可以傳NULL給value_ ptr參數(shù)。
  • 我們讓線程return一個new出來的5

    #include <iostream> #include <pthread.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> using namespace std;void* ThreadRoute(void* args) {int* p = new int(5);cout << "threadID: " << pthread_self() << endl;sleep(2);return (void*)p; } int main() { if(fork() == 0) {pthread_t tid; void* ret;int thread_id = pthread_create(&tid, nullptr, ThreadRoute, (void*)"I am thread");pthread_join(tid, &ret);cout << "ret: " << *(int*)ret << endl;delete (int*)ret;exit(11);}int status;pid_t id = waitpid(-1, &status, 0);cout << "exit code: " << ((status>>8)&0xff) << endl;cout << "sig: " << ((status)&0x7f) << endl;return 0; }

    2.4 獲取線程ID

    pthread_ create函數(shù)會產(chǎn)生一個線程ID,存放在第一個參數(shù)指向的地址中。該線程ID和前面說的線程ID不是一回事。

    前面講的線程ID屬于進(jìn)程調(diào)度的范疇。因為線程是輕量級進(jìn)程,是操作系統(tǒng)調(diào)度器的最小單位,所以需要一個數(shù)值來唯一表示該線程。

    pthread_ create函數(shù)第一個參數(shù)指向一個虛擬內(nèi)存單元,該內(nèi)存單元的地址即為新創(chuàng)建線程的線程ID,屬于NPTL線程庫的范疇。線程庫的后續(xù)操作,就是根據(jù)該線程ID來操作線程的。

    線程庫NPTL提供了pthread_ self函數(shù),可以獲得線程自身的ID:

    pthread_t pthread_self(void); #include <iostream> #include <pthread.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> using namespace std;void* ThreadRoute(void* args) {cout << "threadID: " << pthread_self() << endl; } int main() {if(fork() == 0) {pthread_t tid; int thread_id = pthread_create(&tid, nullptr, ThreadRoute, (void*)"I am thread");exit(11);}int status;pid_t id = waitpid(-1, &status, 0);return 0; }

    2.5 線程分離

    線程分離的本質(zhì)是讓主線程不join新線程,不關(guān)心返回值,從而讓新線程退出的時候自動回收

    如果一個線程被設(shè)置為分離狀態(tài),他就不該被join
    如果你join,結(jié)果就是未定義
    即便線程被設(shè)置為分離狀態(tài),但是如果該線程依舊出錯崩潰,還是會影響主線程和其他正常線程 -> 所有線程在同一個地址空間中運行

    int pthread_detach(pthread_t thread);

    可以是線程組內(nèi)其他線程對目標(biāo)線程進(jìn)行分離,也可以是線程自己分離:

    pthread_detach(pthread_self());

    線程分離后,主線程等待不到新線程:

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> void* thread_run(void* arg) {pthread_detach(pthread_self());printf("%s\n", (char*)arg);return NULL; } int main(void) {pthread_t tid;if (pthread_create(&tid, NULL, thread_run, "thread1 run...") != 0) {printf("create thread error\n");return 1;}int ret = 0;sleep(1);//很重要,要讓線程先分離,再等待if (pthread_join(tid, NULL) == 0) {printf("pthread wait success\n");ret = 0;}else {printf("pthread wait failed\n");ret = 1;}return ret; }

    3.總結(jié)補(bǔ)充

    為什么要有pthread原生線程庫?

    linux沒有真正的線程,是用進(jìn)程來模擬的

    操作系統(tǒng)是不會直接提供類似的線程創(chuàng)建、退出、分離、等待相關(guān)的system call 接口,但是會提供創(chuàng)建輕量級進(jìn)程的接口,但是用戶需要有所謂的線程創(chuàng)建、退出、分離、等待相關(guān)的接口啊,所以為了更好的適配輕量級進(jìn)程的接口,就模擬封裝了一個用戶層原生線程庫NPTL

    可是進(jìn)程是PCB去管理的,用戶層先要以管理線程的辦法來管理輕量級進(jìn)程,就得知道,線程id,狀態(tài),優(yōu)先級,其他屬性,從而用來進(jìn)行用戶線程管理!

    所以tcb不用內(nèi)核維護(hù),而在用戶層維護(hù)

    曾經(jīng)的pthead_t 是用戶層的概念,是pthread庫中的地址


    相當(dāng)于警察派了幾個線人去犯罪集團(tuán)當(dāng)臥底,警察不懂行話,但是線人懂,所以線人充當(dāng)了和犯罪分子溝通的角色,而線人反手會把情報用人話反饋給警察

    警察只需要來操縱、管理線人,就能得到情報

    總結(jié)

    以上是生活随笔為你收集整理的【Linux】线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。