Linux共享內(nèi)存實例及文件映射編程及實現(xiàn)原理_第1頁
Linux共享內(nèi)存實例及文件映射編程及實現(xiàn)原理_第2頁
Linux共享內(nèi)存實例及文件映射編程及實現(xiàn)原理_第3頁
Linux共享內(nèi)存實例及文件映射編程及實現(xiàn)原理_第4頁
Linux共享內(nèi)存實例及文件映射編程及實現(xiàn)原理_第5頁
已閱讀5頁,還剩6頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、目錄(一)IPC共享內(nèi)存和文件映射的區(qū)別1(二)共享內(nèi)存實現(xiàn)流程總結1(三)存儲映射I/O(包含實現(xiàn)原理說明)2文件映射API補充4(四)IPC共享存儲(包含實現(xiàn)原理說明)6(五)共享內(nèi)存實現(xiàn)基本原理10(六)IPC共享內(nèi)存實現(xiàn)機制11(七)文件映射的實現(xiàn)機制13(一)IPC共享內(nèi)存和文件映射的區(qū)別1. 文件映射的頁框是磁盤文件高速緩存中的頁框,內(nèi)核線程pdflush會將頁框中的內(nèi)容回寫進磁盤, 如果是私有映射類型,將會進行寫時復制。而IPC共享內(nèi)存映射的是一種特殊文件系統(tǒng)中的文件高速緩存,它沒有相應的磁盤映像。2. IPC共享內(nèi)存只存在于內(nèi)存中,系統(tǒng)重新啟動,數(shù)據(jù)將會丟失。而文件共享映射會將

2、數(shù)據(jù)寫回磁盤。3. IPC共享內(nèi)存的大小是在創(chuàng)建的時候指定,而且大小不能改變,而文件在創(chuàng)建時大小為0,此時還不能建立映射,文件的大小會間接的決定映射區(qū)的大小。例如文件的大小是123,而要求映射的區(qū)域大小是4096*2,但實際只會分配4096的映射空間,此時引用4096以后的線性空間將引起缺頁異常。4. 當?shù)谝淮巫x取共享內(nèi)存時IPC共享內(nèi)存對象將分配一個新的頁框,而文件映射分配新頁框的同時會將磁盤中的數(shù)據(jù)寫入新頁框。5. IPC共享內(nèi)存不需要寫回磁盤操作,完全是為共享內(nèi)存而設計,所以使用效率會更高。6. IPC共享內(nèi)存對象必須調用shmctl()顯示的撤銷,否則會一直保留著,使用key或者id號

3、定位一個共享內(nèi)存對象,key和id號的對應關系并不是固定的。例如,第一次使用key建立一個共享內(nèi)存對象為shm1對應的id為id1,之后系統(tǒng)重新啟動,然后再使用key建立一個共享內(nèi)存對象shm2,對應的id是id2,此時id2和id1是不同的。而文件映射使用相同的路徑將會定位相同的磁盤文件。總結:IPC共享內(nèi)存和文件映射的實現(xiàn)機制是一樣的,文件映射的目的是加快對文件的讀寫速度,而IPC共享內(nèi)存就是為了共享內(nèi)存而設計的,所以效率會高一些。(二)共享內(nèi)存實現(xiàn)流程總結1. 建立一個線性區(qū)對象struct vm_area_struct 并加入進程的內(nèi)存描述符current-mm中。函數(shù)mmap()和s

4、hmat()就是用于建立并注冊線性區(qū)對象,這個對象中的struct file *vm_file指向映射文件的文件對象,vm_page_prot是線性區(qū)中頁框的訪問許可權。但此時并未修改進程的頁表,而是注冊相應的缺頁異?;卣{函數(shù),注冊在對象的vm_ops。2. 當進程第一次訪問共享內(nèi)存區(qū)時,由于相應的頁表還未填寫,將產(chǎn)生缺頁異常,并根據(jù)線性地址找到對應的線性區(qū)對象,然后調用前邊注冊過的缺頁異?;卣{函數(shù),并根據(jù)vm_file文件對象和vm_page_prot的信息來填寫相應的頁表項,最后重新執(zhí)行產(chǎn)生缺頁異常的代碼。說明:文件映射和IPC共享內(nèi)存映射的物理頁框都是磁盤文件的頁高速緩存中的,IPC共享

5、內(nèi)存使用一種特殊文件系統(tǒng),這個文件系統(tǒng)并沒有對應的磁盤映像,只是復用了文件系統(tǒng)的框架。更詳細的內(nèi)容參見后邊的五,六,七節(jié)。下面3,4節(jié)是UNIX環(huán)境高級編程對文件映射和IPC共享內(nèi)存的講解,已經(jīng)說明的很詳細了,我在它的基礎上附加了一些內(nèi)核實現(xiàn)原理的說明,實現(xiàn)原理說明部分放在括號內(nèi)。(三)存儲映射I/O(包含實現(xiàn)原理說明)存儲映射I/O使一個磁盤文件與存儲空間中的一個緩存相映射。于是當從緩存中取數(shù)據(jù),就相當于讀文件中的相應字節(jié)。與其類似,將數(shù)據(jù)存入緩存,則相應字節(jié)就自動地寫入文件。這樣,就可以在不使用read和write的情況下執(zhí)行I/O。為了使用這種功能,應首先告訴內(nèi)核將一個給定的文件映射到一

6、個存儲區(qū)域中。這是由mmap函數(shù)實現(xiàn)的。#include #include void * mmap(void addr, size_t len, int prot, int flag, int fd, off_t off) ;返回:若成功則為映射區(qū)的起始地址,若出錯則為- 1addr參數(shù)用于指定映射存儲區(qū)的起始地址。通常將其設置為0,這表示由系統(tǒng)選擇該映射區(qū)的起始地址。此函數(shù)的返回地址是:該映射區(qū)的起始地址。fd指定要被映射文件的描述符(fd用于定位是哪個磁盤文件的頁高速緩存)。在映射該文件到一個地址空間之前,先要打開該文件。len是映射的字節(jié)數(shù)。off是要映射字節(jié)在文件中的起始位移量(下面將

7、說明對off值有某些限制)。在說明其余參數(shù)之前,先看一下存儲映射文件的基本情況。圖12 - 12顯示了一個存儲映射文件。在此圖中,“起始地址”是mmap的返回值。在圖中,映射存儲區(qū)位于堆和棧之間:這屬于實現(xiàn)細節(jié),各種實現(xiàn)之間可能不同。prot參數(shù)說明映射存儲區(qū)的保護要求。見表12 - 8。對于映射存儲區(qū)所指定的保護要求與文件的open方法匹配。例如,若該文件是只讀打開的,那么對映射存儲區(qū)就不能指定PROT _WRITE。(對存儲映射區(qū)的保護是通過設置頁表項的保護標志來實現(xiàn)的,如果頁表項的read/write標志位為0,說明頁是只讀的,如果進程試圖修改頁的內(nèi)容,將產(chǎn)生段錯誤,這些保護方案都是由C

8、PU硬件控制的)flag參數(shù)影響映射存儲區(qū)的多種屬性:? MAP_FIXED 返回值必須等于addr。因為這不利于可移植性,所以不鼓勵使用此標志。如果未指定此標志,而且addr非0,則內(nèi)核只把addr視為何處設置映射區(qū)的一種建議。通過將addr指定為0可獲得最大可移植性。? MAP_SHARED 這一標志說明了本進程對映射區(qū)所進行的存儲操作的配置。此標志指定存儲操作修改映射文件也就是,存儲操作相當于對該文件write。(這里映射的頁是包含在文件的頁高速緩存中,用戶態(tài)進程在讀寫磁盤的時候,內(nèi)核先在頁高速緩存中增加一個新頁,將所請求的磁盤塊寫入新頁,用戶態(tài)進程從頁高速緩存中取出數(shù)據(jù)。如果要寫入數(shù)據(jù)

9、,也是要添加一個頁將磁盤中的數(shù)據(jù)寫入該頁,然后再將數(shù)據(jù)寫入該頁,內(nèi)核會在一定的時機對磁盤進行更新。)(以上的頁高速緩存是組織在inode的i_mmaping對象中,對于一個磁盤文件唯一對應一個磁盤inode,每個磁盤inode也唯一對應一個內(nèi)核inode,也就是,每個磁盤文件只有一個頁高速緩存,如果兩個進程映射的是同一個文件的頁高數(shù)緩存,則它們共享相同的物理頁)? MAP_PRIVATE 本標志說明,對映射區(qū)的存儲操作導致創(chuàng)建該映射文件的一個副本。所有后來對該映射區(qū)的存訪都是存訪該副本,而不是原始文件。(這里內(nèi)核用到了寫時復制技術,在相應的頁表項中設置寫時復制標志,當進程試圖修改該頁,內(nèi)核將會

10、產(chǎn)生缺頁異常,內(nèi)核就把該頁框進行復制,并在進程頁表中用復制的頁來替換原來的頁框,顯然這個新的頁框已經(jīng)不在頁高速緩存中了,對頁框的內(nèi)容進行修改將不會寫回文件,其它進程將無法共享這個頁框。如果本進程還未進行寫復制,而其它進程修改了頁的內(nèi)容,本進程是可以獲得更新后的數(shù)據(jù))因為映射文件的起動位移量受系統(tǒng)虛存頁長度的限制,那么如果映射區(qū)的長度不是頁長度的整數(shù)倍時,將如何呢?假定文件長12字節(jié),系統(tǒng)頁長為512字節(jié),則系統(tǒng)通常提供512字節(jié)的映射區(qū),其中后500字節(jié)被設0??梢孕薷倪@50字節(jié),但任何變動都不會在文件中反映出來。(這是由于內(nèi)核分配線性區(qū)和分配物理內(nèi)存都是以頁為單位)與映射存儲區(qū)相關有兩個信號

11、: SIGSEGV和SIGBUS。信號SIGSEGV通常用于指示進程試圖存取它不能存取的存儲區(qū)。如果進程企圖存數(shù)據(jù)到用mmap指定為只讀的映射存儲區(qū),那么也產(chǎn)生此信號。如果存取映射區(qū)的某個部分,而在存取時這一部分已不存在,則產(chǎn)生SIGBUS信號。例如,用文件長度映射一個文件,但在存訪該映射區(qū)之前,另一個進程已將該文件截短。此時,如果進程企圖存取對應于該文件尾端部分的映射區(qū),則接收到SIGBUS信號。(對信號的實現(xiàn)機制有待進一步分析)在fork之后,子進程繼承存儲映射區(qū)(因為子進程復制父進程地址空間,而存儲映射區(qū)是該地址空間中的一部分),但是由于同樣的理由,exec后的新程序則不繼承此存儲映射區(qū)

12、。(關閉文件描述符也不影響存儲映射區(qū),磁盤文件的頁高速緩存并不會因為進程的撤銷而撤銷,如果有足夠的空閑內(nèi)存,頁高速緩存中的頁將長期存在,使其它進程再使用該頁時不再訪問磁盤。)進程終止時,或調用了munmap之后,存儲映射區(qū)就被自動去除。關閉文件描述符fd并不解除映射區(qū)。(關閉存儲映射區(qū),只是撤銷進程頁表中的相應目錄項,并不影響頁高速緩存。)#include #include int munmap(void addr,size_t len) ;返回:若成功則為0,若出錯則為- 1munmap 并不影響被映射的對象也就是說,調用munmap并不使映射區(qū)的內(nèi)容寫到磁盤文件上。對于MAP_SHARED

13、區(qū)磁盤文件的更新,在寫到存儲映射區(qū)時按內(nèi)核虛存算法自動進行。(pdflush內(nèi)核線程用于刷新臟頁)例子程序:#include #include #include #include #include #include int main(int argc, char *argv)int fd, i, counter;pid_t pid;char *area = NULL;if(fd = open(test, O_RDWR) )= 0)printf(open errorn);area = (char *)mmap(0, sizeof(long), PROT_READ | PROT_WRITE, MA

14、P_SHARED, fd, 0);printf(area:%pn, area);close(fd);*(area + 1) = c;文件映射API補充msync函數(shù)的使用原型:? #include ? int msync(const void *start, size_t length, int flags);msync函數(shù)用來把映像的文件寫入磁盤。調用msync可以用對內(nèi)存中的映像的更新寫入一個被映像的文件,被強行寫入到磁盤的內(nèi)存取從start指定的地址開始,寫入length個字節(jié)的數(shù)據(jù)。flags可以是下面的一個值或多個的邏輯“或”:? 1、MS_ASYNC 調度一次寫入操作然后返回? 2

15、、MS_SYNC 在msync返回前寫入數(shù)據(jù)? 3、MS_INVALIDATE 讓映像到同一文件的映像無效,以便用新數(shù)據(jù)更新它們(MS_INVALIDATE的作用是使映射的頁高速緩存中的內(nèi)容無效,重新從磁盤寫入數(shù)據(jù)到映射的頁高速緩存??梢允褂肕S_INVALIDATE來測試內(nèi)核是否進行頁高速緩存數(shù)據(jù)的回寫磁盤操作,測試過程:寫一個字符到映射區(qū),然后使用MS_INVALIDATE使映射區(qū)的數(shù)據(jù)失效,并從磁盤寫入數(shù)據(jù),從測試結果看字符會被寫入磁盤,也就是說內(nèi)核幾乎在對映射區(qū)進行寫入操作的同時就進行了回寫磁盤操作)mprotect函數(shù)的使用mprotect函數(shù)修改在內(nèi)存映像上的保護模式。函數(shù)原型:?

16、 #include ? #include ? int mprotect(const void *start, size_t len, int prot);mprotect把自start開始的內(nèi)存區(qū)的保護模式修改為prot指定的值,如果執(zhí)行成功返回0,如果執(zhí)行失敗,mprotect返回-1,并且設置errno變量。port可以是PROT_READ(可讀)、PROT_WRITE(可寫)、PROT_EXEC(可執(zhí)行)、PROT_NONE(不可訪問)中的一個或多個(這里只是修改本進程頁表中的訪問控制標志,并不涉及物理頁,對其它進程沒有影響)鎖定內(nèi)存原型:? #include int mlock(con

17、st void *start, size_t len);? int munlock(void *start, size_t len);? int mlockall(int flags);? int munlockall(void);以4上個函數(shù)是對指定的內(nèi)存映像加鎖和解鎖,其中mlockall的flags包括MCL_CURRENT和MCL_FUTURE。只有root權限才能使用它們。start指出被加鎖或解鎖的內(nèi)存區(qū),len指出加鎖或解鎖的內(nèi)存區(qū)大小。flags的值可以是MCL_CURRENT和MCL_FUTURE之一或者兩個都有。MCL_CURRENT在調用返回前請求鎖住所有內(nèi)存頁面,MCL

18、_FUTURE指出鎖住所有增加到進程的地址空間的內(nèi)存頁面。(mlock的操作結果是vma的屬性,VM_LOCKED. 作用是被lock的內(nèi)存不參加swap,保證一直存在于內(nèi)存中.內(nèi)核中具體的策略執(zhí)行可以看函數(shù) swap_out_vma.當使用exec的時候,lock失效. fork的子進程也不繼承此屬性.對于實時進程和安全程序,此調用很有意義.對于加密程序,密碼不會被dump到磁盤上. 但是lock并不能阻止系統(tǒng)休眠的時候內(nèi)存被存儲到磁盤.mlock,munlock的操作不可堆疊. 多次調用mlock的一段內(nèi)存也會被一次unlock操作解鎖.mlock/munlock指定的地址會被round

19、down到一個page的邊界.)mremap函數(shù)的使用mremap函數(shù)用于改變一個被映像的文件大小。原型:? #include ? void *mremap(void *old_addr, size_t old_len, size_t new_len, unsigned long flags);mremap用指定的flags把地址在old_addr的內(nèi)存映像大小從old_len調整為new_len,flags如果為MREMAP_MAYMOVE則調整此內(nèi)存映像的地址。成功返回新地址,失敗返回NULL。(內(nèi)核要做事情是:改變線性區(qū)對象的長度。內(nèi)核會檢查是否可以直接擴大或者縮小線性區(qū)的大小,如果線性

20、對象相鄰的線性空間已經(jīng)被使用了,此時將沒法擴大了,如果此時設置了MREMAP_MAYMOVE標志,將會重新分配一塊新的線性空間,顯然這個空間的起始地址已經(jīng)改變)(四)IPC共享存儲(包含實現(xiàn)原理說明)IPC共享存儲允許兩個或多個進程共享一給定的存儲區(qū)。因為數(shù)據(jù)不需要在客戶機和服務器之間復制,所以這是最快的一種IPC。使用共享存儲的唯一竅門是多個進程之間對一給定存儲區(qū)的同步存取。若服務器將數(shù)據(jù)放入共享存儲區(qū),則在服務器做完這一操作之前,客戶機不應當去取這些數(shù)據(jù)。通常,信號量被用來實現(xiàn)對共享存儲存取的同步。調用的第一個函數(shù)通常是shmget,它獲得一個共享存儲標識符。#include #inclu

21、de int shmget(key_t key, size_t size, int shmflg);如果參數(shù)key為 IPC_PRIVATE。則會建立新的共享內(nèi)存對象其大小由size(單位字節(jié)Byte)指定,如果key不為IPC_PRIVATE,并且存在鍵值為key的共享內(nèi)存對象,則返回所關聯(lián)的id號,如果不存在鍵值為key的共享內(nèi)存對象,那么系統(tǒng)會視參數(shù)shmflg是否有IPC_CREAT來決定是否新建一個共享內(nèi)存對象。(每個共享內(nèi)存對象都對應一個目錄對象和一個inode對象,每個inode對象都包含一個address_space i_mapping對象,但是這些對象并沒有磁盤映像,而是為了

22、可以重復利用文件映射中提供的代碼,每個進程映射的物理頁就存儲在i_mapping對象里。)(以上是一種特殊的文件系統(tǒng),它并沒有掛載在某個目錄下,只是為了方便實現(xiàn)共享內(nèi)存) size是要建立共享內(nèi)存的長度。所有的內(nèi)存分配操作都是以頁為單位的。實際的大小是((bytes進位到4096整數(shù)倍)/4096 + 4) * 4096。(因為線性區(qū)和物理內(nèi)存的分配都是以頁為單位) shmflg主要和一些標志有關,其中有效的包括IPC_CREAT和IPC_EXCL,它們的功能與open()的O_CREAT和O_EXCL相當。 IPC_CREAT 如果共享內(nèi)存不存在,則創(chuàng)建一個共享內(nèi)存,否則打開操作。 IPC_

23、EXCL 只有在共享內(nèi)存不存在的時候,新的共享內(nèi)存才建立,否則就產(chǎn)生錯誤。如果單獨使用IPC_CREAT,shmget()函數(shù)要么返回一個已經(jīng)存在的共享內(nèi)存的操作符,要么返回一個新建的共享內(nèi)存的標識符。如果將IPC_CREAT和IPC_EXCL標志一起使用,shmget()將返回一個新建的共享內(nèi)存的標識符;如果該共享內(nèi)存已存在,或者返回-1。IPC_EXEL標志本身并沒有太大的意義,但是和IPC_CREAT標志一起使用可以用來保證所得的對象是新建的,而不是打開已有的對象。返回值成功返回共享內(nèi)存的標識符;不成功返回-1,errno儲存錯誤原因。 EINVAL 參數(shù)size小于SHMMIN或大于S

24、HMMAX。 EEXIST 預建立key所致的共享內(nèi)存,但已經(jīng)存在。 EIDRM 參數(shù)key所致的共享內(nèi)存已經(jīng)刪除。 ENOSPC 超過了系統(tǒng)允許建立的共享內(nèi)存的最大值(SHMALL )。 ENOENT 參數(shù)key所指的共享內(nèi)存不存在,參數(shù)shmflg也未設IPC_CREAT位。 EACCES 沒有權限。 ENOMEM 核心內(nèi)存不足。struct shmid_ds shmid_ds數(shù)據(jù)結構表示每個新建的共享內(nèi)存。當shmget()創(chuàng)建了一塊新的共享內(nèi)存后,返回一個可以用于引用該共享內(nèi)存的shmid_ds數(shù)據(jù)結構的標識符。include/linux/shm.h struct shmid_ds s

25、truct ipc_perm shm_perm; /* operation perms */ int shm_segsz; /* 共享內(nèi)存的大小 */ _kernel_time_t shm_atime; /* 最后一次附加這個共享內(nèi)存的時間 */ _kernel_time_t shm_dtime; /*最后一次分離這個共享內(nèi)存的時間 */ _kernel_time_t shm_ctime; /*最后一次改變這個共享內(nèi)存結構的時間*/ _kernel_ipc_pid_t shm_cpid; /* 建立這個共享內(nèi)存的進程識別碼 */ _kernel_ipc_pid_t shm_lpid; /*最后

26、一個操作共享內(nèi)存的進程識別碼*/ unsigned short shm_nattch; /* 附加這個共享內(nèi)存的進程個數(shù) */ unsigned short shm_unused; /* compatibility */ void *shm_unused2; /* ditto - used by DIPC */ void *shm_unused3; /* unused */ ;struct ipc_perm 對于每個IPC對象,系統(tǒng)共用一個struct ipc_perm的數(shù)據(jù)結構來存放權限信息,以確定一個ipc操作是否可以訪問該IPC對象。 struct ipc_perm _kernel_ke

27、y_t key; /共享內(nèi)存對象的key _kernel_uid_t uid; /共享內(nèi)存所屬的用戶識別碼(可以修改) _kernel_gid_t gid; /共享內(nèi)存所屬的組識別碼(可以修改) _kernel_uid_t cuid; /建立共享內(nèi)對象的用戶識別碼 _kernel_gid_t cgid; /建立共享內(nèi)對象的組識別碼 _kernel_mode_t mode; /這個共享內(nèi)存的讀寫權限(可以修改) unsigned short seq; /序號;shmctl函數(shù)對共享存儲段執(zhí)行多種操作。#include #include #include int shmctl(int shmid,

28、 int cmd, struct shmid_ds * buf) ;返回:若成功則為0,若出錯則為- 1cmd參數(shù)指定下列5種命令中一種,使其在shmid指定的段上執(zhí)行。? IPC_STAT 對此段取shmid_ds結構,并存放在由buf指向的結構中。? IPC_SET按buf指向的結構中的值設置與此段相關結構中的下列三個字段:(只能修改這3個字段)shm_perm.uid、shm_perm.gid以及shm_perm.mode。此命令只能由下列兩種進程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shm_perm.uid的進程;另一種是具有超級用戶特權的進程。? IPC_RMID

29、 從系統(tǒng)中刪除該共享存儲段。因為每個共享存儲段有一個連接計數(shù)(shm_nattch在shmid_ds結構中),所以除非使用該段的最后一個進程終止或與該段脫接,否則不會實際上刪除該存儲段。不管此段是否仍在使用,該段標識符立即被刪除,所以不能再用shmat與該段連接。此命令只能由下列兩種進程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shm_perm.uid的進程;另一種是具有超級用戶特權的進程。? SHM_LOCK鎖住共享存儲段。此命令只能由超級用戶執(zhí)行。? SHM_UNLOCK解鎖共享存儲段。此命令只能由超級用戶執(zhí)行。一旦創(chuàng)建了一個共享存儲段,進程就可調用shmat將其連接到它的

30、地址空間中。#include #include #include void *shmat(int shmid, void *addr, int flag) ;返回:若成功則為指向共享存儲段的指針,若出錯則為- 1。共享存儲段連接到調用進程的哪個地址上與addr參數(shù)以及在flag中是否指定SHM_RND位有關。(1) 如果addr為0,則此段連接到由內(nèi)核選擇的第一個可用地址上。(2) 如果addr非0,并且沒有指定SHM_RND,則此段連接到addr所指定的地址上。(3) 如果addr非0,并且指定了SHM_RND,則此段連接到( addr(addr mod SHMLBA))所表示的地址上。SH

31、M_RND命令的意思是:取整。SHMLBA的意思是:低邊界地址倍數(shù),它總是2的乘方。該算式是將地址向下取最近1個SHMLBA的倍數(shù)。除非只計劃在一種硬件上運行應用程序(這在當今是不大可能的),否則不用指定共享段所連接到的地址。所以一般應指定addr為0,以便由內(nèi)核選擇地址。如果在f l a g中指定了SHM_RDONLY位,則以只讀方式連接此段。否則以讀寫方式連接此段。(在進程頁表項中設置只讀標志,試圖修改該頁時將產(chǎn)生缺頁異常,這些都是由CPU的頁尋址硬件控制的)shmat的返回值是該段所連接的實際地址,如果出錯則返回1。當對共享存儲段的操作已經(jīng)結束時,則調用shmdt脫接該段。注意,這并不從

32、系統(tǒng)中刪除其標識符以及其數(shù)據(jù)結構。該標識符仍然存在,直至某個進程(一般是服務器)調用shmctl(帶命令IP C_RMID)特地刪除它。(連接是進程將共享內(nèi)存的物理頁加入進程頁表,脫離是從頁表中撤銷該物理頁的信息,并不改變實際的物理頁)#include #include #include int shmdt(void * addr);返回:若成功則為0,若出錯則為- 1addr參數(shù)是以前調用shmat時的返回值。下面是和IPC共享內(nèi)存有關的內(nèi)核參數(shù),可以修改的。其中shmall是所有共享內(nèi)存段可以使用的最大頁個數(shù)shmmni是一個共享內(nèi)存段的最小字節(jié)數(shù)shmmax是一個共享內(nèi)存段的最大字節(jié)數(shù)下

33、面是顯示系統(tǒng)中已近建立的共享內(nèi)存對象其中bytes是申請共享內(nèi)存時使用的大小參數(shù),實際的大小是((bytes進位到4096整數(shù)倍)/4096 + 4) * 4096。nattch是附加此共享內(nèi)存對象的進程數(shù)。例子程序:#include #include #include #include #include #include #include #define KEY 4 #define SIZE 4096*3 int main(int argc, char *argv)int shmid = 0, ret = 0;char *shmaddr = 0;struct shmid_ds buf;shm

34、id = shmget(KEY, SIZE, IPC_CREAT | SHM_R);printf(id: %dn, shmid);shmaddr = (char *)shmat(shmid, NULL, 0);*(shmaddr + 4095*3) = c;shmdt(shmaddr); 使用ipcs m 查看建立的共享內(nèi)存對象:要理解IPC共享內(nèi)存和文件映射的實現(xiàn)機制,先要理解什么是共享內(nèi)存,共享內(nèi)存實現(xiàn)的基本原理是什么。(五)共享內(nèi)存實現(xiàn)基本原理CPU要訪問某塊內(nèi)存,必須要獲得內(nèi)存的物理地址。CPU集成有尋址硬件,會根據(jù)機器語言指令中提供的地址,執(zhí)行地址轉換,獲得的物理地址。CPU有兩種轉

35、換模式:1.實模式 2.保護模式。實模式下的物理地址 = 線性地址。保護模式下的物理地址 = 線性地址通過分頁機制轉化為物理地址。啟動保護模式:把CPU控制寄存器CR0中的最高位置1。CPU保護模式尋址方式:圖1 分頁機制尋址說明:CR3控制寄存器的值是物理地址。由于尋找的是頁框的物理地址,所以CR3,頁目錄和頁表中存儲的物理地址后12位都為0。也就是這12位的空間不存儲物理地址,而是用于訪問控制(可讀/可寫/CPU特權級別),指示對應的頁是否存在等作用。在轉換過程中,出現(xiàn)以下情況之一將會引起也異常:涉及的頁目錄表內(nèi)的表項或頁表內(nèi)的表項中的P=0,即涉及到的頁不在內(nèi)存;違反也保護屬性的規(guī)定而對

36、頁進行訪問。注意:從2.6.11版本開始,采用了四級分頁模型,但基本原理是一樣的。每個進程的CR3的值是不同,進程切換時保存或者恢復CR3的值。只要設置了CR3的值CPU將自動進行尋址。要將某個物理頁加入進程的地址空間,要做的事情就是將物理頁的物理地址填寫進程頁目錄表內(nèi)的表項和頁表內(nèi)的表項。共享內(nèi)存實現(xiàn)原理就是:將相同的物理頁加入不同進程的地址空間。顯然進程中要加入一塊物理頁,就必須對應一塊線性空間,于是就要先申請一塊線性空間,然后根據(jù)這塊線性空間填寫頁目錄表內(nèi)的表項和頁表內(nèi)的表項。(這里可以說明為什么不同進程中的不同線性地址可以對應相同的物理地址)。由于linux會先分配線性空間,頁表的修改

37、會推后進行。Linux通過vm_area_struct對象實現(xiàn)線性區(qū),當產(chǎn)生缺頁異常時,會根據(jù)vm_area_struct對象來修改頁表,然后重新執(zhí)行產(chǎn)生缺頁異常的代碼??偟膩碚f,內(nèi)核實際要做的事情是很多的,但是,內(nèi)核也提供了很多接口,所以我們要做的事情還是比較少的。以上就是共享內(nèi)存實現(xiàn)的基本原理,下面分析一下IPC共享內(nèi)存和內(nèi)存映射實現(xiàn)機制。實際上IPC共享內(nèi)存的實現(xiàn)是基于內(nèi)存映射,原因是:內(nèi)存映射提供了一些接口,基于內(nèi)存映射來實現(xiàn)IPC共享內(nèi)存可以復用這些代碼。但是,兩者最終的實現(xiàn)原理還是修改頁表。(六)IPC共享內(nèi)存實現(xiàn)機制先看一下內(nèi)核是如何組織共享內(nèi)存對象的,如圖2所示:圖2 共享內(nèi)存

38、對象組織其中struct shmid_kernel就是一個共享內(nèi)存對象,使用id radix tree來組織所有的共享內(nèi)存對象。使用id號查找一個共享內(nèi)存對象。我們現(xiàn)在最關心的問題是:如何根據(jù)struct shmid_kernel結構獲得對象所擁有的物理內(nèi)存。是根據(jù)file-f_path.dentry-d_inodestruct address_space *mapping = inode-i_mappingmapping存儲著共享內(nèi)存擁有的物理頁,如圖3所示:圖3 共享內(nèi)存物理頁存儲方式其中page_tree用于存儲物理頁,每個節(jié)點的值類型是struct page *每個共享內(nèi)存對象對應一個

39、inode對象,這個對象是被多個進程共享,也就是說,進程是通過inode對象獲得物理頁。這里借用了文件映射的框架,i_mmaping對象也就是文件的頁高速緩存。進程映射共享內(nèi)存區(qū)域的過程:1.需要申請一塊線性地址空間,也就是生成一個vm_area_struct對象,并將對象加入到自己的地址空間,當此時并不修改進程頁表,而是把struct file對象加入到vm_area_struct對象中,執(zhí)行以下代碼:vma-vm_file = file;get_file(file);error = file-f_op-mmap(file, vma);注意:這里的file是根據(jù)shm_file生成的一個新的

40、對象,相當于shm_file的復制。2. 當?shù)谝淮卧L問共享內(nèi)存塊時,由于相應的頁表項還未填寫,將產(chǎn)生缺頁異常,內(nèi)核根據(jù)產(chǎn)生異常的線性地址找到對應的vm_area_struct對象,最后將執(zhí)行以下函數(shù):static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)struct inode *inode = vma-vm_file-f_path.dentry-d_inode;int error;int ret;if (loff_t)vmf-pgoff = i_size_read(inode)return VM_FAULT_SIGBUS;error = shmem_getpage(inode, vmf-pgoff, &vmf-page, SGP_CACHE, &ret);if (error)return (error = -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS);return ret | VM_FAULT_LOCKED;本函數(shù)的功能是:根據(jù)產(chǎn)生缺頁異常的線性地址找到對應的物理頁,并將這個物理頁加入頁表。以上說明的也就是內(nèi)存映射實現(xiàn)機制,現(xiàn)在做個總結:調用do_mmap()函數(shù)生成并注冊一個線性對象,同

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論