我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來 進程之間通信的方式 管道 消息隊列 信號 信號量 共用存儲區 套接字(socket) 進程間通信(三)—信號量傳送門:http://www.cnblogs.com/lenomirei/ ...
我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來
進程之間通信的方式
- 管道
- 消息隊列
- 信號
- 信號量
- 共用存儲區
- 套接字(socket)
進程間通信(三)—信號量傳送門:http://www.cnblogs.com/lenomirei/p/5649792.html
進程間通信(二)—消息隊列傳送門:http://www.cnblogs.com/lenomirei/p/5642575.html
進程間通信(一)—管道傳送門:http://www.cnblogs.com/lenomirei/p/5636339.html
這篇主要記錄的是共用存儲區的相關操作,說是共用存儲區,其實就是共用記憶體,進程擁有的能互相通信存儲區也就有記憶體了吧
為什麼用共用存儲區進行通信?因為快!管道是文件,操作慢,消息隊列創建操作都有消耗所以慢,共用記憶體是要創建好兩個進程都可以直接對這塊記憶體進行操作,互相都是可見的。
為什麼用共用存儲區編寫程式?因為介面簡單!操作絕對比消息隊列簡單好多。
- 創建共用存儲區
雖然感覺很簡單的事情,但是還是要創建開闢一下,不然每個進程都用自己的地址空間映射到不同的物理地址,哪怕虛擬地址是一樣的,也是各自獨立的,互相不可見,聲明瞭這個共用存儲區之後,才可以往這個公共的區域映射(這樣才有用不是麽)。
- 函數原型:int shmget(key_t key, size_t size, int shmflg);
- 頭文件:#include <sys/ipc.h> #include <sys/shm.h>
- 參數解析
- key參數通過ftok函數的返回值取得,或者傳入IPC_PRIVATE由操作系統自動分配
- size表示你要開闢多大的共用存儲空間,PS:分配空間最終會變成分配物理空間,是通過分配整頁的方式實現的,Linux系統下一頁大小是4KB=4096B,小於4096B則分配一頁,size傳入4097則分配兩頁
- shmflg有IPC_CREAT 和 IPC_EXCL
調用這個函數就可以開闢一個共用存儲區了,可以通過ipcs -m查看當前共用存儲區狀態
第一個鍵值就是傳入的key,shmid標識唯一共用記憶體段,擁有者許可權位元組就不多說了額,這個nattch是指當前有多少個進程連接到該共用存儲區
nattch:只創建共用存儲區是不夠的,你需要把它和進程鏈接,才能讓進程的地址空間中的一段地址映射到共用記憶體段上。
- 共用存儲區的連接
這就講一下用什麼函數鏈接共用存儲區,需要註意的是,兩個進程都需要鏈接才可以,創建共用存儲區的進程不會自動連接,也需要調用鏈接函數
- 函數原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
- 頭文件:#include <sys/types.h> #include <sys.shm.h>
- 參數解析
- shmid表示共用存儲區的標識
- 第二個參數表示共用存儲區的開始地址,如果不是對記憶體十分瞭解建議交給操作系統去做,設置為0
- shmflg可以設置當前進程的讀寫許可權,SEM_RDONLY之類的,預設0為讀寫均可,測試程式我給了0
每有一個進程調用這個函數,就會使nattch增加1,返回值因為是個空類型的指針常常需要強制轉換
- 共用存儲區的鏈接的斷開
鏈接使用完之後就斷開是個好習慣,而且對銷毀共用存儲空間也好
- 函數原型:int shmdt(const void *shmaddr);
- 頭文件:#include <sys/types.h> #include <sys.shm.h>
- 參數解析刪除共用存儲區
- 就是要刪除的共用存儲區的起始地址,因為是空類型的指針,沒必要轉換,直接給來都能刪除
- 刪除共用存儲區
最後還是要用到shmctl函數來刪除共用存儲區
- 函數原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 頭文件:#include <sys/ipc.h> #include <sys/shm.h>
- 參數解析
- shmid表示共用存儲區的標識
- cmd給出IPC_RMID表示刪除
- 因為第二個參數設置為IPC_RMID表示刪除,第三個參數沒用了,第三個參數直接給NULL(0)
事已至此,基本操作就說完了,廢話少說,show me the code
我的程式分為comm.h(公共頭文件) comm.c(封裝基本函數) server.c(簡易伺服器端) 一共3個文件
功能主要實現了簡單的字元串共用?父進程寫入,子進程列印,就這麼簡單
結果圖並看不出什麼鬼
comm.h
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <string.h>
4 #include <sys/ipc.h>
5 #include <unistd.h>
6 #include <sys/shm.h>
7 #include <errno.h>
8 #include <stdlib.h>
9
10
11 #define _PATH_NAME_ "/tmp"
12 #define _PROJ_ID_ 0x6666
13
14
15
16 static int comm_create_ssm(int flags,size_t size);
17 int create_shm(size_t size);
18 int get_shm();
19 char *shm_at(int shm_id);
20 void destory_shm(int shm_id);
21 int shm_dt(char *addr);
comm.c
1 #include "comm.h"
2
3
4 static int comm_create_shm(int flags,size_t size)
5 {
6 key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
7 if(_key<0)
8 {
9 printf("%d:%s",errno,strerror(errno));
10 }
11 int shm_id;
12 if((shm_id=shmget(_key,size,flags))<0)
13 {
14 printf("shmget error,%d:%s",errno,strerror(errno));
15 }
16 return shm_id;
17 }
18
19
20 int create_shm(size_t size)
21 {
22 int flags=IPC_CREAT |IPC_EXCL;
23 return comm_create_shm(flags,size);
24 }
25
26 int get_shm()
27 {
28 int flags=IPC_CREAT;
29 return comm_create_shm(flags,0);
30 }
31
32 char *shm_at(int shm_id)
33 {
34 return (char *)shmat(shm_id,NULL,0);
35 }
36 int shm_dt(char *addr)
37 {
38 return shmdt(addr);
39 }
40
41 void destory_shm(int shm_id)
42 {
43 shmctl(shm_id,IPC_RMID,0);
44 }
server.c
1 #include "comm.h"
2
3
4
5 int main()
6 {
7 int pid=fork();
8 if(pid>0)
9 {
10 //father
11 int shm_id=create_shm(4096);
12 char *buf=shm_at(shm_id);
13 int i=0;
14 while(i<4096)
15 {
16 sleep(1);
17 buf[i]='A';
18 i++;
19 buf[i]='\0';
20 }
21 }
22 else
23 {
24 //child
25 int shm_id=get_shm();
26 char *buf=shm_at(shm_id);
27 while(1)
28 {
29 sleep(1);
30 printf("%s\n",buf);
31 }
32 }
33 return 0;
34 }