進程通信之共享內存

共享內存
共享內存就是允許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞數據的一種非常有效的方式。不同進程之間共享的內存通常安排爲同一段物理內存。進程可以將同一段共享內存連接到它們自己的地址空間中,所有進程都可以訪問共享內存中的地址,就好像它們是由用C語言函數malloc分配的內存一樣。而如果某個進程向共享內存寫入數據,所做的改動將立即影響到可以訪問同一段共享內存的任何其他進程。

共享內存並未提供同步機制,也就是說,在第一個進程結束對共享內存的寫操作之前,並無自動機制可以阻止第二個進程開始對它進行讀取。所以我們通常需要用其他的機制來同步對共享內存的訪問。

http://blog.csdn.net/xiaoliangsky/article/details/40024657

共享內存相關的函數

1創建共享內存shmget

函數原型:int   shmget(key_t   key,   size_t   size,   int  shmflag);

key: 標識符的規則
size:共享存儲段的字節數
shmflag:讀寫的權限
返回值:成功返回共享存儲的id,失敗返回-1
-----------------------------------------------
key 標識共享內存的鍵值:0/IPC_PRIVATE。當key的取值爲IPC_PRIVATE創建一塊新的內存;如果key的取值爲0,而參數shmflg中設置了IPC_PRIVATE這個標誌,則同樣將創建一塊新的共享內存。
在IPC的通信模式下,不管是使用消息隊列還是共享內存,甚至是信號量,每個IPC的對象(object)都有唯一的名字,稱爲“鍵”(key)。通過“鍵”,進程能夠識別所用的對象。“鍵”與IPC對象的關係就如同文件名稱之於文件,通過文件名,進程能夠讀寫文件內的數據,甚至多個進程能夠共用一個文件。而在IPC的通訊模式下,通過“鍵”的使用也使得一個IPC對象能爲多個進程所共用。
Linux系統中的所有表示System V中IPC對象的數據結構都包括一個ipc_perm結構,其中包含有IPC對象的鍵值,該鍵用於查找System V中IPC對象的引用標識符。如果不使用“鍵”,進程將無法存取IPC對象,因爲IPC對象並不存在於進程本身使用的內存中。
通常,都希望自己的程序能和其他的程序預先約定一個唯一的鍵值,但實際上並不是總可能的成行的,因爲自己的程序無法爲一塊共享內存選擇一個鍵值。因此,在此把key設爲IPC_PRIVATE,這樣,操作系統將忽略鍵,建立一個新的共享內存,指定一個鍵值,然後返回這塊共享內存IPC標識符ID。而將這個新的共享內存的標識符ID告訴其他進程可以在建立共享內存後通過派生子進程,或寫入文件或管道來實現。

int size(單位字節Byte)
-----------------------------------------------
    size是要建立共享內存的長度。所有的內存分配操作都是以頁爲單位的。所以如果一段進程只申請一塊只有一個字節的內存,內存也會分配整整一頁(在i386機器中一頁的缺省大小PACE_SIZE=4096字節)這樣,新創建的共享內存的大小實際上是從size這個參數調整而來的頁面大小。即如果size爲1至4096,則實際申請到的共享內存大小爲4K(一頁);4097到8192,則實際申請到的共享內存大小爲8K(兩頁),依此類推。

int shmflg
-----------------------------------------------
    shmflg主要和一些標誌有關。其中有效的包括IPC_CREAT和IPC_EXCL,它們的功能與open()的O_CREAT和O_EXCL相當。
    IPC_CREAT   如果共享內存不存在,則創建一個共享內存,否則打開操作。
    IPC_EXCL    只有在共享內存不存在的時候,新的共享內存才建立,否則就產生錯誤。

    如果單獨使用IPC_CREAT,shmget()函數要麼返回一個已經存在的共享內存的操作符,要麼返回一個新建的共享內存的標識符。如果將IPC_CREAT和IPC_EXCL標誌一起使用,shmget()將返回一個新建的共享內存的標識符;如果該共享內存已存在,或者返回-1。IPC_EXEL標誌本身並沒有太大的意義,但是和IPC_CREAT標誌一起使用可以用來保證所得的對象是新建的,而不是打開已有的對象。對於用戶的讀取和寫入許可指定SHM_R和SHM_W,(SHM_R>3)和(SHM_W>3)是一組讀取和寫入許可,而(SHM_R>6)和(SHM_W>6)是全局讀取和寫入許可。

返回值
-----------------------------------------------
成功返回共享內存的標識符;不成功返回-1,errno儲存錯誤原因。
    EINVAL        參數size小於SHMMIN或大於SHMMAX。
    EEXIST        預建立key所致的共享內存,但已經存在。
    EIDRM         參數key所致的共享內存已經刪除。
    ENOSPC        超過了系統允許建立的共享內存的最大值(SHMALL )。
    ENOENT        參數key所指的共享內存不存在,參數shmflg也未設IPC_CREAT位。
    EACCES        沒有權限。
    ENOMEM        核心內存不足。


2 連接共享內存shmat

函數原型:void* shmat(int shmid, const void *shmaddr, int shmflag)

連接共享內存標識符爲shmid的共享內存,連接成功後把共享內存區對象映射到調用
進程的地址空間,隨後可以像在本地空間一樣訪問。

-----------------------------------------------

shmid 共享內存標識符,由shmget函數返回的id
shmaddr 指點共享內存出現在進程內存地址的什麼位置,直接指定爲NULL時,有內核自己決定
一個合適的地址位置。
shmflg SHM_RDONLY:爲只讀模式,其他爲讀寫模式

返回值

-----------------------------------------------

成功:附件好的共享內存地址
出錯:-1,錯誤原因存在於error中
注意:fork後子進程繼承已連接的共享內存地址。exec後該子進程與共享的內存地址自動脫離。
進程結束後,已連接的共享內存地址會自動脫離。

錯誤代碼

-----------------------------------------------

EACCES:無權限已指定方式連接共享內存
EINVAL:    無效的參數shmid或shmaddr
ENOEME:核心內存不足


3“分離”共享內存shmdt

函數原型:int shmdt(const void *shmaddr)

用來斷開與共享內存附加點的地址空間,阻止本進程訪問此片共享內存。
shmaddr:連接的共享內存的起始地址

返回值

-----------------------------------------------

成功返回0
出錯返回-1,錯誤原因存在於error中
注意:本函數調用並不刪除所指定的共享內存區,而只是將先前用shmat函數連接(attach)好的共享內存脫離(detach)目前的進程

錯誤碼

-----------------------------------------------

EINVAL:無效的參數shmaddr


4管理共享內存shmctl

函數原型:int  shmctl(int   shmid,    int   cmd,    struct   shmid_ds   *buf)

管理共享沒存。

-----------------------------------------------

shmid 共享內存標識符
cmd     IPC_STAT:得到共享內存的狀態,把共享內存的shmid_ds結構複製到buf;
            IPC_SET:改變共享內存的狀態,把shmid_ds中的uid、gid、mode複製到共享內存的shmid_ds結構體。
            IPC_RMID:刪除這片共享內存
buf 共享內存管理結構體。具體說明參見貢獻內存內核結構定義部分。

返回值

-----------------------------------------------

成功返回0
出錯返回-1,錯誤原因存在於error中

錯誤代碼

-----------------------------------------------

EACCESS:參數cmd爲IPC_STAT,確無權限讀取該共享內存
EFAULT:參數buf指向無效的內存地址
EIDRM:標識符爲msqid的共享內存已被刪除

EINVAL:無效的參數cmd或shmid
EPERM:參數cmd爲IPC_SET或IPC_RMID,卻無足夠的權限執行


最後來一個例子:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <error.h>

#define MEMORY_SIZE 1024

int main()
{
	int               wpid;
	int               status;
	int               failed;
	int               shmid;
	char             *addr;
	pid_t             pid;
	struct shmid_ds   buf;

	failed = 0;

	shmid  = shmget(IPC_PRIVATE, MEMORY_SIZE, IPC_CREAT|0600);
	if (shmid == -1)
	{
		perror("shmget error");
		return -1;
	}

	pid = fork();
	if (pid == 0)
	{
		addr = (char*)shmat(shmid, NULL, 0);
		if ((int)addr == -1)
		{
			perror("shmat addr error");
			return -1;
		}

		strcpy(addr, "I am the child process\n");

		shmdt(addr);
		
		return 3;
	}
	else if (pid > 0)
	{
		wpid = waitpid(pid, &status, 0);
		if (wpid > 0 && WIFEXITED(status))
		{
			printf("child process return is %d\n", WEXITSTATUS(status));
		}

		failed = shmctl(shmid, IPC_STAT, &buf);
		if (failed == -1)
		{
			perror("chmctl error");
			return -1;
		}
		
		printf("shm_segsz =%d bytes\n", buf.shm_segsz);
        printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid);
        printf("chlid pid=%d, shm_lpid = %d \n", pid, buf.shm_lpid);
		printf("mode = %08x \n", buf.shm_perm.mode);

		addr = (char*)shmat(shmid, NULL, 0);
		if ((int)addr == -1)
		{
			perror("shmat addr error");
			return -1;
		}
	
		printf("%s", addr);
		shmdt(addr);
		shmctl(shmid, IPC_RMID, NULL);
	}	
	else 
	{
		perror("fork error");
		shmctl(shmid, IPC_RMID, NULL);
		return -1;
	}

	return 0;
}


未完

待續;

參考:

http://blog.csdn.net/guoping16/article/details/6584058



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章