标识符与键key
内核中的IPC结构(消息队列、信号量、共享内存)都用一个非负整数的标识符加以引用。标识符是IPC对象的内部名,为使多个进程能够使用同一IPC对象,需要提供一个外部命名方案。为此,每个IPC对象都与一个键key相关联,将这个键作为该对象的外部名。创建IPC结构时都应指定一个键,这个键的数据类型是基本系统数据类型key_t,这个键由内核变换成标识符。
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
// 返回值:若成功返回键 key,失败返回(key_t)-1
参数:
- pathname:当前操作系统中一个存在的路径,使用该文件属性的st_dev和st_ino填充键
- proj_id:产生键时,使用该参数的低8位
// 该结构规定了权限和所有者
struct ipc_perm
{
__kernel_key_t key; // key
__kernel_uid_t uid; // 所有者ID
__kernel_gid_t gid; // 所属组ID
__kernel_uid_t cuid; // 创建者进程ID
__kernel_gid_t cgid; // 创建者进程组ID
__kernel_mode_t mode; // 读写权限
unsigned short seq; // 序号
};
消息队列
消息队列是消息的链表,存放在内核中,由消息队列标识符标识。
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
- 消息队列独立于进程,不受进程生命周期的影响。
- 消息队列的消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
内核为每个消息队列维护了一个结构体,数据结构如下:
struct msqid_ds
{
struct ipc_perm msg_perm; // ipc_perm权限等信息
struct msg *msg_first; /* 指向消息队列头 */
struct msg *msg_last; /* 指向消息队列尾 */
__kernel_time_t msg_stime; /* 最后发送消息的时间 */
__kernel_time_t msg_rtime; /* 最后接收消息的时间 */
__kernel_time_t msg_ctime; /* 最后修改的时间 */
unsigned long msg_lcbytes; /* 重用32位垃圾字段 */
unsigned long msg_lqbytes; /* 重用32位垃圾字段 */
unsigned short msg_cbytes; /* 当前队列大小 */
unsigned short msg_qnum; /* 当前队列的消息个数 */
unsigned short msg_qbytes; /* 队列的最大字节数 */
__kernel_ipc_pid_t msg_lspid; /* 最后mesgsnd的pid*/
__kernel_ipc_pid_t msg_lrpid; /* 最后recevice的pid*/
};
消息的数据结构如下:
struct msg_msg
{
struct list_head m_list;
long m_type; // 消息的类型
size_t m_ts; // 消息的大小
struct msg_msgseg *next; // 下一个节点
void *security; // 真正的消息的位置
};
创建/获取消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
//返回值,若成功返回消息队列ID,失败返回-1
参数:
- key:消息队列的键key
- msgflg:创建消息队列时指定的属性
IPC_CREAT
:创建新的消息队列,同时需要指定对消息队列的操作权限IPC_EXCL
:检测消息队列是否存在,必须和IPC_CREAT
一起使用
操作消息队列
#include <sys/msg.h>
int msgctl(int msqid,int cmd,struct msqid_ds* buf);
//返回值:成功返回0,失败返回-1
参数:
- shmid:消息队列ID
- cmd
IPC_STAT
:得到当前消息队列的状态IPC_SET
:设置消息队列的状态IPC_RMID
:删除该消息队列以及仍在该队列中的所有数据,删除立即生效。仍在使用这一队列的其他进程,再次操作时,会得到EIDRM
错误
- buf:
cmd=IPC_STAT
,作为传出参数,会得到消息队列的相关属性信息
cmd=IPC_SET
,作为传入参数,将用户的自定义属性设置到消息队列中
cmd=IPC_RMID
,buf无意义,指定为NULL即可
发送消息
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// 成功返回0, 失败返回-1
参数:
- msqid:消息队列ID
- msgp:指向自定义缓冲区
struct msgbuf
{
long mtype; // 消息的类型
char mtext[512]; // 消息内容,在使用时,自己重新定义此结构
};
- msgsz:待发送的消息内容的长度,即mtext的长度
- msgflg
- 0:阻塞式函数。进程解除阻塞:有空间可以容纳要发送的消息;从系统中删除此队列;捕捉到一个信号
IPC_NOWAIT
:类似文件I/O的非阻塞I/O标志,若消息队列已满,或者队列中的消息总数等于系统限制值,或队列中的字节总数等于系统限制值,则函数立即出错,返回EAGAIN
接收消息
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
// 成功返回消息数据部分的长度,出错返回-1
- msqid:消息队列ID
- msgp:与msgsnd函数一致
- msgsz:数据缓冲区的长度
- msgtype:从消息队列中取出哪一类型的消息
- type=0:返回队列中的第一个消息。
- type>0:返回队列中消息类型为type的第一个消息。
- type<0:返回队列中消息类型值小于等于type绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
- msgflg参数
- 0:阻塞式函数。
IPC_NOWAIT
:类似文件I/O的非阻塞I/O标志。MSG_NOERROR
:若返回的消息长度大于msgsz,则该消息会被截断,系统不会通知。若没有设置这一标志,则出错返回E2BIG
,消息仍留在队列中。
示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <string.h>
// 自定义消息
struct msg_buf
{
long mtype; // 消息类型
char mtext[512]; // 消息缓冲区
};
int main(int argc, char *argv[])
{
key_t key;
struct msg_buf msg_buf_snd = {
1, "1-hello world!"};
struct msg_buf msg_buf_recv;
memset(&msg_buf_recv, 0, sizeof(msg_buf_