大纲:
1.消息队列
2.信号量
一.消息队列
1.概念
消息队列就是消息的链表,可以把消息看成一个记录,具有特定的格式,一些进程可以向其中按照特定的规则添加新的消息,另一些进程可以从
消息列队里面读取消息。消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。
2.消息列队的优势
信号传输的数据量有限,管道只能传送无格式的字节流,消息列队克服了这些缺点
3.消息队列的形式
POSIX消息队列和系统v消息队列
系统v消息队列:随着内核持续的,只有在内核重启或者人工删除时候,该消息队列才会被删除
4.键值--键来命名某个特定的消息队列
消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,必须提供该消息队列的键值
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(char *pathname,int proj_id)
pathname:文件名
proj_id是一个1-255之间的一个整数值,典型的值是一个ASCII值。
功能:返回文件名对应的键值
5.打开/创建消息队列
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgget(key_t key,int msgflg)
key:键值,由ftok获得
msgget:标志位
IPC_CREAT---创建消息队列
IPC_EXCL---和IPC_CREAT一起使用,如果创建已存在的消息队列则出错
IPC_NOWAIT---读写消息队列无法得到满足时候,不阻塞
返回值:与键值key相对应的消息队列的描述字
6.创建消息队列需要什么条件,满足一个即可:
1)如果没有和键值key相对应的消息队列,并且msgflg中包含了IPC_CREAT
2)key为IPC_PRIVATE
实例:
int open_queue(key_t keyval){
int qid;
if((qid=msgget(keyval,IPC_CREAT))==-1)
return(-1);
else
return(qid);
}
7.消息队列发送消息
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgsnd(int msgid, struct msgbuf *msgp,int msgsz, int msgflg);
msgid:已经打开的消息队列的id
msgp:存放消息结构
msgsz:消息数据长度
msgflag:发送标志位
0,忽略标志位;
IPC_NOWAIT,如果消息队列没有足够空间,消息将不被写入队列,控制权返回调用函数的线程。如果不指定这个参数,线程将被阻塞直到消息被可以被写入。
8.消息结构
struct msgbuf{
long mtype;//消息类型>0
char mtest[x];//信息内容,长度由msgsz指定
};
9.接收消息
int msgrcv(int msgid,struct msgbuf *msgp,int msgsz,long msgtyp,int msgflg)
功能:从msqid代表的消息列队中读取一个msgtype类型的消息,并把消息存储在msgp指向的msgbuf结构中,成功读取消息后,队列里面这条消息就会被删除
函数的前三个参数和msgsnd()函数中对应的参数的含义是相同的。
msgtyp--信息类型:
msgtyp = 0 ,不分类型,直接返回消息队列中的第一项
msgtyp > 0 ,返回第一项 msgtyp与 msgbuf结构体中的mtype相同的信息
msgtyp <0 , 返回第一项 mtype小于等于msgtyp绝对值的信息
msgflg--控制函数行为的标志,取值可以是:
0,表示忽略;
IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以
从队列中得到符合条件的消息为止。如果一个客户端正在等待消息的时候队列被删除,EIDRM就会被返回。如果进程在阻塞等待过程中收到了系统
的中断信号,EINTR就会被返回。
MSG_NOERROR,如果函数取得的消息长度大于msgsz,将只返回msgsz 长度的信息,剩下的部分被丢弃了。如果不指定这个参数,E2BIG 将被返回,
而消息则留在队列中不被取出。
实例
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include <memory.h>
#include<string.h>
struct msg_buf{
int mtype;//消息类型
char data[255]; //信息内容长度最大为255
};
int main(){
key_t key;
int msgid,ret,ret2;
struct msg_buf msgbuf;
key=ftok("/tmp/test",1);//获取键值,/tmp/test--tmp目录下的test文件
printf("key=[%x]\n",key);
msgid=msgget(key,IPC_CREAT|0666);//创建打开消息列队,key通过ftok函数获取
if(msgid==-1){//消息列队的描述字返回值为-1表示消息列队创建失败
printf("create error!\n");
return(-1);
}
msgbuf.mtype=getpid();//获取消息类型用进程号表示
strcpy(msgbuf.data,"test hello world");//将字符串"test hello world"复制给msgbuf结构体的data中
ret=msgsnd(msgid,&msgbuf,sizeof(msgbuf.data),IPC_NOWAIT);//发送消息
if(ret==-1){
printf("send message error\n");
return(-1);
}
memset(&msgbuf,0,sizeof(msgbuf));
ret2=msgrcv(msgid,&msgbuf,sizeof(msgbuf.data),getpid(),IPC_NOWAIT);
if(ret2==-1){
printf("recv message error\n");
return(-1);
}
printf("recv ,msg=%s\n",msgbuf.data);
}
测试
linux 查看消息ipcs -q
二.信号量—又名信号灯
1.作用:保护临界资源,用于进程访问控制,进程同步。不同于其他的通信方式
2.分类
1)二值信号量:信号值只能是0或者1,类似于互斥锁,信号量强调共享资源,只有共享资源可用,其他进程同样可以修改信号量的值,而互斥锁
更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
2)计数信号灯:信号量的值可以取任意非负数
3.创建/打开信号量
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semget(key_t key,int nsems,int semflag)
key:键值,由ftok获得
nsems:打开或者创建新的信号灯集中包含信号灯数目
semflag:标志,和消息队列取值一样
4.对信号量进行控制
int semop(int semid,struct sembuf *sops,unsigned nsops)
semid:信号量集的id
sops:操作数组表明进行什么操作
nsops:sops所指向数组元素的个数
struct sembuf
{
unsigned short int sem_num; //要操作信号量的编号,第一个为0
short int sem_op;
/*正数,加到现有信号量,释放信号量;负数,绝对值大于现在信号量,操作阻塞,
直到信号值大于或者等于sem_op绝对值,用于获取信号量,sem_op=0暂时阻塞*/
short int sem_flg;
/*sem_flg取值:
IPC_NOWAIT:对信号操作不能满足时候,semop()不会阻塞,立即返回,设定错误信息
IPC_UNDO:程序结束(正常/不正常),释放信号量,避免程序在异常下结束时未将锁定资源解锁*/
};