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

System V IPC : 基於內核持續性

System V 消息隊列:在程序之間傳遞數據的一種方法

System V 共享內存:用於在程序之間高效的共享數據

System V 信號量:用於管理對資源的訪問

內核會爲每個IPC對象維護一個數據結構:

struct ipc_perm
{
      key_t      key;                   /*  調用shmget()時給出的關鍵字*/
      uid_t      uid;                   /*共享內存所有者的有效用戶ID */
      gid_t      gid;                   /* 共享內存所有者所屬組的有效組ID*/ 
      uid_t      cuid;                  /* 共享內存創建 者的有效用戶ID*/
      gid_t      cgid;                  /* 共享內存創建者所屬組的有效組ID*/
      unsigned short   mode;            /* Permissions + SHM_DEST和SHM_LOCKED標誌*/
      unsignedshort    seq;             /* 序列號*/
};
系統創建和打開IPC通道

1.從IPC鍵生成IPC標識符:


2.創建或打開一個IPC對象的邏輯:


ipcs 查看對應的IPC結構,

ipcrm 刪除對應的IPC結構。

生成鍵值的函數:

key_t ftok(const char *path, int id);                          //返回值:成功返回鍵,出錯返回(key_t)-1

簡單用法: key_t ftok(".", 'a');

消息隊列:消息隊列是消息的鏈接表,存放在內核中並由消息隊列標識符標識。

消息隊列的數據結構:

struct msqid_ds {
	struct ipc_perm msg_perm;
	struct msg *msg_first;		/* first message on queue,unused  */
	struct msg *msg_last;		/* last message in queue,unused */
	__kernel_time_t msg_stime;	/* last msgsnd time */
	__kernel_time_t msg_rtime;	/* last msgrcv time */
	__kernel_time_t msg_ctime;	/* last change time */
	unsigned long  msg_lcbytes;	/* Reuse junk fields for 32 bit */
	unsigned long  msg_lqbytes;	/* ditto */
	unsigned short msg_cbytes;	/* current number of bytes on queue */
	unsigned short msg_qnum;	/* number of messages in queue */
	unsigned short msg_qbytes;	/* max number of bytes on queue */
	__kernel_ipc_pid_t msg_lspid;	/* pid of last msgsnd */
	__kernel_ipc_pid_t msg_lrpid;	/* last receive pid */
};

內核中的消息隊列狀態:


消息隊列的幾個基本參數:

1>一條消息最大是多大?                                         cat /proc/sys/kernel/msgmax

2>消息隊列中所有消息的總和最大是多大 ?            cat /proc/sys/kernel/msgmnb    

3>系統能夠創建多少個消息隊列?                            cat /proc/sys/kernel/msgmni

4>消息通道即是消息類型

消息隊列的創建:

int msgget(key_t key, int msgflg);               //msgflg   當消息隊列已經存在,打開時置0,不存在創建時IPC_CREAT|0644

返回值:成功返回消息隊列額id,出錯返回-1

消息隊列操作:(這裏討論做刪除用時的用法)

int msgctl(int msqid, int cmd, struct msqid_ds *buf);     //msqid msgget返回的id, cmd 一般置爲IPC_RMID, 最後一個參數一般不關注,置0即可

返回值:成功返回0,出錯返回-1

發送消息:

消息格式:

struct msgbuf {
          long mtype;       /* message type, must be > 0 */
         char mtext[100];    /* message data,oflength nbytes */
};

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);         //msgp--數據的起始地址    msgsz--有效數據的大小(不包括通道號)  

    //msgflg--一般爲0 | IPC_NOWAIT

返回值:成功返回0, 出錯返回-1

從消息隊列中取出數據:

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

//msgp--存放收到數據的緩衝區, msgsz--緩衝區大小(不包括通道大小)  msgtyp(即是通道號)--從哪個通道接收數據

//int msgflg    //0 | IPC_NOWAIT

msgtyp == 0 :返回隊列中的第一個消息

返回值:成功返回0, 出錯返回-1

實例應用:發送程序往指定的消息隊列通道中寫,接受程序從指定的消息隊列通道中讀


創建消息隊列:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>            
int main()
{
    int id =msgget(123, IPC_CREAT| 0644);
    if(-1 == id) perror("msgget"),exit(1);
    printf("msg create ok\n");
}
發送消息:

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

struct msgbuf{
    long channel;   //  >0  表示通道號
    char msgtext[100];      //信息緩存
};

int main()
{
    struct msgbuf buf;
    int id =msgget(123, 0);
    if(-1 == id) perror("msgget"),exit(1);

    while(1) 
    {
        printf("please enter channel num:\n");
        scanf("%d", &buf.channel);
        printf("please enter message:\n");
        scanf("%s", buf.msgtext);
        if(-1 == msgsnd(id, &buf, strlen(buf.msgtext), 0))
            perror("msgsnd"), exit(1);

        printf("msg write ok!\n");
    }
}
接受消息:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <unistd.h>

struct msgbuf{
    long channel;   //  >0  表示通道號
    char msgtext[100];     
};

int main()
{
    struct msgbuf buf;
    int id =msgget(123, 0);
    if(-1 == id) perror("msgget"),exit(1);
    
    while(1) 
    {
        int channel;

        printf("please enter channel num you want to read:\n");
        scanf("%d", &channel);
        memset(&buf, 0x00, sizeof(buf));
        msgrcv(id, &buf, 100, channel, 0);

        printf("msgtext = %s\n", buf.msgtext);
    }
}
刪除消息隊列:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main()
{
    int id = msgget(123, 0);
    if(-1 == id) perror("msgget"), exit(1);

    if (-1 == msgctl(id, IPC_RMID, 0)) perror("msgctl"), exit(1);
    
}

實例2:多個客戶端進程分別向服務器程序發送消息,服務器程序接收到消息後轉發回該程序。


客戶機程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>

struct msgbuf{
    long channel;       //通道號
    char msgtext[100];  //消息
};

int main()
{
    int id = msgget(123, 0);
    if(-1 == id) perror("msgget"), exit(1);

    struct msgbuf buf;
    while(1)
    {
        memset(&buf, 0x00, sizeof(buf));
        *(long*)(buf.msgtext) = (long)getpid();
        buf.channel = 1;
        printf("please enter msg you want send:\n ");
        scanf("%s", buf.msgtext + sizeof(long));
        
        if(strncmp(buf.msgtext + sizeof(long), "quit", 4) == 0)
        {
            printf("exit");
            break;
        }

        if( -1 == msgsnd(id, &buf , strlen(buf.msgtext + sizeof(long))+ sizeof(long), 0))
        {
            perror("msgsnd");
            exit(1);
        }
        
        //這裏的緩衝區大小的計算要考慮到前面加的pid的值在存儲時會存在小端存儲的問題
        memset(&buf, 0x00, sizeof(buf));
        if(-1 == msgrcv(id, &buf, 100, getpid(), 0))
        {
            perror("msgrcv");
            exit(1);
        }
        printf("rcv = %s\n", buf.msgtext + sizeof (long));
    }        
}

服務器程序:

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

struct msgbuf{
    long channel;       //通道號
    char msgtext[100];  //消息
};

int main()
{
    int id = msgget(123, IPC_CREAT|0644);
    if(-1 == id) perror("msgget"), exit(1);

    struct msgbuf buf;
    while(1)
    {   
        //清空緩存區
        memset(&buf, 0x00, sizeof(buf));
        //從1號通道接收,存放在buf
        if (-1 == msgrcv(id, &buf, 100, 1, 0)) 
        {
            perror("msgrcv");
            exit(1);
        }
        buf.channel = *(long*)(buf.msgtext);
        printf("rcv %d msg %s\n", buf.channel, buf.msgtext + sizeof(long));       
        //將消息發送到返回通道,getpid得到的通號,這裏存放在msgtext前sizeof(long)個字節中

        if(-1 == msgsnd(id, &buf, strlen(buf.msgtext+sizeof(long)) + sizeof(long), 0))
        {
            perror("msgsnd");
            exit(1);
        }

        printf("send ok\n");
    }
}
清除程序:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>


int main()
{
    int id = msgget(123, 0);
    if(-1 == id) perror("msgget"), exit(1);

    msgctl(id, IPC_RMID, 0);
}



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