IPC進程間通信---消息隊列


消息隊列

   消息隊列:消息隊列是一個存放在內核中的消息鏈表,每個消息隊列由消息隊列標識符標識。與管道不同的是消息隊

存放在內核中,只有在內核重啟(即操作系統重啟)或者顯式地刪除一個消息隊列時,該消息隊列才會被真正的刪除。

    Linux內核中,每個消息隊列都維護一個結構體msqid_ds,此結構體保存着消息隊列當前的狀態信息。該結構定義在

件linux/msg.h中,具體如下:

struct msqid_ds
{
   struct_ipc_perm  msg_perm;   //是一個ipc_perm的結構,保存了消息隊列的存取權限,以及隊列的用戶ID、組ID等信息
   struct_msg  *msg_first;   //指向隊列中的第一條消息
   struct_msg  *msg_last;   //指向隊列中的最后一條消息
   __kernel_t time_t  msg_stime;   //向消息隊列發送最后一條信息的時間
   __kernel_t time_t  msg_rtime;   //從消息隊列取最后一條信息的時間
   __kernel_t time_t  msg_ctime;  //最后一次變更消息隊列的時間
   unsigned long  msg_lcbytes;
   unsigned long  msg_lqbytes;
   unsigned short  msg_cbytes;    //消息隊列中所有消息占的字節數
   unsigned short  msg_qnum;     //消息隊列中消息的數目
   unsigned short  msg_qbytes;   //消息隊列的最大字節數
   __kernel_ipc_pid_t  msg_lspid;  //向消息隊列發送最后一條消息的進程ID
   __kernel_ipc_pid_t  msg_lrpid;   //從消息隊列讀取最后一條信息的進程ID
};

   消息隊列是隨着內核的存在而存在的,每個消息隊列在系統范圍內對應惟一的鍵值。要獲得一個消息隊列的描述符,

提供該消息隊列的鍵值即可,該鍵值通常由函數ftok返回,該函數原形為:

   #include <sys/ipc.h>

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

   創建一個新的消息隊列或訪問一個已存在的消息隊列前需要使用ftok函數得到key值,下面是ftok函數的包裹函數:

key_t Ftok(const char *pathname,int proj_id)
{
  key_t key= ftok(pathname,proj_id);
  if(key== -1)
  {
    perror("ftok.");
    exit(1);
  }
  return key;
}

   消息隊列的創建或打開:

   使用函數msgget進行消息隊列的創建或打開。該函數定義在頭文件<sys/msg.h>中,該函數的原形為:

   #include <sys/ipc.h>
   #include <sys/msg.h>
   int semget(key_t key,int msgflg);

   該函數如果調用成功則返回一個消息隊列的描述符,否則返回-1。函數的第一個參數為ftok()函數得到的鍵值第二

個參msgflg是一個標志參數,可以取如下值:

  • IPC_CREAT:如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列,返回該

息隊列的描述符。

  • IPC_EXCL:和IPC_CREAT一起使用,如果對應鍵值的消息隊列已經存在,則出錯,返回-1。

      消息隊列的讀寫:

   創建了一個消息隊列后,就可以對消息隊列進行讀寫了,函數msgsnd用於向消息隊列發送寫數據,該函數定義在頭文

<sys/msg.h>中,函數原形為:

   int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);

   函數中,參數msgqid為函數向msgid標識的消息隊列發送一個消息(msqid是由msgget返回的標識符);參數msgp指向

送的消息是一個結構指針,該結構如下所示;參數msgsz為要發送的消息的大小,不包含消息類型占用的4個字節;參數

msgflg為操作標志位,可以設置為0或者IPC_NOWAIT,如果msgflg為0,則當消息隊列已滿時,msgsnd將會阻塞,直到消息

可以寫消息隊列,如果msgflg為IPC_NOWAIT,當消息隊列已滿的時候,msgsnd函數將不等待立即返回。msgsnd函數成功

返回0,失敗返回-1。

struct msgbuf
{
   long mtype;           //代表消息類型,給消息指定類型,可以使得消息在一個隊列中重復使用
   char mtext[1];        //消息內容
};

    消息隊列中放入數據后,其它進程就可以讀取其中的消息了。讀取消息的系統調用為msgrcv()函數,該函數定義在頭

<sys/msg.h>中,函數原形為:

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

    函數中,參數msqid為消息隊列描述符(由msgget返回的標識符);參數msgp同上面的參數相同;參數msgsz為消息緩

沖區的大小;參數msgtyp為請求讀取的消息類型;參數msgflg為操作標志位,msgflg可以為IPC_NOWAIT、IPC_EXCEPT、IP

C_NOERROR三個常量,IPC_NOWAIT:如果沒有滿足條件的消息,調用立即返回,此時錯誤碼為ENOMSG.IPC_EXCEPT:與msgt

yp配合使用,返回隊列中第一個類型不為msgtyp的消息.IPC_NOERROR:如果隊列中滿足條件的消息內容大於所請求的msgs

z字節,則把該消息截斷,截斷部分將被丟棄。msgrcv函數成功會返回讀出消息的實際字節數,否則返回-1。

    消息隊列的控制:

    消息隊列的屬性保存在系統維護的數據結構msqid_ds中,用戶可以通過函數msgctl獲取或設置消息隊列的屬性。msg

ctl在頭文件<sys/msg.h>中,函數原形為:

    int msgctl(int msqid,int cmd,struct msqid_ds *buf);

    函數中,第一個參數為由msgget返回的標識符;第二個參數為執行的cmd操作;第三個參數為上面已經定義的msqid_d

s構體類型。系統中定義了3種cmd操作:IPC_STAT、IPC_SET、IPC_RMID,它們的含義如下:

  • IPC_STAT:該命令用來獲取消息隊列對應的msqid_ds數據結構,並將其保存到buf指向的地址空間。
  • IPC_SET:該命令用來設置消息隊列的屬性,要設置的屬性存儲在buf中,可設置的屬性包括:msg_perm.uid、msg_pe

rm.gid、msg_perm.mode和msg_qbytes。

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

   下面是使用消息隊列進行發送消息的例子:

// myipc.h
#pragma once

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

key_t Ftok(const char *pathname,int proj_id)
{
    key_t key= ftok(pathname,proj_id);
    if(key== -1)
    {
        perror("ftok.");
        exit(1);
    }
    return key;
}
//utili.h
#pragma once

#define MSG_BUFFER_LEN 256
#define SERVER_SEND_FLAG 100
#define SERVER_RECV_FLAG 200

#define CLIENT_SEND_FLAG 200
#define CLIENT_RECV_FLAG 100

typedef struct Msg
{
    long msg_type;
    char msg_text[MSG_BUFFER_LEN];
}Msg;
//server.c
#include "myipc.h"
#include "utili.h"

int main(int argc,char *argv[])
{
    key_t msg_key= Ftok(argv[1],atoi(argv[2]));
    int msg_id= msgget(msg_key,IPC_CREAT|0755);
    if(msg_id== -1)
    {
        perror("msgget");
        exit(1);
    }
    Msg msg;
    while(1)
    {
        printf("Ser:");
        scanf("%s",msg.msg_text);
        msg.msg_type= SERVER_SEND_FLAG;
        msgsnd(msg_id,&msg,strlen(msg.msg_text)+ 1,0);

        msgrcv(msg_id,&msg,MSG_BUFFER_LEN,SERVER_RECV_FLAG,0);
        printf("Cli:%s\n",msg.msg_text);
    }
    msgctl(msg_id,IPC_RMID,NULL);
    return 0;
}
//client.c
#include "myipc.h"
#include "utili.h"

int main(int argc,char *argv[])
{
    key_t msg_key= Ftok(argv[1],atoi(argv[2]));
    int msg_id= msgget(msg_key,0);
    Msg msg;
    while(1)
    {
        msgrcv(msg_id,&msg,MSG_BUFFER_LEN,CLIENT_RECV_FLAG,0);
        printf("Ser:%s\n",msg.msg_text);
        printf("Cli:");
        scanf("%s",msg.msg_text);
        msg.msg_type= CLIENT_SEND_FLAG;
        msgsnd(msg_id,&msg,strlen(msg.msg_text)+ 1,0);
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM