linux系統IPC通信-共享內存

很久以前就想研究一下內核源碼,今天開始看看共享內存是如何實現的.
以前就瞭解幾個函數.

共享內存使用的函數

#include <sys/ipc.h> 
#include <sys/shm.h> 
int shmget(key_t key, size_t size, int shmflg); 
void *shmat(int shm_id, const void *shm_addr, int shmflg); 
int shmdt(const void *shm_addr); int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

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

size: 是要建立共享內存的長度。所有的內存分配操作都是以頁爲單位的。所以如果一段進程只申請一塊只有一個字節的內存,內存也會分配整整一頁(在i386機器中一頁的缺省大小PACE_SIZE=4096字節)這樣,新創建的共享內存的大小實際上是從size這個參數調整而來的頁面大小。即如果size爲1至4096,則實際申請到的共享內存大小爲4K(一頁);4097到8192,則實際申請到的共享內存大小爲8K(兩頁),依此類推。
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)是全局讀取和寫入許可。
需要注意的是,使用參數要加上 | 0666 作爲校驗,在有些Linux系統中,如果不加此校驗,則不能順利獲取共享空間的值(如Ubuntu)。此外,有兩個常用參數,一般要同時出現,他們是:S_IRUSH | S_IWUSR 。由於這兩個參數非常常用,程序員一般做這樣的操作
#define PERM S_IRUSR | S_IWUSR | IPC_CREAT
  這樣一來,第三個參數就可以直接用PERM來表示了!

返回值
成功返回共享內存的標識符;不成功返回-1,errno儲存錯誤原因。

   EINVAL        參數size小於SHMMIN或大於SHMMAX。
    EEXIST        預建立key所致的共享內存,但已經存在。
    EIDRM         參數key所致的共享內存已經刪除。
    ENOSPC        超過了系統允許建立的共享內存的最大值(SHMALL )。
    ENOENT        參數key所指的共享內存不存在,參數shmflg也未設IPC_CREAT位。
    EACCES        沒有權限。
    ENOMEM        核心內存不足。NOTE: 權限標誌對共享內存非常有用,因爲它允許一個進程創建的共享內
    存可以被共享內存的創建者所擁有的進程寫入,同時其它用戶創建的進程只能讀取共享內存。

我們可以利用這個功能來提供一種有效的對數據進行只讀訪問的方法,通過將數據放共享內存並設置它的權限,就可以避免數據被其他用戶修改。
創建成功,則返回一個非負整數,即共享內存標識;如果失敗,則返回-1.

shmget這個函數要調用

shm.c
SYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg)
{
	struct ipc_namespace *ns;
	struct ipc_ops shm_ops;
	struct ipc_params shm_params;
	ns = current->nsproxy->ipc_ns;
	...
	return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params);
}

util.c
int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
	struct ipc_ops *ops, struct ipc_params *params)
{
	if (params->key == IPC_PRIVATE)
		return ipcget_new(ns, ids, ops, params);  #create a new ipc object
	else
		return ipcget_public(ns, ids, ops, params); #get an ipc object or create  a new one
}

static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, struct ipc_ops *ops, struct ipc_params *params)
{
	struct kern_ipc_perm *ipcp;
	int flg = params->flg;
	int err;
retry:
	err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL); //爲idr預留資源
	down_write( &ids->rw_mutex);
	ipcp = ipc_findkey( ids, params->key); //查找key是否被使用
	if (ipcp==NULL) {
		```
		if (!err)
			err = ipc_check_perms(ipcp, ops, params); #check security and permissions for on IPC
	}
	up_write(&ids->rw_mutex);
}

shmid_ds數據結構表示每個新建的共享內存。當shmget()創建了一塊新的共享內存後,返回一個可以用於引用該共享內存的shmid_ds數據結構的標識符。
include/linux/shm.h
struct shmid_ds {
      struct ipc_perm    shm_perm;      /* operation perms */
      int                shm_segsz;     /* size of segment (bytes) */
      __kernel_time_t    shm_atime;     /* last attach time */
      __kernel_time_t    shm_dtime;     /* last detach time */
      __kernel_time_t    shm_ctime;     /* last change time */
      __kernel_ipc_pid_t shm_cpid;      /* pid of creator */
      __kernel_ipc_pid_t shm_lpid;      /* pid of last operator */
      unsigned short     shm_nattch;    /* no. of current attaches */
      unsigned short     shm_unused;    /* compatibility */
      void               *shm_unused2; /* ditto - used by DIPC */
      void               *shm_unused3; /* unused */
  };

代碼太多了,其他的自己看吧,

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