進程間通信——消息隊列


前言


進程間需要進程通信進行數據傳輸和資源共享,另外一個進程在很多時候需要向另外的一個進程通知事件,有時候進程之間存在關係,需要去控制另外的一個進程,所以進程通信是很需要的。

進程間通信的第一種就是消息隊列

什麼是消息隊列


消息隊列是消息的鏈表,存放在內核當中,由消息隊列標識符標識,消息隊列提供一種進程之間數據塊傳送的方法,每個數據塊都被認爲是一種類型。每個進程都有一個與之相關聯的消息隊列,其功能就類似於一個信箱

注意


因爲管道是隨進程的,進程結束管道生命週期也就結束,而對於消息隊列來說,是隨操作系統的,就算進程突出,不去手動的釋放消息隊列,消息隊列依然是存在的。
我們可以使用ipcs -q進行查看操作系統的消息隊列,使用ipcrm -q msgid進行銷燬消息隊列。

消息隊列的創建


使用msgget函數,我們可以創建或者訪問一個消息隊列,
在創建消息隊列之前我們依然要用ftok得到一個key標識符。

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

這裏的pathname是路徑,proj_id用來指定數字,爲生成唯一的key提供。該函數把從pathname導出的信息與id的低序8位組合成⼀個整數IPC鍵。

然後創建隊列。

int msgget(key_t key, int msgflg);

msgflg使用IPC_CREATE和IPC_EXCL進行創建消息隊列,當msgflg使用IPC_CREATE和0的時候,這個時候使用消息隊列。

函數返回值返回一個msgid,失敗返回-1。

發送消息和接受消息


使用消息隊列當然涉及到發送消息和接受消息,msgsnd函數和msgrcv函數提供了這兩個功能,

首先來看msgsnd函數

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

在這裏我們需要關注第二個參數,第二個參數是指向一個結構體的一個指針,這個結構體叫做 struct msgbuf

 struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[用戶指定大小];    /* message data */
           };

這個結構體第一個變量是消息的類型,第二個是存放消息的數組。
這個結構體早sys/msg.h當中已經有定義,所以我們可以直接拿來用就行。

然後看msgsnd的後面兩個參數:
msgsz是注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度,是指第二個參數製作指向的消息長度。

msgflg,用來指明在沒有數據的情況下所需要做的操作,這個操作我們經常使用IPC_NOWAIT,這樣當消息隊列滿了的時候,再向消息隊列中發送消息,不會發生阻塞現象。

當調用成功,這個時候消息數據將被拷貝放入到消息隊列中
接收消息:

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

msgrcv函數第二個參數,同樣是指向消息隊列結構體的指針,第三個參數和第四個也和上述是一樣的。

當調用成功的時候,返回消息隊列接受緩存當中的字節數,消息複製到用戶緩衝區,然後刪除消息隊列當中的對應的信息。

控制消息隊列


控制消息隊列需要使用的函數是msgctl函數,

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

這個函數中的cmd參數提供需要的操作,例如使用IPCRMID就可以進行刪除消息隊列。

示例:


我們使用服務器進程進行創建消息隊列,然後使用客戶進程進行send數據,服務進程接收數據。

comm.h

#ifndef __COMM_H__
#define __COMM_H__
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<string.h>
#include<time.h>

#define _MSG_SIZE_ 1024
#define FILEPATH "."
#define PROJID 0
#define PERM 0666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
#define MYSIZE 128

struct msgbuf
{
    long mtype;
    char mtext[MYSIZE];
};

static int commmsg(int msgflg);
int createmsg();
int sendmsg(int msgid,long type,const char * msg);
int getmsg();
int recvmsg(int msgid,int type,char out[]);
int destorymsg(int msgid);

#endif //!__COMM_H__

comm.c


#include"comm.h"

static int commmsg(int msgflg)
{
    key_t key=ftok(FILEPATH,PROJID);
    if(key<0)
    {
       perror("ftok");
        return -1;
    }
    int msqid=msgget(key,msgflg);
    if(msqid<0)
    {
        perror("msgget");
        return -2;
    }
    return msqid;
}
int createmsg()
{
    return commmsg(IPC_CREAT|IPC_EXCL|PERM);   
}
int getmsg()
{
    return commmsg(0);
}
int sendmsg(int msgid,long type,const char *msg)
{
    struct msgbuf buf;

    buf.mtype=type;
    strcpy(buf.mtext,msg);

    int id=msgsnd(msgid,&buf,sizeof(buf.mtext),0);
    if(id<0)
    {
        perror("msgsnd");
        return -1;
    }
    return 0;
}
int recvmsg(int msgid,int type,char out[])
{
    struct msgbuf buf;
    int size=msgrcv(msgid,&buf,sizeof(buf.mtext),type,0);
    if(size>0)
    {
        //buf.mtext[size]='\0';
        strncpy(out,buf.mtext,size);
        return 0;

    }
    perror("msgrcv");
    return -1;
}
int destorymsg(int msgid)
{
    if(msgctl(msgid,IPC_RMID,NULL)<0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}

client.c

#include"comm.h"
int main()
{
    int msqid=getmsg();
    char buf[MYSIZE];
    char out[2*MYSIZE];
    while(1){
        printf("please input:");
        fflush(stdout);

        ssize_t _s=read(0,buf,sizeof(buf)-1);
        if(_s>0)
        {
            buf[_s]='\0';
            sendmsg(msqid,CLIENT_TYPE,buf);
        }
        if(recvmsg(msqid,SERVER_TYPE,out)<0)
        {
            break;
        }
        printf("server echo :%s\n",out);
    }

    return 0;

}

server.c

#include"comm.h"
int main()
{
    int msqid=createmsg();
    char buf[2*MYSIZE];
    while(1){
        if(recvmsg(msqid,CLIENT_TYPE,buf)<0)
        {
            break;
        }
        printf("client# %s\n",buf);
        if(sendmsg(msqid,SERVER_TYPE,buf)<0)
        {
            break;   
        }
    }
    destorymsg(msqid);

    return 0;
}

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章