消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识
首先需要包含头文件
#include <sys/msg.h>
先是创建或者打开消息队列,其中key_t key
一般用ftok来生成,pathname
传入一个路径,一般是当前路径". ",proj_id
随便填写一个数,要做通信的话通信的另外一端要与这个数保持一致才能找到对应的icpID,返回值为生成一个独有的数,msgflg
确定消息队列的访问权限,以下为相关的flag
- IPC_CREAT 找到当前的key对应的IPC,如果没有IPC则进行创建
- IPC_EXCL 如果存在则返回失败
- IPC_NOWAIT 返回值为当前ipc对应的id值
int msgget(key_t key, int msgflg);// 创建或打开消息队列:成功返回队列ID,失败返回-1
key_t ftok(const char *pathname, int proj_id);
数据传输函数,ptr
指定为结构体包含内容和类型,msgflg
通常设定为0表示不阻塞
int msgsnd(int msqid, const void *ptr, size_t size, int flag);// 添加消息:成功返回0,失败返回-1
数据传输函数,ptr
指定为结构体包含内容和类型,msgflg
通常设定为0表示阻塞
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);// 读取消息:成功返回消息数据的长度,失败返回-1
控制消息队列函数,可以用来删除队列,对于cmd
参数如下
IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所有数据
IPC_SET:按由buf指向结构中的值, 设置与此队列相关结构中的msg_perm.uid, msg_perm.gid,msg_perm.mode和msg_qbytes. 该命令只有下列两种进程可以执行:
有效用户ID等于msg_perm.cuid或msg_per.uid.
具有超级用户特权的进程.
IPC_STAT:取此队列的msqid_ds结构, 并将它存放在buf指向的结构中*
IPC_INFO:获取ipc信息 和ipcs
一般使用IPC_RMID 删除队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);// 控制消息队列:成功返回0,失败返回-1
DOME
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
//第一个进程
int main()
{
struct msgbuf readBuf;
key_t key;
key=ftok(".",'z');
if(key==-1){
perror("key");
exit(-1);
}
int msgID = msgget(key,IPC_CREAT|0777);
if(msgID==-1){
perror("msgget");
exit(-1);
}
struct msgbuf sendBuf;
sendBuf.mtype=988;
memset(&sendBuf.mtext,0,128);
strcpy(sendBuf.mtext,"this is another");
msgsnd(msgID,&sendBuf,strlen(sendBuf.mtext),0);
msgrcv(msgID,&readBuf,sizeof(readBuf.mtext),888,0);
printf("read from que:%s\n",readBuf.mtext);
msgctl(msgID,IPC_RMID,NULL);
return 0;
}
//第二个进程
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sendBuf={888,"this is message from quen"};
struct msgbuf readBuf;
key_t key;
key=ftok(".",'z');
if(key==-1){
perror("key");
exit(-1);
}
int msgID = msgget(key,IPC_CREAT|0777);
if(msgID==-1){
printf("get que failer\n");
exit(-1);
}
msgrcv(msgID,&readBuf,sizeof(readBuf.mtext),988,0);
printf("read from que:%s\n",readBuf.mtext);
msgsnd(msgID,&sendBuf,strlen(sendBuf.mtext),0);
msgctl(msgID,IPC_RMID,NULL);
return 0;
}
要点:消息队列中的一个节点如果被读出了就不存在了
共享内存
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
特点
- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
- 因为多个进程可以同时操作,所以需要进行同步。
- 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
首先包含头文件
#include <sys/shm.h>
创建共享内存,size
指定内存大小 ,这里的内存最小单位1M即1024byte,flag
确定消息队列的访问权限,其中IPC_CREAT
找到当前的key对应的IPC,如果没有IPC则进行创建
int shmget(key_t key, size_t size, int flag);// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
映射共享内存,其中第二个参数是用来指定将共享内存放在虚拟地址空间的什么位置的,大部分人都会将第二个参数设置为NULL的时候,表示用户并不在意,一切交由内核做主
当shmaddr
的地址不是NULL的时候目标是进程希望将共享内存attach
到该地址,但是该地址必须是系统分页的整数倍,否则会返回EINVAL
错误,内核提供了一个shmflg
为SHM_RND
,表示该地址不是系统分页的整数倍也没关系,系统会在用户给出的地址附近,就近找一个系统分页整数倍的地址。
如果指定的shmaddr
落在已经在用的地址范围内,就会导致EINVAL
错误,但是linux提供了一个非标准的扩展SHNM_REMAP
,这个标志为表示替换位于shmaddr
处且长度为共享内存段的长度的任何内存映射,很明显,设置了SHNM_REMAP
标志位,shmaddr
参数就不能再为NULL了。
如果进程仅仅是读取共享内存段的内容,并不修改,则可以指定SHM_RDONLY
标志位
void *shmat(int shm_id, const void *addr, int flag);// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
断开shmat建立的关系
int shmdt(void *addr); // 断开与共享内存的连接:成功返回0,失败返回-1
根据参数cmd
执行相应的操作。常用的是IPC_RMID
(从系统中删除该共享内存),当cmd
为IPC_STAT
和IPC_SET
时需要用到第三个参数,其中shmid_ds
结构体的定义如下
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);// 控制共享内存的相关信息:成功返回0,失败返回-1
struct shmid_ds {
struct ipc_perm shm_perm;
int shm_segsz;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
unsigned short shm_cpid;
unsigned short shm_lpid;
short shm_nattch;
...
};
DEMO
//进程一
int main()
{
char *shmaddr;
int shmid;
key_t key;
key=ftok(".",1);
shmid= shmget(key,1024*4,IPC_CREAT|0666);
if(shmid==-1){
printf("shmget noOK\n");
exit(-1);
}
shmaddr=shmat(shmid,0,0);
printf("shmat ok\n");
strcpy(shmaddr,"chenlichen");
sleep(5); //没有阻塞防止退出
shmdt(shmaddr);
shmctl(shmid,IPC_RMID,0);
printf("quit\n");
return 0;
}
//进程二
int main()
{
char *shmaddr;
int shmid;
key_t key;
key=ftok(".",1);
shmid= shmget(key,1024*4,0);
if(shmid==-1){
printf("shmget noOK\n");
exit(-1);
}
shmaddr=shmat(shmid,0,0);
printf("shmat ok\n");
printf("data:%s\n",shmaddr);
shmdt(shmaddr);
printf("quit\n");
return 0;
}
在linux中ipcs -m
可以查看有哪些共享内存,删除共享内存ipcrm -m+内存id