进程通信 IPC 之消息队列

来源:互联网 发布:单片机软件调试 编辑:程序博客网 时间:2024/04/30 21:06
/**
 *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);
}

原创粉丝点击