最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Pthread并發(fā)編程之線程基本元素和狀態(tài)的剖析

 更新時間:2022年11月03日 14:52:17   作者:一無是處的研究僧  
本篇文章主要給大家介紹pthread并發(fā)編程當中關于線程的基礎概念,并且深入剖析進程的相關屬性和設置,以及線程在內(nèi)存當中的布局形式,幫助大家深刻理解線程

前言

在本篇文章當中講主要給大家介紹 pthread 并發(fā)編程當中關于線程的基礎概念,并且深入剖析進程的相關屬性和設置,以及線程在內(nèi)存當中的布局形式,幫助大家深刻理解線程。

深入理解 pthread_create

基礎例子介紹

在深入解析 pthread_create 之前,我們先用一個簡單的例子簡單的認識一下 pthread,我們使用 pthread 創(chuàng)建一個線程并且打印 Hello world 字符串。

#include?<stdio.h>
#include?<pthread.h>

void*?func(void*?arg)?{
??printf("Hello?World?from?tid?=?%ld\n",?pthread_self());?//?pthread_self?返回當前調(diào)用這個函數(shù)的線程的線程?id
??return?NULL;
}

int?main()?{

??pthread_t?t;?//?定義一個線程
??pthread_create(&t,?NULL,?func,?NULL);?//?創(chuàng)建線程并且執(zhí)行函數(shù)?func?

??//?wait?unit?thread?t?finished
??pthread_join(t,?NULL);?//?主線程等待線程?t?執(zhí)行完成然后主線程才繼續(xù)往下執(zhí)行

??printf("thread?t?has?finished\n");
??return?0;
}

編譯上述程序:

clang?helloworld.c?-o?helloworld.out?-lpthread
或者
gcc?helloworld.c?-o?helloworld.out?-lpthread

在上面的代碼當中主線程(可以認為是執(zhí)行主函數(shù)的線程)首先定義一個線程,然后創(chuàng)建線程并且執(zhí)行函數(shù) func ,當創(chuàng)建完成之后,主線程使用 pthread_join 阻塞自己,直到等待線程 t 執(zhí)行完成之后主線程才會繼續(xù)往下執(zhí)行。

我們現(xiàn)在仔細分析一下 pthread_create 的函數(shù)簽名,并且對他的參數(shù)進行詳細分析:

int?pthread_create(pthread_t?*thread,?const?pthread_attr_t?*attr,
??????????????????????????void?*(*start_routine)?(void?*),?void?*arg);
  • 參數(shù) thread 是一個類型為 pthread_t 的指針對象,將這個對象會在 pthread_create 內(nèi)部會被賦值為存放線程 id 的地址,在后文當中我們將使用一個例子仔細的介紹這個參數(shù)的含義。
  • 參數(shù) attr 是一個類型為 pthread_attr_t 的指針對象,我們可以在這個對象當中設置線程的各種屬性,比如說線程取消的狀態(tài)和類別,線程使用的棧的大小以及棧的初始位置等等,在后文當中我們將詳細介紹這個屬性的使用方法,當這個屬性為 NULL 的時候,使用默認的屬性值。
  • 參數(shù) start_routine 是一個返回類型為 void* 參數(shù)類型為 void* 的函數(shù)指針,指向線程需要執(zhí)行的函數(shù),線程執(zhí)行完成這個函數(shù)之后線程就會退出。
  • 參數(shù) arg ,傳遞給函數(shù) start_routine 的一個參數(shù),在上一條當中我們提到了 start_routine 有一個參數(shù),是一個 void 類型的指針,這個參數(shù)也是一個 void 類型的指針,在后文當中我們使用一個例子說明這個參數(shù)的使用方法。

深入理解參數(shù) thread

在下面的例子當中我們將使用 pthread_self 得到線程的 id ,并且通過保存線程 id 的地址的變量 t 得到線程的 id ,對兩個得到的結(jié)果進行比較。

#include?<stdio.h>
#include?<pthread.h>

void*?func(void*?arg)?{

??printf("線程自己打印線程\tid?=?%ld\n",?pthread_self());

??return?NULL;
}

int?main()?{

??pthread_t?t;
??pthread_create(&t,?NULL,?func,?NULL);
??printf("主線程打印線程?t?的線程?id?=?%ld\n",?*(long*)(&t));
??pthread_join(t,?NULL);
??return?0;
}

上面程序的執(zhí)行結(jié)果如下圖所示:

根據(jù)上面程序打印的結(jié)果我們可以知道,變量 pthread_t t 保存的就是線程 id 的地址, 參數(shù) t 和線程 id 之間的關系如下所示:

在上面的代碼當中我們首先對 t 取地址,然后將其轉(zhuǎn)化為一個 long 類型的指針,然后解引用就可以得到對應地址的值了,也就是線程的ID。

深入理解參數(shù) arg

在下面的程序當中我們定義了一個結(jié)構體用于保存一些字符出的信息,然后創(chuàng)建一個這個結(jié)構體的對象,將這個對象的指針作為參數(shù)傳遞給線程要執(zhí)行的函數(shù),并且在線程內(nèi)部打印字符串當中的內(nèi)容。

#include?<stdio.h>
#include?<pthread.h>
#include?<malloc.h>
#include?<stdlib.h>
#include?<string.h>


typedef?struct?info?{
??char?s[1024];?//?保存字符信息
??int??size;????//?保存字符串的長度
}info_t;

static
void*?func(void*?arg)?{
??info_t*?in?=?(info_t*)arg;
??in->s[in->size]?=?'\0';
??printf("string?in?arg?=?%s\n",?in->s);
??return?NULL;
}

int?main()?{

??info_t*?in?=?malloc(sizeof(info_t));?//?申請內(nèi)存空間
??//?保存?HelloWorld?這個字符串?并且設置字符串的長度
??in->s[0]?=?'H';
??in->s[1]?=?'e';
??in->s[2]?=?'l';
??in->s[3]?=?'l';
??in->s[4]?=?'o';
??in->s[5]?=?'W';
??in->s[6]?=?'o';
??in->s[7]?=?'r';
??in->s[8]?=?'l';
??in->s[9]?=?'d';
??in->size?=?10;
??pthread_t?t;?????????//?將?in?作為參數(shù)傳遞給函數(shù)?func
??pthread_create(&t,?NULL,?func,?(void*)in);?
??pthread_join(t,?NULL);
??free(in);?//?釋放內(nèi)存空間
??return?0;
}

上面程序的執(zhí)行結(jié)果如下所示:

可以看到函數(shù)參數(shù)已經(jīng)做到了正確傳遞。

深入理解參數(shù) attr

在深入介紹參數(shù) attr 前,我們首先需要了解一下程序的內(nèi)存布局,在64位操作系統(tǒng)當中程序的虛擬內(nèi)存布局大致如下所示,從下往上依次為:只讀數(shù)據(jù)/代碼區(qū)、可讀可寫數(shù)據(jù)段、堆區(qū)、共享庫的映射區(qū)、程序棧區(qū)以及內(nèi)核內(nèi)存區(qū)域。我們程序執(zhí)行的區(qū)域就是在棧區(qū)。

根據(jù)上面的虛擬內(nèi)存布局示意圖,我們將其簡化一下得到單個線程的執(zhí)行流和大致的內(nèi)存布局如下所示(程序執(zhí)行的時候有他的棧幀以及寄存器現(xiàn)場,圖中將寄存器也做出了標識):

程序執(zhí)行的時候當我們進行函數(shù)調(diào)用的時候函數(shù)的棧幀就會從上往下生長,我們現(xiàn)在進行一下測試,看看程序的棧幀最大能夠達到多少。

#include?<stdio.h>
#include?<stdlib.h>
#include?<sys/types.h>
#include?<unistd.h>
int?times?=?1;

void*?func(void*?arg)?{
??char?s[1?<<?20];?//?申請?1MB?內(nèi)存空間(分配在??臻g上)
??printf("times?=?%d\n",?times);
??times++;
??func(NULL);
??return?NULL;
}

int?main()?{

??func(NULL);
??return?0;
}

上述程序的執(zhí)行結(jié)果如下圖所示:

從上面的程序我們可以看到在第 8 次申請棧內(nèi)存的時候遇到了段錯誤,因此可以判斷棧空間大小在 8MB 左右,事實上我們可以查看 linux 操作系統(tǒng)上,棧內(nèi)存的指定大?。?/p>

事實上在 linux 操作系統(tǒng)當中程序的??臻g的大小默認最大為 8 MB。

現(xiàn)在我們來測試一下,當我們創(chuàng)建一個線程的時候,線程的棧的大小大概是多少:

#include?<stdio.h>
#include?<pthread.h>
#include?<stdlib.h>
#include?<sys/types.h>
#include?<unistd.h>
int?times?=?1;

void*?func(void*?arg)?{
??printf("times?=?%d\n",?times);
??times++;
??char?s[1?<<?20];?//?申請?1MB?內(nèi)存空間(分配在??臻g上)
??func(NULL);
??return?NULL;
}

int?main()?{

??pthread_t?t;
??pthread_create(&t,?NULL,?func,?NULL);
??pthread_join(t,?NULL);
??return?0;
}

上面的程序執(zhí)行結(jié)果如下圖所示,可以看到當我們創(chuàng)建一個線程的時候棧的最大的大小也是 8MB。

設置線程??臻g的大小

現(xiàn)在如果我們有一個需求,需要的??臻g大于 8MB,我們應該怎么辦呢?這就是我們所需要談到的 attr,這個變量是一個 pthread_attr_t 對象,這個對象的主要作用就是用于設置線程的各種屬性的,其中就包括線程的棧的大小,在下面的程序當中我們將線程的??臻g的大小設置成 24MB,并且使用程序進行測試。

#include?<stdio.h>
#include?<pthread.h>
#include?<stdlib.h>

#define?MiB?*?1?<<?20

int?times?=?0;
void*?stack_overflow(void*?args)?{
??printf("times?=?%d\n",?++times);
??char?s[1?<<?20];?//?1?MiB
??stack_overflow(NULL);
??return?NULL;
}

int?main()?{
??pthread_attr_t?attr;
??pthread_attr_init(&attr);?//?對變量?attr?進行初始化操作
??pthread_attr_setstacksize(&attr,?24?MiB);?//?設置棧幀大小為?24?MiB?這里使用了一個小的?trick?大家可以看一下?MiB?的宏定義
??pthread_t?t;
??pthread_create(&t,?&attr,?stack_overflow,?NULL);
??pthread_join(t,?NULL);
??pthread_attr_destroy(&attr);?//?釋放線程屬性的相關資源
??return?0;
}

上面的程序執(zhí)行結(jié)果如下圖所示:

從上面程序的執(zhí)行結(jié)果來看我們設置的 24 MB 的??臻g大小起到了效果,我們可以通過線程的遞歸次數(shù)可以看出來我們確實申請了那么大的空間。在上面的程序當中我們對屬性的操作如下,這也是對屬性操作的一般流程:

  • 使用 pthread_attr_init 對屬性變量進行初始化操作。
  • 使用各種各樣的函數(shù)對屬性 attr 進行操作,比如 pthread_attr_setstacksize,這個函數(shù)的作用就是用于設置線程的??臻g的大小。
  • 使用 pthread_attr_destroy 釋放線程屬性相關的系統(tǒng)資源。

自己為線程的棧申請空間

在上一小節(jié)當中我們通過函數(shù) pthread_attr_setstacksize 給??臻g設置了新的大小,并且使用程序檢查驗證了新設置的??臻g大小,在這一小節(jié)當中我們將介紹使用我們自己申請的內(nèi)存空間也可以當作線程的棧使用。我們將使用兩種方法取驗證這一點:

  • 使用 malloc 函數(shù)申請內(nèi)存空間,這部分空間主要在堆當中。
  • 使用 mmap 系統(tǒng)調(diào)用在共享庫的映射區(qū)申請內(nèi)存空間。

使用 malloc 函數(shù)申請內(nèi)存空間

#include?<stdio.h>
#include?<pthread.h>
#include?<stdlib.h>

#define?MiB?*?1?<<?20

int?times?=?0;
static
void*?stack_overflow(void*?args)?{
??printf("times?=?%d\n",?++times);
??char?s[1?<<?20];?//?1?MiB
??stack_overflow(NULL);
??return?NULL;
}

int?main()?{
??pthread_attr_t?attr;
??pthread_attr_init(&attr);
??void*?stack?=?malloc(2?MiB);?//?使用?malloc?函數(shù)申請內(nèi)存空間?申請的空間大小為?2?MiB?
??pthread_t?t;
??pthread_attr_setstack(&attr,?stack,?2?MiB);?//?使用屬性設置函數(shù)設置棧的位置?棧的最低地址為?stack?棧的大小等于?2?MiB?
??pthread_create(&t,?&attr,?stack_overflow,?NULL);
??pthread_join(t,?NULL);
??pthread_attr_destroy(&attr);?//?釋放系統(tǒng)資源
??free(stack);?//?釋放堆空間
??return?0;
}

上述程序的執(zhí)行結(jié)果如下圖所示:

從上面的執(zhí)行結(jié)果可以看出來我們設置的??臻g的大小為 2MB 成功了。在上面的程序當中我們主要使用 pthread_attr_setstack 函數(shù)設置棧的低地址和??臻g的大小。我們申請的內(nèi)存空間內(nèi)存布局大致如下圖所示:

使用 mmap 申請內(nèi)存作為??臻g

#define?_GNU_SOURCE
#include?<stdio.h>
#include?<pthread.h>
#include?<stdlib.h>
#include?<sys/mman.h>

#define?MiB?*?1?<<?20
#define?STACK_SIZE?2?MiB

int?times?=?0;

static
void*?stack_overflow(void*?args)?{
??printf("times?=?%d\n",?++times);
??char?s[1?<<?20];?//?1?MiB
??stack_overflow(NULL);
??return?NULL;
}

int?main()?{
??pthread_attr_t?attr;
??pthread_attr_init(&attr);
??void*?stack?=?mmap(NULL,?STACK_SIZE,?PROT_READ?|?PROT_WRITE,
????????????????MAP_PRIVATE?|?MAP_ANONYMOUS?|?MAP_STACK,?-1,?0);
??if?(stack?==?MAP_FAILED)
??????perror("mapped?error:");
??pthread_t?t;
??pthread_attr_setstack(&attr,?stack,?STACK_SIZE);
??pthread_create(&t,?&attr,?stack_overflow,?NULL);
??pthread_join(t,?NULL);
??pthread_attr_destroy(&attr);
??free(stack);
??return?0;
}

在上面的程序當中我們使用 mmap 系統(tǒng)調(diào)用在共享庫空間申請了一段內(nèi)存空間,并且將其做為??臻g,我們在這里就不將程序執(zhí)行的結(jié)果放出來了,上面整個程序和前面的程序相差不大,只是在申請內(nèi)存方面發(fā)生了變化,總體的方向是不變的。

根據(jù)前面知識的學習,我們可以知道多個線程可以共享同一個進程虛擬地址空間,我們只需要給每個線程申請一個棧空間讓線程執(zhí)行起來就行,基于此我們可以知道多個線程的執(zhí)行流和大致的內(nèi)存布局如下圖所示:

在上圖當中不同的線程擁有不同的棧空間和每個線程自己的寄存器現(xiàn)場,正如上圖所示,??臻g可以是在堆區(qū)也可以是在共享庫的映射區(qū)域,只需要給線程提供??臻g即可。

深入理解線程的狀態(tài)

在 pthread 當中給我們提供了一個函數(shù) pthread_cancel 可以取消一個正在執(zhí)行的線程,取消正在執(zhí)行的線程之后會將線程的退出狀態(tài)(返回值)設置成宏定義 PTHREAD_CANCELED 。我們使用下面的例子去理解一下線程取消的過程:

#include?<stdio.h>
#include?<pthread.h>
#include?<assert.h>

void*?task(void*?arg)?{

?while(1)?{
????pthread_testcancel();?//?測試是否被取消執(zhí)行了
??}
??return?NULL;
}

int?main()?{

??void*?res;
??pthread_t?t;
??pthread_create(&t,?NULL,?task,?NULL);
??int?s?=?pthread_cancel(t);?//?取消函數(shù)的執(zhí)行
??if(s?!=?0)
????fprintf(stderr,?"cancel?failed\n");
??pthread_join(t,?&res);
??assert(res?==?PTHREAD_CANCELED);
??return?0;
}

在上面的程序當中我們在主線程當中使用函數(shù) pthread_cancel 函數(shù)取消線程的執(zhí)行,編譯執(zhí)行上面的程序是可以通過的,也就是說程序正確執(zhí)行了,而且 assert 也通過了。我們先不仔細去分析上面的代碼的執(zhí)行流和函數(shù)的意義。我們先需要了解一個線程的基本特性。

與線程取消執(zhí)行相關的一共有兩個屬性,分別是:

1.取消執(zhí)行的狀態(tài),線程的取消執(zhí)行的狀態(tài)一共有兩個:

  • PTHREAD_CANCEL_ENABLE:這個狀態(tài)表示這個線程是可以取消的,也是線程創(chuàng)建時候的默認狀態(tài)。
  • PTHREAD_CANCEL_DISABLE:這個狀態(tài)表示線程是不能夠取消的,如果有一個線程發(fā)送了一個取消請求,那么這個發(fā)送取消消息的線程將會被阻塞直到線程的取消狀態(tài)變成 PTHREAD_CANCEL_ENABLE 。

2.取消執(zhí)行的類型,取消線程執(zhí)行的類型也有兩種:

  • PTHREAD_CANCEL_DEFERRED:當一個線程的取消狀態(tài)是這個的時候,線程的取消就會被延遲執(zhí)行,知道線程調(diào)用一個是取消點的(cancellation point)函數(shù),比如 sleep 和 pthread_testcancel ,所有的線程的默認取消執(zhí)行的類型就是這個類型。
  • PTHREAD_CANCEL_ASYNCHRONOUS:如果線程使用的是這個取消類型那么線程可以在任何時候被取消執(zhí)行,當他接收到了一個取消信號的時候,馬上就會被取消執(zhí)行,事實上這個信號的實現(xiàn)是使用 tgkill 這個系統(tǒng)調(diào)用實現(xiàn)的。

事實上我們很少回去使用 PTHREAD_CANCEL_ASYNCHRONOUS ,因為這樣殺死一個線程會導致線程還有很多資源沒有釋放,會給系統(tǒng)帶來很大的災難,比如線程使用 malloc 申請的內(nèi)存空間沒有釋放,申請的鎖和信號量沒有釋放,尤其是鎖和信號量沒有釋放,很容易造成死鎖的現(xiàn)象。

有了以上的知識基礎我們現(xiàn)在可以來談一談前面的兩個函數(shù)了:

  • pthread_cancel(t) :是給線程 t 發(fā)送一個取消請求。
  • pthread_testcancel():這個函數(shù)是一個取消點,當執(zhí)行這個函數(shù)的時候,程序就會取消執(zhí)行。

現(xiàn)在我們使用默認的線程狀態(tài)和類型創(chuàng)建一個線程執(zhí)行死循環(huán),看看線程是否能夠被取消掉:

#include?<stdio.h>
#include?<pthread.h>
#include?<assert.h>
#include?<unistd.h>

void*?task(void*?arg)?{
??while(1)?{
????
??}
??return?NULL;
}

int?main()?{

??void*?res;
??pthread_t?t1;
??pthread_create(&t1,?NULL,?task,?NULL);
??int?s?=?pthread_cancel(t1);
??if(s?!=?0)?//?s?==?0?mean?call?successfully
????fprintf(stderr,?"cancel?failed\n");
??pthread_join(t1,?&res);
??assert(res?==?PTHREAD_CANCELED);
??return?0;
}

在上面的代碼當中我們啟動了一個線程不斷的去進行進行死循環(huán)的操作,程序的執(zhí)行結(jié)果為程序不會終止,因為主線程在等待線程的結(jié)束,但是線程在進行死循環(huán),而且線程執(zhí)行死循環(huán)的時候沒有調(diào)用一個是取消點的函數(shù),因此程序不會終止取消。

下面我們更改程序,將線程的取消類型設置為 PTHREAD_CANCEL_ASYNCHRONOUS ,在看看程序的執(zhí)行結(jié)果:

#include?<stdio.h>
#include?<pthread.h>
#include?<assert.h>
#include?<unistd.h>

void*?task(void*?arg)?{
??pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,?NULL);
??while(1)?{
????
??}
??return?NULL;
}

int?main()?{

??void*?res;
??pthread_t?t1;
??pthread_create(&t1,?NULL,?task,?NULL);
??int?s?=?pthread_cancel(t1);
??if(s?!=?0)?//?s?==?0?mean?call?successfully
????fprintf(stderr,?"cancel?failed\n");
??pthread_join(t1,?&res);
??assert(res?==?PTHREAD_CANCELED);
??return?0;
}

在上面的程序當中我們在線程執(zhí)行的函數(shù)當中使用 pthread_setcanceltype 將線程的取消類型設置成 PTHREAD_CANCEL_ASYNCHRONOUS 這樣的話就能夠在其他線程使用 pthread_cancel 的時候就能夠立即取消線程的執(zhí)行。

int?pthread_setcanceltype(int?type,?int?*oldtype)

上方是 pthread_setcanceltype 的函數(shù)簽名,在前面的使用當中我們只使用了第一個參數(shù),第二個參數(shù)我們是設置成 NULL,第二個參數(shù)我們可以傳入一個 int 類型的指針,然后會在將線程的取消類型設置成 type 之前將前一個 type 拷貝到 oldtype 所指向的內(nèi)存當中。

type: 有兩個參數(shù):PTHREAD_CANCEL_ASYNCHRONOUS 和 PTHREAD_CANCEL_DEFERRED 。

int?pthread_setcancelstate(int?state,?int?*oldstate);

設置取消狀態(tài)的函數(shù)簽名和上一個函數(shù)簽名差不多,參數(shù)的含義也是差不多,type 表示需要設置的取消狀態(tài),有兩個參數(shù):PTHREAD_CANCEL_ENABLE 和 PTHREAD_CANCEL_DISABLE ,參數(shù) oldstate 是指原來的線程的取消狀態(tài),如果你傳入一個 int 類型的指針的話就會將原來的狀態(tài)保存到指針指向的位置。

其實關于線程的一些細節(jié)還有比較多的內(nèi)容限于篇幅,在本篇文章當中主要給大家介紹這些細節(jié)。

關于棧大小程序的一個小疑惑

在上文當中我們使用了一個小程序去測試線程的??臻g的大小,并且打印函數(shù) func 的調(diào)用次數(shù),每一次調(diào)用的時候我們都會申請 1MB 大小的棧空間變量?,F(xiàn)在我們看下面兩個程序,在下面兩個程序只有 func 函數(shù)有區(qū)別,而在 func 函數(shù)當中主要的區(qū)別就是:

  • 在第一個程序當中是先申請內(nèi)存空間,然后再打印變量 times 的值。
  • 在第二個程序當中是先打印變量 times 的值,然后再申請內(nèi)存空間。
#include?<stdio.h>
#include?<pthread.h>
#include?<stdlib.h>
#include?<sys/types.h>
#include?<unistd.h>
int?times?=?1;

//?先申請內(nèi)存空間再打印
void*?func(void*?arg)?{
??char?s[1?<<?20];?//?申請?1MB?內(nèi)存空間(分配在??臻g上)
??printf("times?=?%d\n",?times);
??times++;
??func(NULL);
??return?NULL;
}

int?main()?{

??pthread_t?t;
??pthread_create(&t,?NULL,?func,?NULL);
??pthread_join(t,?NULL);
??return?0;
}
#include?<stdio.h>
#include?<pthread.h>
#include?<stdlib.h>
#include?<sys/types.h>
#include?<unistd.h>
int?times?=?1;

//?先打印再申請內(nèi)存空間
void*?func(void*?arg)?{
??printf("times?=?%d\n",?times);
??times++;
??char?s[1?<<?20];?//?申請?1MB?內(nèi)存空間(分配在棧空間上)
??func(NULL);
??return?NULL;
}

int?main()?{

??pthread_t?t;
??pthread_create(&t,?NULL,?func,?NULL);
??pthread_join(t,?NULL);
??return?0;
}

由于上面兩個程序的輸出結(jié)果是一樣的,所以我就只放出一個程序的輸出結(jié)果了:

但是不對呀!如果是后申請內(nèi)存空間的話,程序的輸出應該能夠打印 times = 8 啊,因為之前只申請了 7MB 的空間,我們打印 times = 8 的時候還沒有執(zhí)行到語句 char s[1 << 20]; ,那為什么也只打印到 7 呢?

出現(xiàn)上面問題的主要原因就需要看編譯器給我們編譯后的程序是如何申請內(nèi)存空間的。我們將上面的函數(shù) func 的匯編代碼展示出來:

00000000004005e0 <func>:
  4005e0:       55                      push   %rbp
  4005e1:       48 89 e5                mov    %rsp,%rbp
  4005e4:       48 81 ec 20 00 10 00    sub    $0x100020,%rsp
  4005eb:       48 8d 04 25 3c 07 40    lea    0x40073c,%rax
  4005f2:       00 
  4005f3:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  4005f7:       8b 34 25 40 10 60 00    mov    0x601040,%esi
  4005fe:       48 89 c7                mov    %rax,%rdi
  400601:       b0 00                   mov    $0x0,%al
  400603:       e8 c8 fe ff ff          callq  4004d0 <printf@plt>
  400608:       48 bf 00 00 00 00 00    movabs $0x0,%rdi
  40060f:       00 00 00 
  400612:       8b 34 25 40 10 60 00    mov    0x601040,%esi
  400619:       81 c6 01 00 00 00       add    $0x1,%esi
  40061f:       89 34 25 40 10 60 00    mov    %esi,0x601040
  400626:       89 85 ec ff ef ff       mov    %eax,-0x100014(%rbp)
  40062c:       e8 af ff ff ff          callq  4005e0 <func>
  400631:       48 bf 00 00 00 00 00    movabs $0x0,%rdi
  400638:       00 00 00 
  40063b:       48 89 85 e0 ff ef ff    mov    %rax,-0x100020(%rbp)
  400642:       48 89 f8                mov    %rdi,%rax
  400645:       48 81 c4 20 00 10 00    add    $0x100020,%rsp
  40064c:       5d                      pop    %rbp
  40064d:       c3                      retq

上面的匯編代碼是上面的程序在 x86_64 平臺下得到的,我們需要注意一行匯編指令 sub $0x100020,%rsp ,這條指令的主要作用就是將棧頂往下擴展(棧是從上往下生長的)1 MB 字節(jié)(實際上稍微比 1MB 大一點,因為還有其他操作需要一些??臻g),事實上就是給變量 s 申請 1MB 的??臻g。

好了,看到這里就破案了,原來編譯器申請棧空間的方式是將棧頂寄存器 rsp ,往虛擬地址空間往下移動,而編譯器在函數(shù)執(zhí)行剛開始的時候就申請了這么大的空間,因此不管是先申請空間再打印,還是先打印再申請空間,在程序被編譯成匯編指令之后,函數(shù) func 在函數(shù)剛開始就申請了對應的空間,因此才出現(xiàn)了都只打印到 times = 7 。

總結(jié)

在本篇文章當中主要給大家介紹了線程的基本元素和一些狀態(tài),還重點介紹了各種與線程相關屬性的函數(shù),主要使用的各種函數(shù)如下:

  • pthread_create,用與創(chuàng)建線程
  • pthread_attr_init,初始話線程的基本屬性。
  • pthread_attr_destroy,釋放屬性相關資源。
  • pthread_join,用于等待線程執(zhí)行完成。
  • pthread_attr_setstacksize,用于設置線程執(zhí)行棧的大小。
  • pthread_attr_setstack,設置線程執(zhí)行棧的棧頂和棧的大小。
  • pthread_testcancel,用于檢測線程是否被取消了,是一個取消點。
  • pthread_cancel,取消一個線程的執(zhí)行。

以上就是Pthread并發(fā)編程之線程基本元素和狀態(tài)的剖析的詳細內(nèi)容,更多關于Pthread線程元素 狀態(tài)的資料請關注腳本之家其它相關文章!

相關文章

  • C++ OpenCV實現(xiàn)像素畫的示例代碼

    C++ OpenCV實現(xiàn)像素畫的示例代碼

    這篇文章主要介紹了通過OpenCV進行圖片像素的變化,從而形成像素畫效果的功能。文中的示例代碼講解詳細,感興趣的小伙伴可以動手試一試
    2022-01-01
  • c++中數(shù)字與字符串之間的轉(zhuǎn)換方法(推薦)

    c++中數(shù)字與字符串之間的轉(zhuǎn)換方法(推薦)

    下面小編就為大家?guī)硪黄猚++中數(shù)字與字符串之間的轉(zhuǎn)換方法(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • VSCode斷點調(diào)試CMake工程項目的實現(xiàn)步驟

    VSCode斷點調(diào)試CMake工程項目的實現(xiàn)步驟

    這篇文章主要介紹了VSCode斷點調(diào)試CMake工程項目的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • C語言實現(xiàn)推箱子小游戲

    C語言實現(xiàn)推箱子小游戲

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)推箱子小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • C++?自定義單向鏈表?ListNode詳情

    C++?自定義單向鏈表?ListNode詳情

    這篇文章主要介紹了C++?自定義單向鏈表?ListNode詳情,文章將介紹鏈表中不帶頭結(jié)點,沒有存放鏈表長度的節(jié)點,從頭結(jié)點開始就存放數(shù)據(jù)得一種,具有一定得參考價值,需要的小伙伴可以參考一下
    2022-03-03
  • 淺析C++的特殊工具與技術

    淺析C++的特殊工具與技術

    以下是對C++中的特殊工具與技術進行了詳細的分析介紹,需要的朋友可以過來參考下
    2013-08-08
  • 帶你了解C++初階之引用

    帶你了解C++初階之引用

    這篇文章主要為大家介紹了C++初階之引用,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 總結(jié)c++性能優(yōu)化策略

    總結(jié)c++性能優(yōu)化策略

    在本篇文章中小編給大家總結(jié)了關于C++的性能優(yōu)化策略的相關知識點,對此有興趣的朋友可以參考學習下。
    2018-03-03
  • C++11 Unicode編碼轉(zhuǎn)換

    C++11 Unicode編碼轉(zhuǎn)換

    這篇文章主要介紹了C++11 Unicode編碼轉(zhuǎn)換的相關資料,幫助大家更好的理解和學習c++11,感興趣的朋友可以了解下
    2020-08-08
  • c++難以發(fā)現(xiàn)的bug(有趣)

    c++難以發(fā)現(xiàn)的bug(有趣)

    這篇文章主要介紹了c++難以發(fā)現(xiàn)的bug(有趣)的相關資料,需要的朋友可以參考下
    2017-10-10

最新評論

隆昌县| 巩留县| 韶山市| 芦山县| 平湖市| 曲松县| 翼城县| 大丰市| 琼结县| 湘阴县| 九江县| 温州市| 抚远县| 静海县| 离岛区| 凤山市| 遂宁市| 始兴县| 云浮市| 神农架林区| 兴文县| 临沧市| 潮安县| 寿宁县| 浦东新区| 内乡县| 即墨市| 信宜市| 东平县| 栾川县| 新泰市| 沈阳市| 昌宁县| 神农架林区| 郑州市| 乌拉特中旗| 观塘区| 竹溪县| 额尔古纳市| 皋兰县| 吉安县|