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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

大内高手—共享内存与线程局部存储

發(fā)布時間:2024/1/17 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大内高手—共享内存与线程局部存储 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

城里的人想出去,城外的人想進來。這是《圍城》里的一句話,它可能比《圍城》本身更加有名。我想這句話的前提是,要么住在城里,要么住在城外,二者只能居其一。否則想住在城里就可以住在城里,想住在城外就可以住在城外,你大可以選擇單日住在城里,雙日住在城外,也就沒有心思去想出去還是進來了。

?

理想情況是即可以住在城里又可以住在城外,而不是走向極端。盡管像青蛙一樣的兩棲動物絕不會比人類更高級,但能適應于更多環(huán)境的能力畢竟有它的優(yōu)勢。技術也是如此,共享內(nèi)存和線程局部存儲就是實例,它們是為了防止走向內(nèi)存完全隔離和完全共享兩個極端的產(chǎn)物。

?

當我們發(fā)明了MMU時,大家認為天下太平了,各個進程空間獨立,互不影響,程序的穩(wěn)定性將大提高。但馬上又認識到,進程完全隔離也不行,因為各個進程之間需要信息共享。于是就搞出一種稱為共享內(nèi)存的東西。

?

當我們發(fā)明了線程的時,大家認為這下可爽了,線程可以并發(fā)執(zhí)行,創(chuàng)建和切換的開銷相對進程來說小多了。線程之間的內(nèi)存是共享的,線程間通信快捷又方便。但馬上又認識到,有些信息還是不共享為好,應該讓各個線程保留一點隱私。于是就搞出一個線程局部存儲的玩意兒。

?

共享內(nèi)存和線程局部存儲是兩個重要又不常用的東西,平時很少用,但有時候又離不了它們。本文介紹將兩者的概念、原理和使用方法,把它們放在自己的工具箱里,以供不時之需。

?

1.?????????共享內(nèi)存

大家都知道進程空間是獨立的,它們之間互不影響。比如同是0xabcd1234地址的內(nèi)存,在不同的進程中,它們的數(shù)據(jù)是不同的,沒有關系的。這樣做的好處很多:每個進程的地址空間變大了,它們獨占4G(32)的地址空間,讓編程實現(xiàn)更容易。各個進程空間獨立,一個進程死掉了,不會影響其它進程,提高了系統(tǒng)的穩(wěn)定性。

?

要做到進程空間獨立,光靠軟件是難以實現(xiàn)的,通常要依賴于硬件的幫助。這種硬件通常稱為MMU(Memory Manage Unit),即所謂的內(nèi)存管理單元。在這種體系結構下,內(nèi)存分為物理內(nèi)存和虛擬內(nèi)存兩種。物理內(nèi)存就是實際的內(nèi)存,你機器上裝了多大內(nèi)存就有多大內(nèi)存。而應用程序中使用的是虛擬內(nèi)存,訪問內(nèi)存數(shù)據(jù)時,由MMU根據(jù)頁表把虛擬內(nèi)存地址轉換對應的物理內(nèi)存地址。

?

MMU把各個進程的虛擬內(nèi)存映射到不同的物理內(nèi)存上,這樣就保證了進程的虛擬內(nèi)存是獨立的。然而,物理內(nèi)存往往遠遠少于各個進程的虛擬內(nèi)存的總和。怎么辦呢,通常的辦法是把暫時不用的內(nèi)存寫到磁盤上去,要用的時候再加載回內(nèi)存中來。一般會搞一個專門的分區(qū)保存內(nèi)存數(shù)據(jù),這就是所謂的交換分區(qū)。

?

這些工作由內(nèi)核配合MMU硬件完成,內(nèi)存管理是操作系統(tǒng)內(nèi)核的重要功能。其中為了優(yōu)化性能,使用了不少高級技術,所以內(nèi)存管理通常比較復雜。比如:在決定把什么數(shù)據(jù)換出到磁盤上時,采用最近最少使用的策略,把常用的內(nèi)存數(shù)據(jù)放在物理內(nèi)存中,把不常用的寫到磁盤上,這種策略的假設是最近最少使用的內(nèi)存在將來也很少使用。在創(chuàng)建進程時使用COW(Copy on Write)的技術,大大減少了內(nèi)存數(shù)據(jù)的復制。為了提高從虛擬地址到物理地址的轉換速度,硬件通常采用TLB技術,把剛轉換的地址存在cache里,下次可以直接使用。

?

從虛擬內(nèi)存到物理內(nèi)存的映射并不是一個字節(jié)一個字節(jié)映射的,而是以一個稱為頁(page)最小單位的為基礎的,頁的大小視硬件平臺而定,通常是4K。當應用程序訪問的內(nèi)存所在頁面不在物理內(nèi)存中時,MMU產(chǎn)生一個缺頁中斷,并掛起當前進程,缺頁中斷負責把相應的數(shù)據(jù)從磁盤讀入內(nèi)存中,再喚醒掛起的進程。

?

進程的虛擬內(nèi)存與物理內(nèi)存映射關系如下圖所示(灰色頁為被不在物理內(nèi)存中的頁):

也許我們很少直接使用共享內(nèi)存,實際上除非性能上有特殊要求,我更愿意采用socket或者管道作為進程間通信的方式。但我們常常間接的使用共享內(nèi)存,大家都知道共享庫(或稱為動態(tài)庫)的優(yōu)點是,多個應用程序可以公用。如果每個應用程序都加載一份共享庫到內(nèi)存中,顯然太浪費了。所以操作系統(tǒng)把共享庫放在共享內(nèi)存中,讓多個應用程序共享。另外,同一個應用程序運行多個實例時,也采用同樣的方式,保證內(nèi)存中只有一份可執(zhí)行代碼。這樣的共享內(nèi)存是設為只讀屬性的,防止應用程序無意中破壞它們。當調試器要設置斷點時,相應的頁面被拷貝一分,設置為可寫的,再向其中寫入斷點指令。這些事情完全由操作系統(tǒng)等底層軟件處理了,應用程序本身無需關心。

?

共享內(nèi)存是怎么實現(xiàn)的呢?我們來看看下圖(黃色頁為共享內(nèi)存)

?

由上圖可見,實現(xiàn)共享內(nèi)存非常容易,只是把兩個進程的虛擬內(nèi)存映射同一塊物理內(nèi)存就行了。不過要注意,物理內(nèi)存相同而虛擬地址卻不一定相同,如圖中所示進程1page5和進程2page2都映射到物理內(nèi)存的page1上。

?

如何在程序中使用共享內(nèi)存呢?通常很簡單,操作系統(tǒng)或者函數(shù)庫提供了一些API給我們使用。如:

?

Linux:

void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);

int munmap(void *start, size_t length);

?

Win32:

HANDLE CreateFileMapping( ? HANDLE hFile,?????? ????????????????// handle to file ? LPSECURITY_ATTRIBUTES lpAttributes, // security ? DWORD flProtect,??????????????????? // protection ? DWORD dwMaximumSizeHigh,??????????? // high-order DWORD of size ? DWORD dwMaximumSizeLow,???????????? // low-order DWORD of size ? LPCTSTR lpName????????????????????? // object name ); BOOL UnmapViewOfFile( ? LPCVOID lpBaseAddress?? // starting address );

?

2.?????????線程局部存儲(TLS)

同一個進程中的多個線程,它們的內(nèi)存空間是共享的(棧除外),在一個線程修改的內(nèi)存內(nèi)容,對所有線程都生效。這是一個優(yōu)點也是一個缺點。說它是優(yōu)點,線程的數(shù)據(jù)交換變得非??旖?。說它是缺點,一個線程死掉了,其它線程也性命不保;?多個線程訪問共享數(shù)據(jù),需要昂貴的同步開銷,也容易造成同步相關的BUG;

?

unix下,大家一直都對線程不是很感興趣,直到很晚以后才引入線程這東西。像X Sever要同時處理N個客戶端的連接,每秒鐘要響應上百萬個請求,開發(fā)人員寧愿自己實現(xiàn)調度機制也不用線程。讓人很難想象X Server是單進程單線程模型的。再如Apache(1.3x),在unix下的實現(xiàn)也是采用多進程模型的,把像記分板等公共信息放入共享內(nèi)存中,也不愿意采用多線程模型。

?

正如《unix編程藝術》中所說,線程局部存儲的出現(xiàn),使得這種情況出現(xiàn)了轉機。采用線程局部存儲,每個線程有一定的私有空間。這可以避免部分無意的破壞,不過仍然無法避免有意的破壞行為。

?

個人認為,這完全是因為unix程序不喜歡面向對象方法引起的,數(shù)據(jù)沒有很好的封裝起來,全局變量滿天飛,在多線程情況下自然容易出問題。如果采用面向對象的方法,可以讓這種情況大為改觀,而無需要線程局部存儲來幫忙。

?

當然,多一種技術就多一種選擇,知道線程局部存儲還是有用的。盡管只用過幾次線程局部存儲的方法,在那種情況下,沒有線程局部存儲,確實很難用其它辦法實現(xiàn)。

?

線程局部存儲在不同的平臺有不同的實現(xiàn),可移植性不太好。幸好要實現(xiàn)線程局部存儲并不難,最簡單的辦法就是建立一個全局表,通過當前線程ID去查詢相應的數(shù)據(jù),因為各個線程的ID不同,查到的數(shù)據(jù)自然也不同了。

?

大多數(shù)平臺都提供了線程局部存儲的方法,無需要我們自己去實現(xiàn):

?

linux:

方法一:

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

int pthread_key_delete(pthread_key_t key);

void *pthread_getspecific(pthread_key_t key);

int pthread_setspecific(pthread_key_t key, const void *value);

方法二:

__thread int i;

Win32

方法一:

DWORD TlsAlloc(VOID);

BOOL TlsFree(

??DWORD dwTlsIndex???// TLS index

);

BOOL TlsSetValue(

??DWORD dwTlsIndex,??// TLS index

??LPVOID lpTlsValue??// value to store

);

LPVOID TlsGetValue(

??DWORD dwTlsIndex???// TLS index

);

方法二:

__declspec( thread ) int tls_i = 1;

?

~~end~~

?

轉載時請注明出處和作者聯(lián)系方式:http://blog.csdn.net/absurd

作者聯(lián)系方式:李先靜?<xianjimli at hotmail dot com>

更新時間:2007-7-9

轉載于:https://www.cnblogs.com/sdphome/archive/2011/05/08/2040477.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結

以上是生活随笔為你收集整理的大内高手—共享内存与线程局部存储的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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