linux進程間通訊之消息隊列

咱們已經知道進程通訊的方式是有多種的,在上一篇博客中講述了經過管道實現簡單的進程間通訊,那麼接下來咱們看看與之相似的另外一種方式,經過消息隊列來實現進程間通訊。linux

什麼是消息隊列shell

消息隊列提供了一種由一個進程向另外一個進程發送塊數據的方法。另外,每個數據塊被看做有一個類型,而接收進程能夠獨立接收具備不一樣類型的數據塊。消息隊列的好處在於咱們幾乎能夠徹底避免同步問題,而且能夠經過發送消息屏蔽有名管道的問題。更好的是,咱們可使用某些緊急方式發送消息。壞處在於,與管道相似,在每個數據塊上有一個最大尺寸限制,同時在系統中全部消息隊列上的塊尺寸上也有一個最大尺寸限制。數據結構

 

如何使用消息隊列ide

咱們先來了解一下System V IPC函數

System V IPC工具

    System V IPC指的是AT&T在System V.2發行版中引入的三種進程間通訊工具:(1)信號量,用來管理對共享資源的訪問 (2)共享內存,用來高效地實現進程間的數據共享 (3)消息隊列,用來實現進程間數據的傳遞。咱們把這三種工具統稱爲System V IPC的對象,每一個對象都具備一個惟一的IPC標識符(identifier)。要保證不一樣的進程可以獲取同一個IPC對象,必須提供一個IPC關鍵字(IPC key),內核負責把IPC關鍵字轉換成IPC標識符。   ui

    System V IPC具備類似的語法,通常操做以下:spa

(1)選擇IPC關鍵字,可使用以下三種方式:3d

       a)IPC_PRIVATE。由內核負責選擇一個關鍵字而後生成一個IPC對象並把IPC標識符直接傳遞給另外一個進程。
       b)直接選擇一個關鍵字。
       c)使用ftok()函數生成一個關鍵字。指針

 (2)使用semget()/shmget()/msgget()函數根據IPC關鍵字key和一個標誌flag建立或訪問IPC對象。若是key是IPC_PRIVATE;或者key還沒有與已經存在的IPC對象相關聯且flag中包含IPC_CREAT標誌,那麼就會建立一個全新的IPC對象。

 (3)使用semctl()/shmctl()/msgctl()函數修改IPC對象的屬性。

 (4)使用semctl()/shmctl()/msgctl()函數和IPC_RMID標誌銷燬IPC實例。

    System V IPC爲每一個IPC對象設置了一個ipc_perm結構體並在建立IPC對象的時候進行初始化。這個結構體中定義了IPC對象的訪問權限和全部者:

struct ipc_perm{
       uid_t uid;   //全部者的用戶id
       gid_t gid;   //全部者的組id
       uid_t cuid;  //建立者的用戶id
       gid_t cgid;  //建立者的組id
       mode_t mode; //訪問模式
       …

};
    

能夠在/usr/include/linux/ipc.h目錄進行查看。

 

shell中管理IPC對象的命令是ipcs、ipcmk和ipcrm。

不少人看到這裏估計就看不下去了,我也是,怎麼這麼麻煩啊,這麼多相關的東西。別急,咱們再來繼續分析,咱們要使用消息隊列、信號量和共享內存就必須知道上面這些,具體怎樣實際的應用呢,咱們繼續來看。

在上面提到了IPC關鍵字,這裏咱們採用ftok函數來生成關鍵字,下面是一些在使用中會涉及的函數

》ftok函數
函數ftok把一個已存在的路徑名和一個整數標識得轉換成一個key_t值,稱爲IPC鍵:

# include <sys/types.h> 


# include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);

參數介紹:*pathname, 路徑名 proj_id,項目號,不爲0

》int msgget(key_t key, int msgflg); 建立消息隊列

參數介紹:

>key,IPC關鍵字

>msgflag,IPC_CREAT 若是IPC不存在,則建立一個IPC資源,不然打開操做。 IPC_EXCL:只有在共享內存不存在的時候,新的共享內存才創建,不然就產生錯誤。
若是單獨使用IPC_CREAT, XXXget()函數要麼返回一個已經存在的共享內存的操做符,要
麼返回一個新建的共享內存的標識符。
若是將IPC_CREAT和IPC_EXCL標誌一塊兒使用, XXXget()將返回一個新建的IPC標識符
;若是該IPC資源已存在,或者返回-1。 IPC_EXEL標誌原本並無太大的意義,可是和IPC_CREAT標誌一塊兒使用能夠來保證

》向隊列讀/寫消息

msgrcv從隊列中取⽤消息:

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msgsnd將數據放到消息隊列中:

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
參數: msqid:消息隊列的標識碼 msgp:指向消息緩衝區的指針,此位置⽤來暫時存儲發送和接收的消息,是一個用戶可定義的通用結構,形態以下:

struct msgstru

{ long mtype; //大於於0
char mtext[用戶指定大小]; };

msgsz:消息的大小。

msgtyp:從消息隊列內讀取的消息形態。若是值爲零,則表示消息隊列中的全部消息都會被讀取。

msgflg:用來指明核心程序在隊列沒有數據的狀況下所應採起的行動。若是msgflg和常數IPC_NOWAIT合用,則在msgsnd()執行時如果消息隊列已滿,則msgsnd()將不會阻塞,而會當即返回-1,若是執行的是msgrcv(),則在消息隊列呈空時,不作等待立刻返回-1,並設定錯誤碼爲ENOMSG。當msgflg爲0時, msgsnd()及msgrcv()在隊列呈滿或呈空的情形時,採起阻塞等待的處理模式。

》設置消息隊列屬性
原型: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
參數: msgctl 系統調用對 msgqid 標識的消息隊列執行 cmd 操做,系統定義了 3 種 cmd 操做: IPC_STAT , IPC_SET , IPC_RMID


IPC_STAT : 該命令用來獲取消息隊列對應的 msqid_ds 數據結構,並將其保存到 buf 指
定的地址空間。 


IPC_SET : 該命令用來設置消息隊列的屬性,要設置的屬性存儲在buf中。

IPC_RMID : 從內核中刪除 msqid 標識的消息隊列

示例代碼:

 comm.h:定義和函數聲明

 1 #pragma once
 2 #include<stdio.h>
 3 #include<sys/ipc.h>
 4 #include<sys/msg.h>
 5 #include<stdlib.h>
 6 #include<string.h>
 7 #include<unistd.h>
 8 #include <sys/types.h>
 9 #include<time.h>
10 extern const  int ser_send_type;//server
11 extern const int cli_send_type;//client
12 #define _PATH_NAME_ "/tmp"
13 #define _PROJ_ID_ 0x111
14 #define _SIZE_ 512
15  
16 int create_msg_queue();
17 int get_msg_queue();
18 int recv_msg(int msg_id,int type,char* out);
19   int send_mag(int msg_id,int type,char* in);
20   
21   typedef struct msgbuf
22   {
23        long mtype;
24        char mtext[_SIZE_];
25   } msg_t;
View Code

comm.c:對server和client須要調用的一些函數的一些封裝

 1 #include"comm.h"
 2  const int ser_send_type=1;//server
 3  const int cli_send_type=2;//client
 4  int comm_msg_queue(int flag)
 5  {
 6       key_t key = ftok(_PATH_NAME_,_PROJ_ID_);
 7       if(key == -1)
 8       {
 9            perror("ftok");
10            return -2;
11       }
12       int msg_id=msgget(key,flag);
13       if(msg_id<0)
14           perror("msgget");
15       return msg_id;
16 }
17 int create_msg_queue()
18 {
19       int flag=IPC_CREAT|IPC_EXCL|0664;
20      return comm_msg_queue(flag);
21  }
22  int get_msg_queue()
23  {
24      int flag=IPC_CREAT;
25      return comm_msg_queue(flag);
26 }
27 
28 int recv_msg(int msg_id,int type,char* out)
29 {
30     msg_t msg;
31     msg.mtype=type;
32     size_t ret=msgrcv(msg_id,&msg,sizeof(msg.mtext),type,0);
33     if(ret<0)
34     {
35          perror("msgrcv");
36          return 1;
37     }
38     strcpy(out,msg.mtext);
39     return 0;
40 }
41 int send_msg(int msg_id,int type,char* msg_in)
42 {
43      msg_t msg;
44      msg.mtype=type;
45      strncpy(msg.mtext,msg_in, strlen(msg_in)+1);
46      size_t  ret=msgsnd(msg_id,&msg,sizeof(msg.mtext),0);
47      if(ret<0)
48      {
49           perror("msgsnd");
50                return 2;
51       }
52       return 0;
53  }
54  int destroy_msg(int msg_id)
55  {
56       msgctl(msg_id,IPC_RMID,NULL);
57  }
View Code

server.c:

 1 #include "comm.h"
 2 
 3 int main()
 4 {
 5      int msg_id=create_msg_queue();
 6      char buf[_SIZE_];
 7      while(1)
 8      {
 9           memset(buf,0,sizeof(buf));
10           printf("please enter >:");
11           fgets(buf,sizeof(buf)-1,stdin);
12           if( strncmp(buf,"quit",4)==0)
13           {
14                printf("server bye!\n");
15                break;
16           }
17            send_msg(msg_id,ser_send_type,buf);
18            memset(buf,0,sizeof(buf));
19            recv_msg(msg_id,cli_send_type,buf);
20            printf("client:%s",buf);
21       }
22       destroy_msg(msg_id);
23       return 0;
24 }

client.c:

 1 #include"comm.h"
 2  int main()
 3  {
 4       int msg_id=get_msg_queue();
 5       if(msg_id < 0){
 6            return 1;
 7       }    
 8     char buf[_SIZE_];
 9     while(1)
10     {
11          recv_msg(msg_id,ser_send_type,buf);
12          printf("server:%s",buf);
13          memset(buf,0,sizeof(buf));
14          
15          printf("please enter:");
16          fgets(buf,sizeof(buf)-1,stdin);
17          if(strncmp(buf,"quit",4)==0)
18          {
19               printf("client bye!\n");
20               break;
21          }    
22          send_msg(msg_id,cli_send_type,buf);
23       }    
24       destroy_msg(msg_id);
25       return 0;
26       
27  }  

  

最後再貼出makefile文件:

.PHONY: all
all:server client server:server.o comm.o
     gcc -o $@ $^
client:client.o comm.o
     gcc -o $@ $^
%.o:%.c
     gcc -c $<
.PHONY:clean
clean:
     rm -f server client *.o

  

  最後咱們再打開一個終端,一個運行server,一個運行client。看一下咱們高大上的結果:

相關文章
相關標籤/搜索