日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

進程間通信——共享內(nèi)存

系統(tǒng) 1943 0
共享內(nèi)存區(qū)域是被多個進程共享的一部分物理內(nèi)存。如果多個進程都把該內(nèi)存區(qū)域映射到自己的虛擬地址空間,則這些進程就都可以直接訪問該共享內(nèi)存區(qū)域,從而可以通過該區(qū)域進行通信。共享內(nèi)存是進程間共享數(shù)據(jù)的一種最快的方法,一個進程向共享內(nèi)存區(qū)域?qū)懭肓藬?shù)據(jù),共享這個內(nèi)存區(qū)域的所有進程就可以立刻看到其中的內(nèi)容。這塊共享虛擬內(nèi)存的頁面,出現(xiàn)在每一個共享該頁面的進程的頁表中。但是它不需要在所有進程的虛擬內(nèi)存中都有相同的虛擬地址。?

?????????象所有的 System V IPC對象一樣,對于共享內(nèi)存對象的獲取是由key控制。內(nèi)存共享之后,對進程如何使用這塊內(nèi)存就不再做檢查。它們必須依賴于其它機制,比如System V的信號燈來同步對于共享內(nèi)存區(qū)域的訪問(信號燈如何控制對臨界代碼的訪問另起一篇說話)。
?
????????每一個新創(chuàng)建的共享內(nèi)存對象都用一個shmid_kernel數(shù)據(jù)結(jié)構(gòu)來表達。系統(tǒng)中所有的shmid_kernel數(shù)據(jù)結(jié)構(gòu)都保存在shm_segs向量表中,該向量表的每一個元素都是一個指向shmid_kernel數(shù)據(jù)結(jié)構(gòu)的指針。
shm_segs向量表的定義如下:
struct shmid_kernel *shm_segs[SHMMNI];

?
??? SHMMNI為128,表示系統(tǒng)中最多可以有128個共享內(nèi)存對象。
?
??? 數(shù)據(jù)結(jié)構(gòu)shmid_kernel的定義如下:
????struct shmid_kernel
????{????
????????struct shmid_ds u;????????
????????unsigned long shm_npages;?
????????unsigned long *shm_pages;? ?
????????struct vm_area_struct *attaches;?
????};

?
??? 其中:
????shm_pages代表該共享內(nèi)存對象的所占據(jù)的內(nèi)存頁面數(shù)組,數(shù)組里面的每個元素當然是每個內(nèi)存頁面的起始地址.
??? shm_npages則是該共享內(nèi)存對象占用內(nèi)存頁面的個數(shù),以頁為單位。這個數(shù)量當然涵蓋了申請空間的最小整數(shù)倍.
??? ?(A new shared memory segment,? with size? equal to the value of size rounded up to a multiple of PAGE_SIZE)
??? shmid_ds是一個數(shù)據(jù)結(jié)構(gòu),它描述了這個共享內(nèi)存區(qū)的認證信息,字節(jié)大小,最后一次粘附時間、分離時間、改變時間,創(chuàng)建該共享區(qū)域的進程,最后一次對它操作的進程,當前有多少個進程在使用它等信息。
????其定義如下:
????struct shmid_ds {
????????struct ipc_perm shm_perm;??
????????int shm_segsz;?????????????
????????__kernel_time_t shm_atime;?
????????__kernel_time_t shm_dtime;?
????????__kernel_time_t shm_ctime;?
????????__kernel_ipc_pid_t shm_cpid;
????????__kernel_ipc_pid_t shm_lpid;
????????unsigned short shm_nattch;??
????????unsigned short shm_unused;??
????????void *shm_unused2;??????????
????????void *shm_unused3;??????????
????};

?
??????? attaches描述被共享的物理內(nèi)存對象所映射的各進程的虛擬內(nèi)存區(qū)域。每一個希望共享這塊內(nèi)存的進程都必須通過系統(tǒng)調(diào)用將其關(guān)聯(lián)(attach)到它的虛擬內(nèi)存中。這一過程將為該進程創(chuàng)建了一個新的描述這塊共享內(nèi)存的vm_area_struct數(shù)據(jù)結(jié)構(gòu)。創(chuàng)建時可以指定共享內(nèi)存在它的虛擬地址空間的位置,也可以讓Linux自己為它選擇一塊足夠的空閑區(qū)域。
?
????????這個新的vm_area_struct結(jié)構(gòu)是維系共享內(nèi)存和使用它的進程之間的關(guān)系的,所以除了要關(guān)聯(lián)進程信息外,還要指明這個共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)shmid_kernel所在位置; 另外,便于管理這些經(jīng)常變化的vm_area_struct,所以采取了鏈表形式組織這些數(shù)據(jù)結(jié)構(gòu),鏈表由attaches指向,同時 vm_area_struct數(shù)據(jù)結(jié)構(gòu)中專門提供了兩個指針:vm_next_shared和 vm_prev_shared,用于連接該共享區(qū)域在使用它的各進程中所對應(yīng)的vm_area_struct數(shù)據(jù)結(jié)構(gòu)。?
?????????????Linux為共享內(nèi)存提供了四種操作。
????????1. 共享內(nèi)存對象的創(chuàng)建或獲得。與其它兩種IPC機制一樣,進程在使用共享內(nèi)存區(qū)域以前,必須通過系統(tǒng)調(diào)用sys_ipc (call值為SHMGET)創(chuàng)建一個鍵值為key的共享內(nèi)存對象,或獲得已經(jīng)存在的鍵值為key的某共享內(nèi)存對象的引用標識符。以后對共享內(nèi)存對象的訪問都通過該引用標識符進行。對共享內(nèi)存對象的創(chuàng)建或獲得由函數(shù)sys_shmget完成,其定義如下:
int sys_shmget (key_t key, int size, int shmflg)
?
??? 這里key是表示該共享內(nèi)存對象的鍵值,size是該共享內(nèi)存區(qū)域的大小(以字節(jié)為單位),shmflg是標志(對該共享內(nèi)存對象的特殊要求)。
?
??? 它所做的工作如下:
??? 1) 如果key == IPC_PRIVATE,則總是會創(chuàng)建一個新的共享內(nèi)存對象。
? 但是? (The name choice IPC_PRIVATE was perhaps unfortunate, IPC_NEW would more?clearly show its function)
??? * 算出size要占用的頁數(shù),檢查其合法性。
??? * 申請一塊內(nèi)存用于建立shmid_kernel數(shù)據(jù)結(jié)構(gòu),注意這里申請的內(nèi)存區(qū)域大小不包括真正的共享內(nèi)存區(qū),實際上,要等到第一個進程試圖訪問它的時候才真正創(chuàng)建共享內(nèi)存區(qū)。
??? * 根據(jù)該共享內(nèi)存區(qū)所占用的頁數(shù),為其申請一塊空間用于建立頁表(每頁4個字節(jié)),將頁表清0。
??? * 搜索向量表shm_segs,為新創(chuàng)建的共享內(nèi)存對象找一個空位置。
??? * 填寫shmid_kernel數(shù)據(jù)結(jié)構(gòu),將其加入到向量表shm_segs中為其找到的空位置。
??? * 返回該共享內(nèi)存對象的引用標識符。
?
??? 2) 在向量表shm_segs中查找鍵值為key的共享內(nèi)存對象,結(jié)果有三:
??? * 如果沒有找到,而且在操作標志shmflg中沒有指明要創(chuàng)建新共享內(nèi)存,則錯誤返回,否則創(chuàng)建一個新的共享內(nèi)存對象。
??? * 如果找到了,但該次操作要求必須創(chuàng)建一個鍵值為key的新對象,那么錯誤返回。
??? * 否則,合法性、認證檢查,如有錯,則錯誤返回;否則,返回該內(nèi)存對象的引用標識符。
?
??? 共享內(nèi)存對象的創(chuàng)建者可以控制對于這塊內(nèi)存的訪問權(quán)限和它的key是公開還是私有。如果有足夠的權(quán)限,它也可以把共享內(nèi)存鎖定在物理內(nèi)存中。
??? 參見include/linux/shm.h
?
??? 2. 關(guān)聯(lián)。在創(chuàng)建或獲得某個共享內(nèi)存區(qū)域的引用標識符后,還必須將共享內(nèi)存區(qū)域映射(粘附)到進程的虛擬地址空間,然后才能使用該共享內(nèi)存區(qū)域。系統(tǒng)調(diào)用 sys_ipc(call值為SHMAT)用于共享內(nèi)存區(qū)到進程虛擬地址空間的映射,而真正完成粘附動作的是函數(shù)sys_shmat,
?
其定義如下:???

???????#include <sys/types.h>
?????? #include <sys/shm.h>

?????? void *shmat(int shmid, const void *shmaddr, int shmflg);


?
??? 其中:
???? shmid是shmget返回的共享內(nèi)存對象的引用標識符;
??? shmaddr用來指定該共享內(nèi)存區(qū)域在進程的虛擬地址空間對應(yīng)的虛擬地址;
??? shmflg是映射標志;
????返回的是在進程中的虛擬地址
?
??? 該函數(shù)所做的工作如下:
??? 1) 根據(jù)shmid找到共享內(nèi)存對象。
??? 2) 如果shmaddr為0,即用戶沒有指定該共享內(nèi)存區(qū)域在它的虛擬空間中的位置,則由系統(tǒng)在進程的虛擬地址空間中為其找一塊區(qū)域(從1G開始);否則,就用shmaddr作為映射的虛擬地址。
?? (If? shmaddr? is NULL, the system chooses a suitable (unused) address a他 which to attach the segment)
??? 3) 檢查虛擬地址的合法性(不能超過進程的最大虛擬空間大小—3G,不能太接近堆棧棧頂)。
??? 4) 認證檢查。
??? 5) 申請一塊內(nèi)存用于建立數(shù)據(jù)結(jié)構(gòu)vm_area_struct,填寫該結(jié)構(gòu)。
??? 6) 檢查該內(nèi)存區(qū)域,將其加入到進程的mm結(jié)構(gòu)和該共享內(nèi)存對象的vm_area_struct隊列中。
?
??? 共享內(nèi)存的粘附只是創(chuàng)建一個vm_area_struct數(shù)據(jù)結(jié)構(gòu),并將其加入到相應(yīng)的隊列中,此時并沒有創(chuàng)建真正的共享內(nèi)存頁。
?
??? 當進程第一次訪問共享虛擬內(nèi)存的某頁時,因為所有的共享內(nèi)存頁還都沒有分配,所以會發(fā)生一個page fault異常。當Linux處理這個page fault的時候,它找到發(fā)生異常的虛擬地址所在的vm_area_struct數(shù)據(jù)結(jié)構(gòu)。在該數(shù)據(jù)結(jié)構(gòu)中包含有這類共享虛擬內(nèi)存的一組處理程序,其中的 nopage操作用來處理虛擬頁對應(yīng)的物理頁不存在的情況。對共享內(nèi)存,該操作是shm_nopage(定義在ipc/shm.c中)。該操作在描述這個共享內(nèi)存的shmid_kernel數(shù)據(jù)結(jié)構(gòu)的頁表shm_pages中查找發(fā)生page fault異常的虛擬地址所對應(yīng)的頁表條目,看共享頁是否存在(頁表條目為0,表示共享頁是第一次使用)。如果不存在,它就分配一個物理頁,并為它創(chuàng)建一個頁表條目。這個條目不但進入當前進程的頁表,同時也存到shmid_kernel數(shù)據(jù)結(jié)構(gòu)的頁表shm_pages中。
?
??? 當下一個進程試圖訪問這塊內(nèi)存并得到一個page fault的時候,經(jīng)過同樣的路徑,也會走到函數(shù)shm_nopage。此時,該函數(shù)查看shmid_kernel數(shù)據(jù)結(jié)構(gòu)的頁表shm_pages時,發(fā)現(xiàn)共享頁已經(jīng)存在,它只需把這里的頁表項填到進程頁表的相應(yīng)位置即可,而不需要重新創(chuàng)建物理頁。所以,是第一個訪問共享內(nèi)存頁的進程使得這一頁被創(chuàng)建,而隨后訪問它的其它進程僅把此頁加到它們的虛擬地址空間。
?
??? 3. 分離。當進程不再需要共享虛擬內(nèi)存的時候,它們與之分離(detach)。只要仍舊有其它進程在使用這塊內(nèi)存,這種分離就只會影響當前的進程,而不會影響其它進程。當前進程的vm_area_struct數(shù)據(jù)結(jié)構(gòu)被從shmid_ds中刪除,并被釋放。當前進程的頁表也被更新,共享內(nèi)存對應(yīng)的虛擬內(nèi)存頁被標記為無效。當共享這塊內(nèi)存的最后一個進程與之分離時,共享內(nèi)存頁被釋放,同時,這塊共享內(nèi)存的shmid_kernel數(shù)據(jù)結(jié)構(gòu)也被釋放。
?
? 系統(tǒng)調(diào)用sys_ipc (call值為SHMDT) 用于共享內(nèi)存區(qū)與進程虛擬地址空間的分離,而真正完成分離動作的是函數(shù) ????

??? sys_shmdt,其定義如下:
??? int sys_shmdt (char *shmaddr)
?
??? 其中shmaddr是進程要分離的共享頁的開始虛擬地址。
?
??? 該函數(shù)搜索進程的內(nèi)存結(jié)構(gòu)中的所有vm_area_struct數(shù)據(jù)結(jié)構(gòu),找到地址shmaddr對應(yīng)的一個,調(diào)用函數(shù)do_munmap將其釋放。
?
??? 在函數(shù)do_munmap中,將要釋放的vm_area_struct數(shù)據(jù)結(jié)構(gòu)從進程的虛擬內(nèi)存中摘下,清除它在進程頁表中對應(yīng)的頁表項(可能占多個頁表項).?
??
??? 如果共享的虛擬內(nèi)存沒有被鎖定在物理內(nèi)存中,分離會更加復(fù)雜。因為在這種情況下,共享內(nèi)存的頁可能在系統(tǒng)大量使用內(nèi)存的時候被交換到系統(tǒng)的交換磁盤。為了避免這種情況,可以通過下面的控制操作,將某共享內(nèi)存頁鎖定在物理內(nèi)存不允許向外交換。共享內(nèi)存的換出和換入,已在第3章中討論。
?
??? 4. 控制。Linux在共享內(nèi)存上實現(xiàn)的第四種操作是共享內(nèi)存的控制(call值為SHMCTL的sys_ipc調(diào)用),它由函數(shù)sys_shmctl實現(xiàn)。控制操作包括獲得共享內(nèi)存對象的狀態(tài),設(shè)置共享內(nèi)存對象的參數(shù)(如uid、gid、mode、ctime等),將共享內(nèi)存對象在內(nèi)存中鎖定和釋放(在對象的mode上增加或去除SHM_LOCKED標志),釋放共享內(nèi)存對象資源等。
?
??? 共享內(nèi)存提供了一種快速靈活的機制,它允許進程之間直接共享大量的數(shù)據(jù),而無須使用拷貝或系統(tǒng)調(diào)用。共享內(nèi)存的主要局限性是它不能提供同步,如果兩個進程企圖修改相同的共享內(nèi)存區(qū)域,由于內(nèi)核不能串行化這些動作,因此寫的數(shù)據(jù)可能任意地互相混合。所以使用共享內(nèi)存的進程必須設(shè)計它們自己的同步協(xié)議,如用信號燈等。

以下是使用共享內(nèi)存機制進行進程間通信的基本操作:

需要包含的頭文件:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

1.創(chuàng)建共享內(nèi)存:

?int shmget(key_t key,int size,int shmflg);

參數(shù)說明:

key:用來表示新建或者已經(jīng)存在的共享內(nèi)存去的關(guān)鍵字。

size:創(chuàng)建共享內(nèi)存的大小。

shmflg:可以指定的特殊標志。IPC_CREATE,IPC_EXCL以及低九位的權(quán)限。

eg:

int shmid;

shmid=shmget(IPC_PRIVATE,4096,IPC_CREATE|IPC_EXCL|0660);

if(shmid==-1)

perror("shmget()");

?

2.連接共享內(nèi)存

char *shmat(int shmid,char *shmaddr,int shmflg);

參數(shù)說明

shmid:共享內(nèi)存的關(guān)鍵字

shmaddr:指定共享內(nèi)存出現(xiàn)在進程內(nèi)存地址的什么位置,通常我們讓內(nèi)核自己決定一個合適的地址位置,用的時候設(shè)為0。

shmflg:制定特殊的標志位。

eg:

int shmid;

char *shmp;

shmp=shmat(shmid,0,0);

if(shmp==(char *)(-1))

perror("shmat()\n");

3.使用共享內(nèi)存

在使用共享內(nèi)存是需要注意的是,為防止內(nèi)存訪問沖突,我們一般與信號量結(jié)合使用。

4.分離共享內(nèi)存:當程序不再需要共享內(nèi)后,我們需要將共享內(nèi)存分離以便對其進行釋放,分離共享內(nèi)存的函數(shù)原形如下:

int shmdt(char *shmaddr);

?

5. 釋放共享內(nèi)存

int shmctl(int shmid,int cmd,struct shmid_ds *buf);

*****************示例**********************

int? ?*__accept_socketfd ;

?int shmid = shmget(SHAREMEMID,sizeof(int),IPC_CREAT|0666);?
?
?if (( __accept_socketfd = (int *)shmat(shmid,NULL,0 )) == (int *)-1 )
?{
??printf("Error:shmat\n");
??return;
?}
??? *__accept_socketfd = 0;

?

轉(zhuǎn)載自: http://www.cppblog.com/sleepwom/archive/2012/01/02/163452.html

進程間通信——共享內(nèi)存


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 宁德市| 青神县| 扶沟县| 宝清县| 平顺县| 盱眙县| 池州市| 泌阳县| 邻水| 集安市| 怀仁县| 诸暨市| 文安县| 浦城县| 佳木斯市| 迁西县| 石家庄市| 民和| 静安区| 合作市| 武功县| 阿巴嘎旗| 义马市| 抚州市| 固原市| 富蕴县| 乐昌市| 万年县| 稻城县| 古交市| 同德县| 扎赉特旗| 台中县| 绥芬河市| 嘉义县| 桐城市| 克东县| 隆尧县| 上杭县| 北票市| 米易县|