訊息佇列

不懂小白在线记录發表於2024-05-28

訊息佇列

​ Linux系統中訊息佇列(Message Queue)是程序間通訊的一種方式,這種通訊機制的好處是可以傳輸指定型別(使用者可以自行定義)的資料,相同型別的資料根據到達順序在佇列中進行排隊。

​ 不同型別的資料不能處於同一個佇列中,也就是說系統中可能存在多個訊息佇列,每個訊息佇列中的資料型別都是不同的,所以使用者打算讀取訊息佇列中的資料時也需要指定資料型別,才可以從儲存該型別資料的訊息佇列中讀取有效資料。

​ Linux系統中有很多訊息佇列,Linux系統是如何管理訊息佇列的呢,因為每個建立訊息佇列都具有唯一的鍵值key,程序可以透過指定訊息佇列的鍵值對指定的訊息佇列傳送資料,,且提供了一個shell命令:ipcs -a來檢視系統所有的ipc物件的訊息。

​ 今日練習:要求程序A建立一條訊息佇列之後向程序B傳送SIGUSR1訊號,程序B收到該訊號之後開啟訊息佇列並把程序的PID作為訊息寫入到訊息佇列中,要求程序B在寫入訊息之後,發SIGUSR2訊號給程序A,程序A收到該訊號則從訊息佇列中讀取訊息並輸出訊息正文的內容。

目錄
  • 訊息佇列
    • 程序A
    • 程序B

程序A

​ 設計一個函式,透過msgget函式介面來獲取訊息佇列的ID,並向訊息佇列中接收字串

/*****************************************************************
*
*      file name :main.c
*      authour   :yq_dyx@163.com
*      date      :2024.05.27 
*      function  :設計一個函式,透過msgget函式介面來獲取訊息佇列的ID,並向訊息佇列中接收字串。
*      note      :為防止重複建立,需要先判斷共享記憶體和訊號量集是否存在。
*      CopyRight (c)   2024   yq_dyx@163.com   All Right Reseverd
*
******************************************************************/

#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/msg.h>
#include <features.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>

struct mymsg{

    long mtype;
    int mtext;
};

struct mymsg msg;
int msg_id;
//接收到訊號後的自定義處理函式的引數只能為訊號,所以想在此函式中呼叫其他引數需要定義為全域性變數
void sig_handler(int signo)
{  
    if(signo == SIGUSR2)
    {
        //用於接收訊息佇列裡面的內容,4:訊息佇列正文位元組數,0:是接收訊息選項,如果設定為0則是預設模式,當無指定型別訊息時阻塞
        msgrcv(msg_id,&msg,4,1,0);       
        printf("msg.mtext is %d\n",msg.mtext);
    }   
}

int main()
{
    //獲取“.”檔案的key值
    key_t key = ftok(".",3);
    if(-1 == key)
    {
        fprintf(stderr,"msgget failed,errno is %d,%s\n",errno,strerror(errno));
        return -1;
    }
    //獲取訊息佇列的ID,如果沒有建立訊息佇列則建立
    msg_id = msgget(key,IPC_CREAT|0644);
    if(-1 == msg_id)
    {
        fprintf(stderr,"msgget failed,errno is %d,%s\n",errno,strerror(errno));
        return -1;
    }
    //訊息佇列函式介面提供的結構體,mtype是訊息型別,mtext是訊息內容
    struct mymsg msg;
    msg.mtype = 1;
    
    //kill是系統提供可以向另一個程序傳送訊號,getpid()是獲取程序號,+1是為了獲取另一個通訊程序的ID,因為那個程序在程序A後立馬建立則+1
    kill(getpid()+1,SIGUSR1);  
    while(1) 
    //對於收到的訊號進行自定義處理,設計一個子函式,用於接受到訊號後此程序的行為
    signal(SIGUSR2,sig_handler);
    return 0;
}

​ ftok()函式可以把一個指定路徑的檔案和一個指定的專案id轉換為一個system-V IPC物件使用的鍵值key。

​ 此key用於msgget來獲取訊息佇列的ID。

程序B

設計一個函式,透過msgget函式介面來獲取訊息佇列的ID,並向訊息佇列中傳送字串。

/*****************************************************************
*
*      file name :main.c
*      authour   :yq_dyx@163.com
*      date      :2024.05.27 
*      function  :設計一個函式,透過msgget函式介面來獲取訊息佇列的ID,並向訊息佇列中傳送字串。
*      note      :為防止重複建立,需要先判斷共享記憶體和訊號量集是否存在。
*      CopyRight (c)   2024   yq_dyx@163.com   All Right Reseverd
*
******************************************************************/

#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/msg.h>
#include <features.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>

struct mymsg{

    long mtype;
    int mtext;
};
struct mymsg msg;
int msg_id;
//接收到訊號後的自定義處理函式的引數只能為訊號,所以想在此函式中呼叫其他引數需要定義為全域性變數
void sig_handler(int signo)
{  
  
    printf("成功\n");
    //mtype指的是訊息型別,必須是一個大於0的正整數 
    msg.mtype = 1;
    msg.mtext = getpid();  
    printf("msg.mtext is %d\n",msg.mtext);
}

int main()
{
    //獲取“.”檔案的key值
    key_t key = ftok(".",3);
    if(-1 == key)
    {
        fprintf(stderr,"msgget failed,errno is %d,%s\n",errno,strerror(errno));
        return -1;
    }
    //獲取訊息佇列的ID,如果沒有建立訊息佇列則建立
    msg_id = msgget(key,IPC_CREAT|0644);
    if(-1 == msg_id)
    {
        fprintf(stderr,"msgget failed,errno is %d,%s\n",errno,strerror(errno));
        return -1;
    }

    while(1)
    {
        //對於收到的訊號進行自定義處理,設計一個子函式,用於接受到訊號後此程序的行為
        signal(SIGUSR1,sig_handler);
        //用於傳送訊息佇列裡面的內容,4:訊息佇列正文位元組數,0:msgsnd函式的第四個引數msgflg指的是訊息佇列的標誌,如果該標誌設定為IPC_NOWAIT,則表示不阻塞,此時如果待寫入的訊息的長度大於訊息佇列剩餘空間,則直接返回並報錯。
        msgsnd(msg_id,&msg,4,0);
         //kill是系統提供可以向另一個程序傳送訊號,getpid()是獲取程序號,+1是為了獲取另一個通訊程序的ID,因為那個程序在程序A後立馬建立則+1
        kill(getpid()-1,SIGUSR2);
    }
        
    return 0;

}

相關文章