我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來 進程之間通信的方式 管道 消息隊列 信號 信號量 共用存儲區 套接字(socket) 進程間通信(一)—管道傳送門:http://www.cnblogs.com/lenomirei/p ...
我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來
進程之間通信的方式
- 管道
- 消息隊列
- 信號
- 信號量
- 共用存儲區
- 套接字(socket)
進程間通信(一)—管道傳送門:http://www.cnblogs.com/lenomirei/p/5636339.html
進程間通信(三)—信號量傳送門:http://www.cnblogs.com/lenomirei/p/5649792.html
進程間通信(四)—共用存儲區傳送門:http://www.cnblogs.com/lenomirei/p/5651995.html
這次主要寫的是消息隊列,之前講過的管道和消息隊列在本質上就有很大的區別,管道是一個文件,而消息隊列是一個數據結構(類似於鏈表)。這說明瞭,管道文件是存放在磁碟上的,關機也會存在(尤其是命名管道更為顯而易見,你不刪除他他就擱那呆著),而消息隊列是存在於內核中的記憶體,顯而易見,關機就沒了。
更關鍵的是,記憶體他快呀,比磁碟I/O快多了,為啥要用那麼慢的管道。而且消息隊列是可以直接完成沒有親緣關係的進程之間的通信的。但是結構比起管道要複雜,用到了很多結構體。內容有些多。先寫一下和管道的主要區別,可以更直觀的進行對比
- 匿名管道是跟隨進程的,消息隊列是跟隨內核的,也就是說進程結束之後,匿名管道就死了,但是消息隊列還會存在(除非顯示調用函數銷毀)
- 管道是文件,存放在磁碟上,訪問速度慢,消息隊列是數據結構,存放在記憶體,訪問速度快
- 管道是數據流式存取,消息隊列是數據塊式存取
那麼從頭開始吧:
- 如何創建一個消息隊列
在C庫函數中有一個系統調用可以創建一個消息隊列,那就是msgget,跟這個函數有關的其他函數也會一併給出來
- 函數原型: int msgget(key_t key, int msgflg)
- 頭文件:#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
- 參數解析
- 第一個參數是一個標識數據結構唯一的key鍵值,可以給IPC_PRIVATE讓內核自動給,也可以自己調用ftok函數綁定一個
- 第二個參數是創建消息隊列的參數,有IPC_CREAT 和 IPC_EXCL
- 單獨使用IPC_CREAT,如果該消息隊列已經存在(就是該key_t對象已經拿去被創建過一個隊列了),打開該隊列並返回,如果不存在,就創建一個返回
- 單獨使用IPC_EXCL沒有意義
- 兩個參數一起使用(IPC_CREAT | IPC_EXCL),如果該隊列存在,出錯返回,如果不存在創建一個返回,也就是說這樣使用一定會獲得一個新隊列
- 返回值,成功返回標誌消息隊列的唯一的一個int,失敗返回-1
- 函數原型:key_t ftok(const char *pathname,int proj_id)
- 頭文件:#include <sys/types.h> #include <sys/ipc.h>
- 參數解析
- 沒啥好解析的,第一個參數就是給個路徑(目錄)就成了
- 第二個就是給個int值,沒啥特殊要求,ftok本質就是把這個proj_id和pathname綁定在一起罷了
相關函數就這麼多,到這裡就可以開心的創建一個新的消息隊列了,那麼怎麼看我們創建出來的消息隊列呢?怎麼銷毀他呢?
先用命令看一下,使用ipcs -q就可查看消息隊列的狀態,這裡我創建了一個消息隊列,下麵來銷毀它
使用ipcrm -q msqid就可以銷毀一個消息隊列,我已經把剛纔創建的消息隊列銷毀了
那麼如何通過C函數銷毀一個消息隊列?使用msgctl函數
- 函數原型: int msgctl(int msgid ,int cmd ,struct msgid_ds *buf)
- 頭文件:#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
- 參數解析
- 第一個參數就是創建好的標識msg的int變數
- 第二個參數有很多,這裡介紹兩個,先說主要的銷毀,IPC_RMID,設置了這個之後第三個參數就沒必要設置了給個0就成
- 第二個參數設置成IPC_SET的時候表示對消息隊列進行初始化,這時第三個參數就有用了,但是我這裡沒用到就不話費篇幅詳細解釋了,貼出結構看看就成
這裡提一下,消息隊列的一個缺點,這裡說到IPC_SET對消息隊列的初始化,就是說消息隊列的創建和初始化是分開的,這樣設計不是很好,因為有線程安全的問題,當一個線程創建了消息隊列還沒初始化,另一個線程直接開始執行訪問的操作就很尷尬了
這裡說錯了!我把信號量的缺點說成是消息隊列的了!現在取消了,真是對不起!!!
1 struct msginfo {
2 int msgpool; /* Size in kibibytes of buffer pool
3 used to hold message data;
4 unused within kernel */
5 int msgmap; /* Maximum number of entries in message
6 map; unused within kernel */
7 int msgmax; /* Maximum number of bytes that can be
8 written in a single message */
9 int msgmnb; /* Maximum number of bytes that can be
10 written to queue; used to initialize
11 msg_qbytes during queue creation
12 (msgget(2)) */
13 int msgmni; /* Maximum number of message queues */
14 int msgssz; /* Message segment size;
15 unused within kernel */
16 int msgtql; /* Maximum number of messages on all queues
17 in system; unused within kernel */
18 unsigned short int msgseg;
19 /* Maximum number of segments;
20 unused within kernel */
21 };
- 使用消息隊列
提到使用消息隊列無非就是消息的寫入和消息的讀出,這涉及兩個函數和一個結構體
先說發送函數
- 函數原型:int msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg)
- 頭文件:#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
- 參數解析
- 第一個還是消息隊列號
- 第二個是一個結構體的指針,這個結構體叫做msgbuf在內核中有(應該是有的。。。沒有就自己寫一個放程式里),必要的,這個就是傳輸數據的數據塊,沒了它就沒得傳了,可不能隨便設置為0
- 第三個就是傳輸的消息長度(一般你的數據有多長就寫多長,不是msgbuf對象的總大小)
- 第四個是傳送方式(無阻塞啊什麼的)的參數,我的程式沒有使用所以我設置為0了
在列接受函數之前必須把結構體放出來,不然下麵就懵逼了
1 struct msgbuf {
2 long mtype; /* message type, must be > 0 */
3 char mtext[1]; /* message data */
4 };
從mtype講起,因為隊列是好多進程(或者說是線程)都拿來用的,比如4個進程共用一個消息隊列,兩兩之間進行通信,就有必要標識哪兩個進程是一組的,哪塊數據是屬於你們組的,不能亂拿是不是,這個mtype就是用來標識的,但是一定要大於0,我一開始設置為0一直報參數錯誤,查了文檔才發現自己蠢了。。。
mtext存放的就是你要傳輸的數據了,怎麼大小隻有1?當然不能只有1,我自己寫的msgbuf就有1024長度。。。所以還是自己寫爽啊
可以說接受函數了
- 函數原型:ssize_t msgrcv(int msgid,const void *msgp,size_t msgsz,long msgtyp,int msgflg)
- 頭文件同msgsnd函數
- 參數解析
- 只用看標紅部分就成了,其他的和msgsnd一樣
- msgtyp就是用來表示我(當前進程)拿數據的時候,只拿(msgbuf對象中的mtype)和我傳入的msgtyp一樣的數據塊(msgbuf對象)
- 成功返回長度,失敗返回-1
事已至此,基本操作就說完了,廢話少說,show me the code
我的程式分為comm.h(公共頭文件) comm.c(封裝基本函數) server.c(簡易伺服器端) client.c(簡易客戶端)一共4個文件
完成了伺服器端和客戶端的簡單通信(回合制聊天(誤))
comm.h
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/msg.h>
4 #include <sys/types.h>
5 #include <sys/ipc.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <memory.h>
10
11
12 #define _PATH_NAME_ "/tmp"
13 #define _PROJ_ID_ 0x666
14 #define _SIZE_ 1024
15
16 static int comm_create_msg_set(int flags);
17 int create_msg_set();
18 int get_msg_set();
19 void destory_msg_set(int msg_id);
20 void send_msg(int msg_id,long msgtype,char * buf);
21 void receive_msg(int msg_id,long msgtype,char *buf);
22
23
24
25 struct msgbuf
26 {
27 long mtype;
28 char mtext[_SIZE_];
29 };
comm.c
1 #include "comm.h"
2 static int comm_create_msg_set(int flags)
3 {
4 key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
5 if(_key<0)
6 {
7 printf("%d:%s",errno,strerror(errno));
8 }
9 int msg_id=msgget(_key,flags);
10 if(msg_id<0)
11 {
12 printf("%d:%s",errno,strerror(errno));
13 }
14 return msg_id;
15 }
16
17
18 int get_msg_set()
19 {
20 key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
21 int flags=IPC_CREAT;
22 return comm_create_msg_set(flags);
23 }
24
25
26 int create_msg_set()
27 {
28 key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
29 int flags=IPC_CREAT | IPC_EXCL;
30 return comm_create_msg_set(flags);
31 }
32
33
34 void send_msg(int msg_id,long msgtype,char *buf)
35 {
36 memset(buf,'\0',strlen(buf)+1);
37 ssize_t _size=read(0,buf,_SIZE_);
38 if(_size>0)
39 {
40 buf[_size-1]='\0';
41 }
42
43 struct msgbuf _mbuf;
44 memset(&_mbuf,'\0',sizeof(struct msgbuf));
45 _mbuf.mtype=msgtype;
46 strcpy(_mbuf.mtext,buf);
47 if(msgsnd(msg_id,&_mbuf,_size,0)<0)
48 {
49 printf("send error,%d:%s",errno,strerror(errno));
50 }
51 }
52
53 void receive_msg(int msg_id , long msgtype ,char *buf)
54 {
55 struct msgbuf _mbuf;
56 memset(&_mbuf,'\0',sizeof(struct msgbuf));
57 _mbuf.mtype=0;
58 if(msgrcv(msg_id,&_mbuf,_SIZE_,msgtype,0)<0)
59 {
60 printf("recv error %d:%s",errno,strerror(errno));
61 }
62 strcpy(buf,_mbuf.mtext);
63 }
64
65 void destory_msg_set(int msg_id)
66 {
67 if(msgctl(msg_id,IPC_RMID,0)<0)
68 {
69 printf("%d:%s",errno,strerror(errno));
70 }
71 }
server.c
1 #include "comm.h"
2 long c_type=1;
3 long s_type=22;
4 int main()
5 {
6 int msg_id=create_msg_set();
7 char buf[_SIZE_];
8
9 while(1)
10 {
11 memset(buf,'\0',sizeof(buf));
12 receive_msg(msg_id,c_type,buf);
13 if(strcasecmp(buf,"quit")==0)
14 {
15 break;
16 }
17 printf("client # %s\n",buf);
18 printf("clent say done ! Please Input:");
19 fflush(stdout);
20 memset(buf,'\0',sizeof(buf));
21 send_msg(msg_id,c_type,buf);
22 }
23 destory_msg_set(msg_id);
24 return 0;
25 }
client.c
1 #include "comm.h"
2
3
4 long c_type=1;
5 long s_type=2;
6
7 int main()
8 {
9 int msg_id = get_msg_set();
10
11 char buf[_SIZE_];
12
13 while(1)
14 {
15 memset(buf,'\0',sizeof(buf));
16 send_msg(msg_id,c_type,buf);
17 if(strcasecmp(buf,"quit")==0)
18 {
19 break;
20 }
21 memset(buf,'\0',sizeof(buf));
22 receive_msg(msg_id,c_type,buf);
23 printf("server # %s\n",buf);
24 printf("server say done ! Please Input:");
25 fflush(stdout);
26 }
27
28 return 0;
29 }