Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    共享內(nèi)存

    • 共享內(nèi)存可以說(shuō)是最有用的進(jìn)程間通信方式,也是最快的ipc形式,兩個(gè)不同的進(jìn)程a、b共享內(nèi)存的意思就是:同一塊物理內(nèi)存被映射到進(jìn)程a、b各自的進(jìn)程地址空間,進(jìn)程a可以同時(shí)看到進(jìn)程b對(duì)共享內(nèi)存中數(shù)據(jù)的更新,反之亦然。

    • 由于個(gè)多個(gè)進(jìn)程共享同一塊內(nèi)存區(qū)域,必然需要某種同步機(jī)制、互斥鎖和信號(hào)量都可以。

    好處: 效率高,進(jìn)程可以直接讀寫內(nèi)存,而不需要復(fù)制任何數(shù)據(jù),而管道、消息隊(duì)列等通信方式,則需要在內(nèi)核和用戶空間進(jìn)行四次數(shù)據(jù)復(fù)制。

    并且只有在解除映射時(shí),共享內(nèi)存的內(nèi)容才會(huì)寫會(huì)文紀(jì)念

    共享內(nèi)存通過內(nèi)核對(duì)象,使得不同的進(jìn)程在自己的虛擬地址空間上分配一塊空間映射到相同的物理內(nèi)存空間上,這塊物理內(nèi)存空間對(duì)于映射到上面的每個(gè)進(jìn)程而言都是可以訪問的。(臨界資源)

    Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    共享內(nèi)存就是允許兩個(gè)不相關(guān)的進(jìn)程訪問同一個(gè)邏輯內(nèi)存

    共享內(nèi)存是在兩個(gè)正在運(yùn)行的進(jìn)程之間共享和傳遞數(shù)據(jù)的一種非常有效的方式。

    不同進(jìn)程之間共享的內(nèi)存通常安排為同一段物理內(nèi)存。

    進(jìn)程可以將同一段共享內(nèi)存連接到它們自己的地址空間中,所有進(jìn)程都可以訪問共享內(nèi)存中的地址,就好像它們是由用c語(yǔ)言函數(shù)malloc()分配的內(nèi)存一樣。

    而如果某個(gè)進(jìn)程向共享內(nèi)存寫入數(shù)據(jù),所做的改動(dòng)將立即影響到可以 訪問同一段共享內(nèi)存的任何其他進(jìn)程。

    mmap()及其相關(guān)的系統(tǒng)調(diào)用

    mmap是linux操作系統(tǒng)提供給用戶空間調(diào)用的內(nèi)存映射函數(shù),很多人僅僅只是知道可以通過mmap完成進(jìn)程間的內(nèi)存共享和減少用戶態(tài)到內(nèi)核態(tài)的數(shù)據(jù)拷貝次數(shù),但是并沒有深入理解mmap在操作系統(tǒng)內(nèi)部是如何實(shí)現(xiàn)的,原理是什么

    Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    使用mmap()系統(tǒng)調(diào)用,進(jìn)程們可以通過映射同一個(gè)普通文件來(lái)實(shí)現(xiàn)內(nèi)存共享。普通文件被映射到進(jìn)程地址空間后,進(jìn)程可以訪問普通內(nèi)存一樣對(duì)文件進(jìn)行訪問,不必再調(diào)用read和write操作。?

    注意: mmap并不是完全為了IPC而設(shè)計(jì)的,只是IPC的一種應(yīng)用方式,它本身提供了一種像訪問普通內(nèi)存一樣的訪問對(duì)普通文件進(jìn)行操作的方式。

    通過使用帶有特殊權(quán)限集的虛擬內(nèi)存段來(lái)實(shí)現(xiàn)。當(dāng)對(duì)這種虛擬內(nèi)存段進(jìn)行讀寫時(shí),操作系統(tǒng)會(huì)去讀寫與之對(duì)應(yīng)的磁盤文件部分。

    mmap 函數(shù)創(chuàng)建一個(gè)指向一段內(nèi)存區(qū)域的指針,該內(nèi)存區(qū)域與可以通過一個(gè)打開的文件描述符訪問的文件的內(nèi)容相關(guān)聯(lián)

    解釋如下:

    mmap()

    #include?<sys>  void?*mmap(void?*addr,?size_t?Length,?int?prot,?int?flags,?int?fd,?off_t?offset);</sys>

    可以通過傳遞 offset 參數(shù)來(lái)改變經(jīng)共享內(nèi)存段訪問的文件中數(shù)據(jù)的起始偏移值。

    打開的文件描述符由 fd 參數(shù)給出。

    可以訪問的數(shù)據(jù)量(即內(nèi)存段的長(zhǎng)度)由 length 參數(shù)設(shè)置。

    可以通過 addr 參數(shù)來(lái)請(qǐng)求使用某個(gè)特定的內(nèi)存地址。如果它的取值是零,結(jié)果指針就將自動(dòng)分配。若不遵循此做法,則會(huì)降低程序的可移植性,因?yàn)榭捎玫刂贩秶诓煌到y(tǒng)上是不同的。

    prot 參數(shù)用于設(shè)置內(nèi)存段的訪問權(quán)限。它是下列常數(shù)值的按位或的結(jié)果

    • PROT_READ 內(nèi)存段可讀。

    • PROT_WRITE 內(nèi)存段可寫。

    • PROT_EXEC 內(nèi)存段可執(zhí)行。

    • PROT_NONE 內(nèi)存段不能被訪問。

    flags 參數(shù)控制程序?qū)υ搩?nèi)存段的改變所造成的影響:

    Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    mmap()用于共享內(nèi)存的量和兩種方式如下:

    使用普通文件提供的內(nèi)存映射,適用于任何進(jìn)程間,使用該方式需要先打開或者創(chuàng)建一個(gè)文件,再調(diào)用ngmmap,典型調(diào)用代碼如下:

    fd?=?open(name.falg.mode); if(fd?<p>使用特殊文件提供的內(nèi)存映射,適用于具有親緣關(guān)系的進(jìn)程之間,由于父子進(jìn)程特殊的親緣關(guān)系,在父進(jìn)程中先調(diào)用mmap,調(diào)用fork,那么在代用fork之后,子進(jìn)程可以繼承父進(jìn)程匿名映射后的地址空間,同樣也繼承mmap返回的地址,這樣父子進(jìn)程就可以通過映射區(qū)域進(jìn)行通信了。(注意:一般來(lái)說(shuō),子進(jìn)程單獨(dú)維護(hù)從父進(jìn)程繼承而來(lái)的一些變量,而mmap()返回的地址由父子進(jìn)程共同維護(hù))【具體使用實(shí)現(xiàn)敬請(qǐng)期待博主整理】</p><h4>munmap()</h4><p>用于解除內(nèi)存映射,取消參數(shù)start所指的映射內(nèi)存的起始地址,參數(shù)length則是欲取消的內(nèi)存大小,當(dāng)進(jìn)程結(jié)束或者利用exec相關(guān)函數(shù)來(lái)執(zhí)行其他程序時(shí),映射內(nèi)存會(huì)自動(dòng)解除,但關(guān)閉對(duì)應(yīng)的文件描述符時(shí)不會(huì)解除映射。</p><pre class="brush:cpp;">#include?<sys>  int?munmap(void?*addr,?size_t?length);</sys>

    共享內(nèi)存的使用

    與信號(hào)量一樣,在linux中也提供了一組函數(shù)接口用于使用共享內(nèi)存,而且使用共享共存的接口還與信號(hào)量的非常相似,而且比使用信號(hào)量的接口來(lái)得簡(jiǎn)單。它們聲明在頭文件 sys/shm.h 中。?

    1.獲取或創(chuàng)建內(nèi)核對(duì)象,并且制定共享內(nèi)存的大小(系統(tǒng)分配物理空間是,按照頁(yè)進(jìn)行分配)

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

    只是創(chuàng)建內(nèi)核對(duì)象,并申請(qǐng)物理空間

    • key_t key:與信號(hào)量的semget函數(shù)一樣,程序需要提供一個(gè)參數(shù)key(非0整數(shù)),它有效地為共享內(nèi)存段命名,不同的進(jìn)程通過相同的key值來(lái)訪問同一塊共享內(nèi)存

    • int size:size以字節(jié)為單位指定需要共享的內(nèi)存容量

    • int flag:falg是權(quán)限標(biāo)志,它的作用與open函數(shù)的mode參數(shù)一樣,如果要想在key標(biāo)識(shí)的共享內(nèi)存不存在時(shí),創(chuàng)建它的話,可以與IPC_CREAT做或操作。共享內(nèi)存的權(quán)限標(biāo)志與文件的讀寫權(quán)限一樣,舉例來(lái)說(shuō),0644,它表示允許一個(gè)進(jìn)程創(chuàng)建的共享內(nèi)存被內(nèi)存創(chuàng)建者所擁有的進(jìn)程向共享內(nèi)存讀取和寫入數(shù)據(jù),同時(shí)其他用戶創(chuàng)建的進(jìn)程只能讀取共享內(nèi)存。

    返回值

    • 調(diào)用成功后,shmget()函數(shù)會(huì)返回一個(gè)非負(fù)整數(shù),用作后續(xù)共享內(nèi)存函數(shù)的共享內(nèi)存標(biāo)識(shí)符,該標(biāo)識(shí)符與key相關(guān)。

    • 調(diào)用失敗返回-1.

    2.分配自己虛擬地址空間映射到共享內(nèi)存的物理空間上

    void?*shmat(int?shmid,const?void?*addr,?int?flag);
    • shmid:shmid是由shmget()函數(shù)返回的共享內(nèi)存標(biāo)識(shí)。

    • void *addr:addr指定共享內(nèi)存連接到當(dāng)前進(jìn)程中的地址位置,通常為NULL,表示讓系統(tǒng)來(lái)選擇共享內(nèi)存的地址。

    • int flag:flag是一組標(biāo)志位,通常為0。

    調(diào)用成功時(shí)返回一個(gè)指向共享內(nèi)存第一個(gè)字節(jié)的指針,如果調(diào)用失敗返回-1.

    3.斷開當(dāng)前進(jìn)程與共享內(nèi)存的映射

    不使用刪除而使用斷開的原因是因?yàn)椋阂苍S還存在其他的進(jìn)程會(huì)繼續(xù)使用這塊共享內(nèi)存

    int?shmdt(const?void?*addr);

    4.操作共享內(nèi)存的方法

    int?shmctl(int?shmid,?int?cmd,?Struct?shmid_t?*buf);
    • int shmid:shmid是shmget()函數(shù)返回的共享內(nèi)存標(biāo)識(shí)符。

    • int cmd:command是要采取的操作,它可以取下面的三個(gè)值 :

    IPC_STAT:把shmid_ds結(jié)構(gòu)中的數(shù)據(jù)設(shè)置為共享內(nèi)存的當(dāng)前關(guān)聯(lián)值,即用共享內(nèi)存的當(dāng)前關(guān)聯(lián)值覆蓋shmid_ds的值。IPC_SET:如果進(jìn)程有足夠的權(quán)限,就把共享內(nèi)存的當(dāng)前關(guān)聯(lián)值設(shè)置為shmid_ds結(jié)構(gòu)中給出的值IPC_RMID:刪除共享內(nèi)存段

    • struct shmid_t *buf:buf是一個(gè)結(jié)構(gòu)指針,它指向共享內(nèi)存模式和訪問權(quán)限的結(jié)構(gòu)

    因?yàn)橛羞B接計(jì)數(shù)器,除非最后一個(gè)進(jìn)程與該共享段斷開連接,則刪除該共享段。否則,并不會(huì)真正刪除該共享段,但是共享內(nèi)存的內(nèi)核對(duì)象會(huì)被立即刪除,不能使用shmat方法與該段連接。?

    一個(gè)進(jìn)程調(diào)用該方法刪除后,不會(huì)影響之前已經(jīng)和該共享存儲(chǔ)段連接的進(jìn)程

    下面我們利用共享內(nèi)存來(lái)進(jìn)行一個(gè)簡(jiǎn)單的測(cè)試:

    Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    完成下面的過程,

    Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    成功在共享內(nèi)存中讀到了數(shù)據(jù)

    Linux進(jìn)程間通信怎么實(shí)現(xiàn)

    #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<unistd.h>  #include<sys> #include<sys> #include<sys>  #include"sem.h"  #define?READSEM?1 #define?WRITESEM?0  int?main() { 	int?shmid?=?shmget((key_t)1234,128,0664?|?IPC_CREAT); 	assert(shmid?!=?-1);  	char?*ptr?=?(char*)shmat(shmid,NULL,0); 	assert(ptr?!=?(char*)-1); 	 	int?initVal[]?=?{1,0}; 	int?semid?=?SemGet(1234,intVal,2); 	assert(semid?!=?-1); 	 	//A進(jìn)程寫 	while(1) 	{ 		SemP(semid,WRITESEM); 		printf("Input:"); 		 		fgets(ptr,127,stdin); 		 		SemV(semid,READSEM); 		 		if(strncmp(ptr,"end",3)?==?0) 		{ 			break; 		} 	} 	 	shmdt(ptr); 	exit(0); }</sys></sys></sys></unistd.h></string.h></assert.h></stdlib.h></stdio.h>
    #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<unistd.h>  #include<sys> #include<sys> #include<sys>  #include"sem.h"  #define?READSEM?1 #define?WRITESEM?0  int?main() { 	int?shmid?=?shmget((key_t)1234,128,0664?|?IPC_CREAT); 	assert(shmid?!=?-1);  	char?*ptr?=?(char*)shmat(shmid,NULL,0); 	assert(ptr?!=?(char*)-1); 	 	int?initVal[]?=?{1,0}; 	int?semid?=?SemGet(1234,intVal,2); 	assert(semid?!=?-1); 	 	//B進(jìn)程讀 	while(1) 	{ 		SemP(semid,READSEM); 		 		if(strncmp(ptr,"end",3)?==?0) 		{ 			break; 		} 		 		int?i?=?0; 		for(;i?<p>從上面的代碼中我們可以看出:?</p> <p><strong>共享內(nèi)存是最快的IPC,在通信過程中少了兩次數(shù)據(jù)的拷貝。(相較于管道)</strong></p> <h3>命令管理共享內(nèi)存</h3> <ul class=" list-paddingleft-2"> <li><p>查看 ipcs -m</p></li> <li><p>刪除 ipcrm -m shmid</p></li> </ul></sys></sys></sys></unistd.h></string.h></assert.h></stdlib.h></stdio.h>

    ? 版權(quán)聲明
    THE END
    喜歡就支持一下吧
    點(diǎn)贊13 分享