一、消息队列
定义:unix早期通信机制之一的信号能够传送的信息量有限,管道则只能传送无格式的字节流,这无疑会给应用程序开发带来不便。消息队列(也叫做报文队列)则克服了这些缺点。
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式。进程可以向中按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息。
键值:消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,必须提供该消息队列的键值。
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (char*pathname, char proj)
功能:返回文件名对应的键值。
pathname: 文件名
proj: 项目名(不为 0 即可)
打开/创建:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
key :键值,由 ftok 获得。
msgflg :标志位。
返回值:与健值 key 相对应的消息队列描述字。
msgflg的取值:
(1)IPC_CREAT
创建新的消息队列
(2)IPC_EXCL
与 IPC_CREAT 一同使用,表示如果要创建的消息队列已经存在,则返回错误。
(3)IPC_NOWAIT
读写消息队列要求无法得到满足时,不阻塞 。
在以下两种情况下,将创建一个新的消息队列:
(1)如果没有与健值 key 相对应的消息队列,并且 msgflg 中包含了 IPC_CREAT 标志位。
(2)key 参数为 IPC_PRIVATE 。
发送消息:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg)
功能:向消息队列中发送一条消息。
msqid:已打开的消息队列id
msgp:存放消息的结构
msgsz:消息数据长度(注意:是数据的长度)
msgflg:发送标志,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。
struct msgbuf
{
long mtype;/* 消息类型 > 0 */
char mtext[1]; /* 消息数据的首地址 */
};
接收消息:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg)
功能:从msqid代表的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp指向的msgbuf结构中。在成功地读取了一条消息以后,队列中的这条消息将被删除。(注意:读取之后会删除,同有名管道。)
#include<string.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
struct msg_buf
{
int mtype;
char data[255];
};
int main()
{
key_t key;
int msgid;
int ret;
struct msg_buf msgbuf;
key=ftok("/tmp/2",'a');
printf("key =[%x]\n",key);
msgid=msgget(key,IPC_CREAT|0666); /*通过文件对应*/
if(msgid==-1)
{
printf("create error\n");
return -1;
}
msgbuf.mtype = getpid();
strcpy(msgbuf.data,"test haha");
ret=msgsnd(msgid,&msgbuf,sizeof(msgbuf.data),IPC_NOWAIT);
if(ret==-1)
{
printf("send message err\n");
return -1;
}
memset(&msgbuf,0,sizeof(msgbuf));
ret=msgrcv(msgid,&msgbuf,sizeof(msgbuf.data),getpid(),IPC_NOWAIT);
if(ret==-1)
{
printf("recv message err\n");
return -1;
}
printf("recv msg =[%s]\n",msgbuf.data);
}
二、信号量
信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。
分类:
(1)二值信号灯:信号灯的值只能取0或1,类似于互斥锁。 但两者有不同:信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
(2)计数信号灯:信号灯的值可以取任意非负值。
创建/打开:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg)
key:键值,由ftok获得
nsems:指定打开或者新创建的信号灯集中将包含信号灯的数目
semflg:标识,同消息队列。
操作:
int semop(int semid, struct sembuf *sops, unsigned nsops)
功能:对信号量进行控制。
semid :信号量集的 ID
sops :是一个操作数组,表明要进行什么操作
nsops : sops 所指向的数组的元素个数。
struct sembuf {
unsigned short sem_num; /* semaphore index in array */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
sem_num:要操作的信号量在信号量集中的编号,第一个信号的编号是0。
sem_op:如果其值为正数,该值会加到现有的信号量值中,通常用于释放信号量;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值,通常用于获取信号量;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。
Sem_flg:信号操作标志,可能的选择有两种:
(1)IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
(2)SEM_UNDO:程序结束时(不论正常或不正常)释放信号量,这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。