多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > 淺析Linux線程中數據

淺析Linux線程中數據

來源:程序員人生   發布時間:2014-12-25 08:08:39 閱讀次數:2542次

  本文首先概述了線程中有哪些數據私有的,和進程中哪些數據線程是同享的,然后詳細分析了線程在用戶空間中的數據,最后通過1個多線程程序來分析線程中的數據散布。

    概述

   線程包括了表示進程內履行環境必須的信息其中包括進程中標識的線程ID、1組寄存器值、棧、調度優先級和策略、信號屏蔽字(每一個線程有自己的信號屏蔽字,但對某個信號的處理方式是進程中所有線程同享的)、errno變量(每一個線程都自己的局部errno)和線程私有數據。進程的所有信息對該進程的所有線程都是同享的,包括可履行的程敘文本、程序的全局內存和堆內存和文件描寫符。原則上線程的私有數據其實不是真的私有,由于線程的特點就是同享地址空間,只是線程的私有空間就是1般而言通過正常手段不會觸及其它線程空間的數據而已,如果通過非正常途徑固然也是可以訪問的。在Linux中,默許情況下,創建1個線程,在用戶空間中分配的空間大小為8M,另外還有4K作為線程警戒區,內核中還會分配1個task_struct結構用于相應的線程。

   線程用戶空間中數據

   Linux線程由Linux內核、glibclibpthread這3種共同支持實現。在用戶空間,和線程關系本身比較密切的,有下面3個部份:

   1是類似于線程控制塊(TCB)的數據結構,在用戶空間中代表著1個線程的存在;

   2是線程私有堆棧;

   3是線程局部數據(TLS);

   在多線程程序中,各個線程之間的大部份數據都是同享的,但上面3部份數據是各個線程獨有的。這3種數據位于同1塊內存中,是在創建線程的時候,用mmap系統調用分配出來的,要訪問這塊地址,需要通過gs寄存器,對同1個進程內的每個線程,gs寄存器指向的地址都是不1樣的,這樣可以保證各個線程之間不會相互干擾。這3塊數據在內存散布上,大致以下:

-----------

pthread

-----------

TLS

-----------

Stack

-----------

   上面說到,這塊內存通過gs寄存器訪問,那末gs寄存器指向這塊地址的哪一個地方呢?是指向pthread結構的首地址。在調用pthread創建線程時,會調用mmap來為線程分配空間,但在mmap之前,會嘗試在進程中查找有無現成的可用空間,這是由于在通常情況下,我們創建了1個線程,當線程運行完后退出時,其占用的空間并沒有釋放,所以如果A線程退出后,我們又需要創建1個新線程B,那末我們就能夠看看A線程的堆棧空間是不是滿足要求,滿足要求的話我們就直接用了。為了線程退出時,釋放其所占用的空間,有兩種方法,1種是在主線程中調用pthread_join;另外一種方法是線程創建時指定detach屬性或創建后在新的線程中調用pthread_detach(pthread_self()),使得線程退出時,自動釋放所占用的資源。注意這里釋放的只是內核空間中所占用的資源(比如task_struct),而在用戶空間中,線程所占用的資源(即在堆上用mmap分配的空間)依然是沒有釋放的。下面來看這3個部份分別包括甚么數據:

   phtread部份保存的是1個類型為pthread的結構體,該結構體包括該線程控制塊(Thread Control Block)字段、mutex相干字段、cleanup線程退出的善后工作相干字段、cancelhandling線程取消相干字段、援用線程私有數據相干字段、start_routine入口函數相干字段和寄存線程start_routine的返回值相干字段等線程屬性相干信息。Linux中,返回的線程id就是這個pthread結構的地址,也就是gs寄存器中的值。

   TLS是值線程本地存儲,它主要保存了自定義_thread修飾符修飾的變量;1些庫級別預定義的變量,比如errno;線程的私有數據實質也存在這部份。

   Stack就是線程履行運行時所用的棧,比如線程中的局部變量就在這部份。下面通過1個多線程程序例子來看線程中數據散布,代碼以下:

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <sys/syscall.h> #include <assert.h> #define gettid() syscall(__NR_gettid) /*get LWP ID */ pthread_key_t key; __thread int count = 42; __thread unsigned long long count2 ; static __thread int count3; /*thread key destructor*/ void keydestr(void* string) { printf("destructor excuted in thread %p,address (%p) param=%s ",pthread_self(),string,string); free(string); } void * thread1(void *arg) { int b; pthread_t tid=pthread_self(); size_t size = 8; int autovar = 0; static staticvar = 1; printf("In thread1, autovaraddress = %p, staticvaraddress = %p ", &autovar, &staticvar); printf("In thread1, tid = %p, gettid = %d ",tid,gettid()); char* key_content = ( char* )malloc(size); if(key_content != NULL) { strcpy(key_content,"maximus0"); } pthread_setspecific(key,(void *)key_content); count = 1024; count2 = 2048; count3 = 4096; printf("In thread1, tid=%p, count(%p) = %8d, count2(%p) = %6llu, count3(%p) = %6d ",tid,&count,count,&count2,count2,&count3,count3); sleep(2); printf("thread1 %p keyselfaddress = %p, returns keyaddress = %p ",tid,&key, pthread_getspecific(key)); sleep(30); printf("thread1 exit "); } void * thread2(void *arg) { int b; pthread_t tid=pthread_self(); size_t size = 8; int autovar = 0; static staticvar = 1; printf("In thread2, autovaraddress = %p, staticvaraddress = %p ", &autovar, &staticvar); printf("In thread2, tid = %p, gettid = %d ",tid,gettid()); char* key_content = ( char* )malloc(size); if(key_content != NULL) { strcpy(key_content,"ABCDEFG"); } pthread_setspecific(key,(void *)key_content); count = 1025; count2 = 2049; count3 = 4097; printf("In thread2, tid=%p, count(%p) = %8d, count2(%p) = %6llu, count3(%p) = %6d ",tid,&count,count,&count2,count2,&count3,count3); sleep(1); printf("thread2 %p keyselfaddress = %p, returns keyaddress = %p ",tid,&key, pthread_getspecific(key)); sleep(50); printf("thread2 exit "); } int main(void) { int b; int autovar = 0; static staticvar = 1; pthread_t tid1,tid2; printf("start,pid=%d ",getpid()); printf("In main, autovaraddress = %p, staticvaraddress = %p ", &autovar, &staticvar); pthread_key_create(&key,keydestr); pthread_create(&tid1,NULL,thread1,NULL); pthread_create(&tid2,NULL,thread2,NULL); printf("In main, pthread_create tid1 = %p ",tid1); printf("In main, pthread_create tid2 = %p ",tid2); if(pthread_join(tid2,NULL) == 0) { printf("In main,pthread_join thread2 success! "); sleep(5); } pthread_key_delete(key); printf("main thread exit "); return 0; }

編譯并運行程序,結果以下:

$gcc -Wall -lpthread -o hack_thread_data hack_thread_data.c $./hack_thread_data start,pid=52168 In main, autovaraddress = 0x7fffd3eceea8, staticvaraddress = 0x601650 In main, pthread_create tid1 = 0x7ffe4baee700 In main, pthread_create tid2 = 0x7ffe4b2ed700 In thread2, autovaraddress = 0x7ffe4b2eceb0, staticvaraddress = 0x601654 In thread2, tid = 0x7ffe4b2ed700, gettid = 52170 In thread2, tid=0x7ffe4b2ed700, count(0x7ffe4b2ed6e8) = 1025, count2(0x7ffe4b2ed6f0) = 2049, count3(0x7ffe4b2ed6f8) = 4097 In thread1, autovaraddress = 0x7ffe4baedeb0, staticvaraddress = 0x601658 In thread1, tid = 0x7ffe4baee700, gettid = 52169 In thread1, tid=0x7ffe4baee700, count(0x7ffe4baee6e8) = 1024, count2(0x7ffe4baee6f0) = 2048, count3(0x7ffe4baee6f8) = 4096 thread2 0x7ffe4b2ed700 returns keyaddress = 0x1598270 thread1 0x7ffe4baee700 returns keyaddress = 0x1598290 thread1 exit destructor excuted in thread 0x7ffe4baee700,address (0x1598290) param=maximus0 thread2 exit destructor excuted in thread 0x7ffe4b2ed700,address (0x1598270) param=ABCDEFG In main,pthread_join thread2 success! main thread exit

在線程1結束之前、線程1結束以后、線程2結束之前和線程結束以后,使用命令cat /proc/52168/maps都得到進程的地址空間都是以下(這說明線程退出后(即便detach這個線程),線程在用戶空間所占用的空間其實不會釋放):


   從地址空間可以看出:

   1)創建的線程和主線程在地址空間的位置。線程中的局部變量都在相應線程的棧空間,而線程的靜態變量都是在.data節,線程動態分配的空間都是在堆(heap)上。

   2)__thread聲明的變量每個線程有1份獨立實體,各個線程的值互不干擾。可以用來修飾那些帶有全局性且值可能變,但是又不值得用全局變量保護的變量。

   3)通過在線程調用syscall(__NR_gettid),可以取得每一個線程在內核中對應的進程ID。如果直接在線程中調用getpid,則實質取得的是該線程所在的線程組tgid。線程在內核中對應的進程ID,也能夠通過命令ps axj -L來查看,其當選項-L會增加顯示LWP列,即線程對應的實質PID,或使用命令top,然后按下H(注意是大寫)鍵,也是顯示所有的線程。

   4)線程id實質是線程棧中的某個地址。

   5)線程在用戶空間分兩部份,權限---p部份的空間實質上是線程的警容緩沖區,這個緩沖區的大小,可以在創建線程時通過修改線程屬性guardsize的值來指定,這個值控制著線程棧末尾以后用以免棧溢出的擴大內存大小,這個值默許值為PAGESIZE字節。當前線程的棧空間的大小也能夠在創建線程時指定。默許線程棧空間的大小為8M,比如thread2就是7ffe4aaee000⑺ffe4b2ee000,即大小為8M,警容緩沖區默許大小為4KB,比如thread2就是7ffe4aaed000⑺ffe4aaee000

  6)在某個線程中使用sleep,只會讓當前線程阻塞,不會影響其他線程。

參考資料

http://blog.csdn.net/liuxuejiang158blog/article/details/14100897
http://www.linuxsir.org/bbs/thread317267.html
http://javadino.blog.sohu.com/74292914.html
http://blog.chinaunix.net/uid⑵4774106-id⑶650136.html
http://blog.chinaunix.net/uid⑵4774106-id⑶651266.html
http://blog.csdn.net/dog250/article/details/7704898
http://www.longene.org/forum/viewtopic.php?f=17&t=414
http://www.longene.org/forum/viewtopic.php?f=17&t=429
http://www.longene.org/forum/viewtopic.php?f=17&t=441
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 天堂网在线网站成人午夜网站 | 亚洲第二区 | a4yy私人毛片在线 | 精品欧美一区二区在线看片 | 三级大片网站 | 亚洲成a人片在线v观看 | 久久成人免费 | 那一个欧美一级毛片 | 中文精品视频一区二区在线观看 | 国产一级在线观看视频 | 亚洲欧美中文字幕高清在线一 | 国产美女网站视频 | 牛和人交vvideos欧美 | 亚洲精品视频在线观看视频 | 亚洲精品视频免费观看 | 国产一区二区免费在线 | 色午夜影院 | 成人性生活免费看 | 最近免费中文字幕大全高清mv | 国产高清免费不卡观看 | 一区二区三区四区国产 | 波多野结衣不卡 | 久久生活片 | 欧美 日本| 欧美日韩国产综合在线小说 | 欧美一级一毛片 | 在线欧洲成人免费视频 | 91久久精品国产一区二区 | 久久综合精品国产一区二区三区 | 精品欧美成人高清在线观看2021 | 在线播放 亚洲 | 日本v在线 | 亚洲另类春色校园小说 | 国内精品免费一区二区三区 | 禁视频网站在线观看漫画 | 日本网站免费 | 欧美一级做一a做片性视频 欧美一级做一级爱a做片性 | 中文字幕中韩乱码亚洲大片 | 国产亚洲精品九九久在线观看 | 亚洲欧美日韩精品久久亚洲区色播 | 亚洲欧美专区精品久久 |