進程通信 IPC 之消息隊列

/**
 *IPC是指進程間的通信,其中有三種我們稱之爲IPC即消息隊列、信號量以及共享存儲器
 *創建IPC結構(調用msgget\semget\shmget), 應指定一個鍵,鍵的數據類型是基本系統數據類型key_t,鍵由內核變換成標識符
 *以下是消息隊列msgget
 */
/**
 * 優缺點:IPC結構是在系統範圍內起作用的,沒有訪問計數。例如,如果進程創建了一個消息隊列,在該隊列中放入了幾則消息,
 * 然後終止,但是該消息隊列及其內容並不會被刪除,它們餘留在系統中直至出現下述情況:
 * 由某個進程調用msgrcv或msgctl讀取消息或刪除消息隊列,或某個進程執行ipcrm命令刪除消息隊列,
 * 或由正在再啓動的系統刪除消息隊列,將此與管道相比,當最後一個訪問管道的進程終止時,管道就被完全地刪除了。
 * 對於FIFO而言,雖然當最後一個引用FIFO進程終止時其名字仍保留在系統中,直至顯式地刪除它,但是留在FIFO中的數據卻在此時被全部刪除,於是也就徒有虛名了。
 * XSI IPC的另一個問題是這些IPC結構在文件系統中沒有名字,我們不能使用那些使用文件系統的函數來訪問它們或修改他們的屬性。
 * 爲了支持它們不得不增加了十幾條全新的系統調用(msgget,semop,shmat).我們不能用ls命令見到IPC對象,不能用rm命令刪除他們,
 * 不能用chmod命令修改它們的訪問權限。於是就不得不增加新的命令ipcs和ipcrm.
 */
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>

#define MY_KEY 8800811           // need to change

#define SERVER_ID 2

#define MAX_BUF 200
void sigend(int);

//消息結構體
struct mymsg {
    long mtype;
    long pid;
    char buf[MAX_BUF]; //在此指定了共享內存的大小 = MAX_BUF+ 8
};

int msgid;

bool isWorking = true;

#define SERVER_TYPE 5555
#define CLIENT_TYPE 6666

//阻塞式接收數據
void * RunRecvData(void * pVoid)
{
    //指定接收類型--對應 發送結構體mymsg.mtype內容
    long *typ = (long*)pVoid;

    struct mymsg msgbuf;

    while(isWorking)
    {
        //接收消息;
        msgrcv(msgid, &msgbuf, sizeof(struct mymsg), *typ, 0);

        printf("recv data: %s, type=%ld, pid= %ld \n", msgbuf.buf, msgbuf.mtype, msgbuf.pid);
    }

    return NULL;
}

int main3(void)
{
    pthread_t pidRecv;

    struct mymsg msgbuf; //定義一結構變量
    //創建新的消息隊列
    if ((msgid = msgget(MY_KEY, IPC_CREAT | IPC_EXCL | 0666)) < 0)
    {
        //創建失敗,已經存在,直接獲取消息id,獲取已有的消息隊列
        msgid = msgget(MY_KEY, 0666);

        printf("Act as client, ask question and wait answer! msgid=%d\n", msgid);

        printf("To end this process, enter end as input question.\n\n");

        pid_t pid = getpid();

        long type = SERVER_TYPE;

        pthread_create(&pidRecv,NULL,RunRecvData,&type);

        //I/O中斷
        printf("Input question in one line:\n");
        fgets(msgbuf.buf, sizeof(struct mymsg) - 2 * sizeof(long) - 1, stdin);

        while (strcmp(msgbuf.buf, "end\n"))
        {
            //獲得服務進程號
            msgbuf.mtype = CLIENT_TYPE;// SERVER_ID;
            //獲得當前進程的進程號
            msgbuf.pid = getpid();
            //發送消息;
            msgsnd(msgid, &msgbuf, sizeof(struct mymsg) - sizeof(long), 0);

            printf("Input question in one line:\n");

            memset(msgbuf.buf,0,sizeof(msgbuf.buf));

            fgets(msgbuf.buf, sizeof(struct mymsg) - 2 * sizeof(long) - 1,
                    stdin);
        }
    }
    else
    {
        //設置某一信號的對應動作, sigend 刪除標記
        signal(SIGINT, sigend);
        signal(SIGTERM, sigend);

        printf("ACT SERVER!!! Wait question and give answer. msgid=%d\n", msgid);
        printf("To end this process, try Ctrl+C or use kill.\n\n");

        long tmpId = CLIENT_TYPE;

        pthread_create(&pidRecv,NULL,RunRecvData,&tmpId);

        while (1)
        {
            memset(msgbuf.buf,0,sizeof(msgbuf.buf));
            fgets(msgbuf.buf, sizeof(struct mymsg) - 2 * sizeof(long) - 1,
                    stdin);

            msgbuf.mtype = SERVER_TYPE; //msgbuf.pid;
            msgbuf.pid = getpid();
            //發送消息;
            msgsnd(msgid, &msgbuf, sizeof(struct mymsg) - sizeof(long), 0);
        }
    }
}

void sigend(int sig)
{
    //操縱一個消息隊列;
    //IPC_RMID:從系統內核中移走消息隊列。
    //IPC_STAT:讀取消息隊列的數據結構msqid_ds,並將其存儲在buf指定的地址中。 可以直接拷貝,減少內存複製
    //IPC_SET:設置消息隊列的數據結構msqid_ds中的ipc_perm元素的值。這個值取自buf參數。
    if(0==msgctl(msgid, IPC_RMID, 0))
        printf("msgctl:success\n");
    else{
        //打印錯誤信息
    }
    exit(0);
}

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