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

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

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

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:
	all
	clean

all:write read

write:

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值