进程学习:进程间通信(system v IPC)1.消息队列

来源:互联网 发布:中国电信网络传真 编辑:程序博客网 时间:2024/06/05 14:10

消息队列与管道的异同点:

消息队列与管道的相同点:

1.都是利用内核的1G空间来通信(如图);



消息队列与管道的不同点:

1.管道的数据读取是严格按照先进先出;


2.消息队列的数据读取可以按照数据类型进行读取;



消息队列函数

一、int msgget(key_t key, int msgflg);

1.功能:创建或打开一个消息队列

2.参数:

key:两种方式:1-由ftok函数获得;2-直接填写IPC_PRIVATE(此时系统会分配一个唯一的key值)

msgflg:打开的方式    

         IPC_CREAT:没有消息队列的话,创建一个新的消息队列,如果已有有相同msgid的消息队列,则返回已有消息队列的msgid;

         IPC_EXCL:不可单独使用。与IPC_CREAT结合使用时(IPC_CREAT|IPC_EXCL),如果已有相同msgid的消息队列存在,则返回一个EEXIST错误,因为已存在;

         读写权限:与文件权限一样。(IPC_CREAT | IPC_EXCL | 0664)

3.返回值:

          成功:返回相应的msgid值;

          错误:返回-1;


二、int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);

1.功能:向值为msgid的消息队列发送一个消息

2.参数:

          msgid:目标消息队列的msgid值

          msgq:要发送消息的结构体地址

          msgsz:要发送的消息大小,结构体总大小减去消息类型大小 sizeof(struct msgbuf) - sizeof(long)

          msgflg:以阻塞(0)或者非阻塞(IPC_NOWAIT)的模式发送

3.返回值:

          成功:返回   0

          失败:返回   -1

要发送消息的结构体模板struct msgbuf{    long msgtype;  //大于0    消息正文,可以自定义类型}

三、int msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

1.功能:从消息队列读取一条消息

2.参数:

           msgid:同msgsnd();

           msgp:要存放接收到消息的结构体地址

           msgsz:要接收正文的长度sizeof(struct msgbuf) - sizeof(long)

           msgtyp:接收消息的类型,区别于管道的这个关键点。

                          msgtyp  =  0:按先进先出逐个读取数据,与管道无异;

                          msgtyp  >  0:表示要接收类型为msgtyp的消息;

                          msgtyp  <  0:表示要接收类型为 [ 小于(msgtyp的绝对值中)] 最小的类型的消息

3.返回值:

                 成功:返回  0

                 失败:返回  -1

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

1.功能:控制消息队列

2.参数:

             msqid:消息队列msgid号;

             cmd:控制命令  IPC_STAT  /   IPC_SET  /  IPC_RMID

                       IPC_STAT:获取消息队列的属性,并放在第三个参数(struct msqid_ds *buf)内;

                       IPC_SET:设置消息队列的属性,要设置的属性放在第三个参数;

                       IPC_RMID:删除消息队列,此时第三个参数为NULL;

3.返回值:

              成功:返回  0

              失败:返回  -1


代码实例

        下面咱们来写几个具体代码验证一下消息队列的特点;



第一个代码:从一个 terminal 输入,从另一个 terminal 读取并打印:(半双工通信方式)

/*在终端输入消息*/#include "msg.h"int main(int argc, const char *argv[]){key_t key;struct msgbuf sendbuf;    //声明要发送的消息变量size_t sndsz = sizeof(sendbuf) - sizeof(long);    //计算要发送的消息的大小,用作发送函数msgsnd的第3个参数int msgid;        //生成key值if( (key = ftok(".", 1)) < 0){perror("ftok error");exit(1);}printf("key = %#0x\n", key);    //打印key值,用十六进制打印if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0){if(errno == EEXIST)    //如果错误值是EEXIST,意思就是这个消息队列已经存在的话,就执行下面的代码{msgid = msgget(key, 0664);    //不创建,只打开消息id为msgid的消息队列printf("messageid %d -- key %#0x is exist\n", msgid, key);}else {perror("msgget error");exit(1);}}else{printf("msgid: %d -- key: %#0x\n is created\n", msgid, key);}system("ipcs -q");sendbuf.msgtype = 1;    //给这个sendbuf变量定义一个类型,用于接收方选择性接收while(fgets(sendbuf.buf, N, stdin) != NULL){msgsnd(msgid, &sendbuf, sndsz, 0);system("ipcs -q");if(strncmp(sendbuf.buf, "quit", 4) == 0){break;}}return 0;}

/*从终端读取并打印消息*/#include "msg.h"int main(int argc, const char *argv[]){key_t key;int msgid;struct msgbuf rcvbuf;size_t rcvsz = sizeof(rcvbuf) - sizeof(long);ssize_t rcvnum;char sysbuf[N];if( (key = ftok(".", 1)) < 0){perror("ftok error");exit(1);}printf("key = %#0x\n", key);if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0){if(errno == EEXIST){msgid = msgget(key, 0664);printf("messageid %d -- key %#0x is exist\n", msgid, key);}else{perror("msgget error");exit(1);}}else{printf("msgid: %d -- key: %#0x\n is created\n", msgid, key);}rcvbuf.msgtype = 1;        //注意msgrcv的第4个参数是要接收的消息的类型,如果是0的话,就是按先进先出的方式接收,此时和管道一样;while( (rcvnum = msgrcv(msgid, &rcvbuf, rcvsz, 0, 0)) > 0){if(strncmp(rcvbuf.buf, "quit", 4) == 0){break;sprintf(sysbuf, "ipcrm -q %d", msgid);system(sysbuf);system("ipcs -q");}system("ipcs -q");fputs(rcvbuf.buf, stdout);}return 0;}


Makefile:利用了 Makefile 的隐晦规则以及伪目标;

.PHONY:allcleanall:write readwrite:read:clean:-rm write read


第二个代码:建立两个 terminal ,并且每个 terminal 均可以向对方发生消息,同时接收对方发送过来的消息(全双工通信模式)

注意发送和接收方的消息类型,这里容易出错的。

#include "msg.h"int main(int argc, const char *argv[]){key_t key;int msgid, pid;struct msgbuf sendbuf;size_t sndsz = sizeof(sendbuf) - sizeof(long);struct msgbuf recvbuf;size_t rcvsz = sizeof(recvbuf) - sizeof(long);if( (key = ftok(".", 1)) < 0){perror("ftok error");exit(1);}if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0){if(errno == EEXIST){msgid = msgget(key, 0664);printf("key:%#0x msgid:%d --message queue is exist\n", key, msgid);}else{perror("msgget error");kill(getpid(), SIGKILL);}}if( (pid = fork()) < 0){perror("fork error");exit(1);}else if(pid == 0){sendbuf.msgtype = 2;while(fgets(sendbuf.buf, N, stdin) != NULL){msgsnd(msgid, &sendbuf, sndsz, 0);if(strncmp(sendbuf.buf, "quit", 4) == 0){kill(pid, SIGKILL);exit(5);}}raise(SIGKILL);}else if(pid > 0){int status, out_pid;recvbuf.msgtype = 2;while(msgrcv(msgid, &recvbuf, rcvsz, 1, 0) > 0){if(strncmp(recvbuf.buf, "quit", 4) == 0){exit(5);}fputs(recvbuf.buf, stdout);}while(1){out_pid = waitpid(-1, &status, WNOHANG);}if(WIFEXITED(status)){printf("over_process, pid = %d, exitnum = %d\n", out_pid,WEXITSTATUS(status));}}return 0;}


#include "msg.h"int main(int argc, const char *argv[]){key_t key;int msgid, pid;struct msgbuf sendbuf;size_t sndsz = sizeof(sendbuf) - sizeof(long);struct msgbuf recvbuf;size_t rcvsz = sizeof(recvbuf) - sizeof(long);if( (key = ftok(".", 1)) < 0){perror("ftok error");exit(1);}if( (msgid = msgget(key, IPC_CREAT|IPC_EXCL|0664)) < 0){if(errno == EEXIST){msgid = msgget(key, 0664);printf("key:%#0x msgid:%d --message queue is exist\n", key, msgid);}else{perror("msgget error");kill(getpid(), SIGKILL);}}if( (pid = fork()) < 0){perror("fork error");exit(1);}else if(pid == 0){sendbuf.msgtype = 1;while(fgets(sendbuf.buf, N, stdin) != NULL){msgsnd(msgid, &sendbuf, sndsz, 0);if(strncmp(sendbuf.buf, "quit", 4) == 0){kill(pid, SIGKILL);exit(5);}}raise(SIGKILL);}else if(pid > 0){int status, out_pid;recvbuf.msgtype = 1;while(msgrcv(msgid, &recvbuf, rcvsz, 2, 0) > 0){if(strncmp(recvbuf.buf, "quit", 4) == 0){exit(5);}fputs(recvbuf.buf, stdout);}while(1){out_pid = waitpid(-1, &status, WNOHANG);}if(WIFEXITED(status)){printf("over_process, pid = %d, exitnum = %d\n", out_pid,WEXITSTATUS(status));}}return 0;}



阅读全文
0 0