共享內存可以說是最有用的進程間通信方式,也是最快的ipC形式。是針對其他通信機制運行效率較低而設計的。兩個不同進程A、B共享內存的意思是,同一塊物理內存被映射到進程A、B各自的進程地址空間。進程A可以即時看到進程B對共享內存中數(shù)據(jù)的更新,反之亦然。由于多個進程共享同一塊內存區(qū)域,必然需要某種同步機制,互斥鎖和信號量都可以。
采用共享內存通信的一個顯而易見的好處是效率高,因為進程可以直接讀寫內存,而不需要任何數(shù)據(jù)的拷貝。對于像管道和消息隊列等通信方式,則需要在內核和用戶空間進行四次的數(shù)據(jù)拷貝,而共享內存則只拷貝兩次數(shù)據(jù)[1]:一次從輸入文件到共享內存區(qū),另一次從共享內存區(qū)到輸出文件。實際上,進程之間在共享內存時,并不總是讀寫少量數(shù)據(jù)后就解除映射,有新的通信時,再重新建立共享內存區(qū)域。而是保持共享區(qū)域,直到通信完畢為止,這樣,數(shù)據(jù)內容一直保存在共享內存中,并沒有寫回文件。共享內存中的內容往往是在解除映射時才寫回文件的。因此,采用共享內存的通信方式效率是非常高的。
系統(tǒng)V共享內存原理進程間需要共享的數(shù)據(jù)被放在一個叫做IPC共享內存區(qū)域的地方,所有需要訪問該共享區(qū)域的進程都要把該共享區(qū)域映射到本進程的地址空間中去。系統(tǒng)V共享內存通過shmget獲得或創(chuàng)建一個IPC共享內存區(qū)域,并返回相應的標識符。內核在保證shmget獲得或創(chuàng)建一個共享內存區(qū),初始化該共享內存區(qū)相應的shmid_kernel結構體的同時,還將在特殊文件系統(tǒng)shm中,創(chuàng)建并打開一個同名文件,并在內存中建立起該文件的相應dentry及inode結構,新打開的文件不屬于任何一個進程(任何進程都可以訪問該共享內存區(qū))。所有這一切都是系統(tǒng)調用shmget完成的。
注:每一個共享內存區(qū)都有一個控制結構struct shmid_kernel,shmid_kernel是共享內存區(qū)域中非常重要的一個數(shù)據(jù)結構,它是存儲管理和文件系統(tǒng)結合起來的橋梁,定義如下:
struct shmid_kernel /* PRivate to the kernel */{ struct kern_ipc_perm shm_perm; /* Operation permission structure */ struct file *shm_file; /* pointer in kernel */ unsigned long shm_nattch; /* number of current attaches */ unsigned long shm_segsz; /* size of segment in bytes */ time_t shm_atim; /* last-attach time */ time_t shm_dtim; /* last-detach time */ time_t shm_ctim; /* last-change time */ pid_t shm_cprid; /* pid of creator */ pid_t shm_lprid; /* pid of last shmop() */};
正如消息隊列和信號燈一樣,內核通過數(shù)據(jù)結構struct ipc_ids shm_ids維護系統(tǒng)中的所有共享內存區(qū)域。上圖中的shm_ids.entries變量指向一個ipc_id結構數(shù)組,而每個ipc_id結構數(shù)組中有個指向kern_ipc_perm結構的指針。到這里讀者應該很熟悉了,對于系統(tǒng)V共享內存區(qū)來說,kern_ipc_perm的宿主是 shmid_kernel結構,shmid_kernel是用來描述一個共享內存區(qū)域的,這樣內核就能夠控制系統(tǒng)中所有的共享區(qū)域。同時,在 shmid_kernel結構的file類型指針shm_file指向文件系統(tǒng)shm中相應的文件,這樣,共享內存區(qū)域就與shm文件系統(tǒng)中的文件對應起來。
在創(chuàng)建了一個共享內存區(qū)域后,還要將它映射到進程地址空間,系統(tǒng)調用shmat()完成此項功能。由于在調用shmget()時,已經(jīng)創(chuàng)建了文件系統(tǒng) shm中的一個同名文件與共享內存區(qū)域相對應,因此,調用shmat()的過程相當于映射文件系統(tǒng)shm中的同名文件過程,原理與mmap()大同小異。
系統(tǒng)V共享內存API頭文件: #include <sys/ipc.h> #include <sys/shm.h>
shmget()用來獲得共享內存區(qū)域的ID,如果不存在指定的共享區(qū)域就創(chuàng)建相應的區(qū)域。shmat()把共享內存區(qū)域映射到調用進程的地址空間中去,這樣,進程就可以方便地對共享區(qū)域進行訪問操作。shmdt()調用用來解除進程對共享內存區(qū)域的映射。shmctl實現(xiàn)對共享內存區(qū)域的控制操作。
系統(tǒng)V共享內存限制在/proc/sys/kernel/目錄下,記錄著系統(tǒng)V共享內存的一下限制,如一個共享內存區(qū)的最大字節(jié)數(shù)shmmax,系統(tǒng)范圍內最大共享內存區(qū)標識符數(shù)shmmni等,可以手工對其調整,但不推薦這樣做。
系統(tǒng)V共享內存范例/***** testwrite.c *******/#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>typedef struct{ char name[4]; int age;} people;main(int argc, char** argv){ int shm_id,i; key_t key; char temp; people *p_map; char* name = "/dev/shm/myshm2"; key = ftok(name,0); if(key==-1) perror("ftok error"); shm_id=shmget(key,4096,IPC_CREAT); if(shm_id==-1) { perror("shmget error"); return; } p_map=(people*)shmat(shm_id,NULL,0); temp='a'; for(i = 0;i<10;i++) { temp+=1; memcpy((*(p_map+i)).name,&temp,1); (*(p_map+i)).age=20+i; } if(shmdt(p_map)==-1) perror(" detach error ");}/********** testread.c ************/#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>typedef struct{ char name[4]; int age;} people;main(int argc, char** argv){ int shm_id,i; key_t key; people *p_map; char* name = "/dev/shm/myshm2"; key = ftok(name,0); if(key == -1) perror("ftok error"); shm_id = shmget(key,4096,IPC_CREAT); if(shm_id == -1) { perror("shmget error"); return; } p_map = (people*)shmat(shm_id,NULL,0); for(i = 0;i<10;i++) { printf( "name:%s/n",(*(p_map+i)).name ); printf( "age %d/n",(*(p_map+i)).age ); } if(shmdt(p_map) == -1) perror(" detach error ");}View Code
新聞熱點
疑難解答