文章目录
  1. 消息队列
    1. msgget系统调用
    2. msgsnd系统调用
    3. msgrcv系统调用
    4. msgctl系统调用
  2. 其他建议
    1. IPC命令
    2. 进程间传递文件描述符

消息队列

消息队列是在两个进程之间传递二进制块数据的一种简单有效的方式。每个数据块都有一个特定的类型,接收方可以根据类型来有选择地接收数据,而不用像管道那样必须以先进先出的方式接收数据。

msgget系统调用

msgget系统调用创建一个消息队列,或者获取一个已有的消息队列。其定义如下。

1
2
3
4
5
6
7
/*
引用方式: #include <sys/msg.h>
key: 键值, 用来标识一个全局唯一的消息队列
flags: 指定一组标志, 其低端的9比特为该信号量的权限, 格式含义均与系统调用open的mode参数相同
返回一个正整数作为消息队列的标识符: 成功 || 返回-1并设置errno: 失败
*/
int msgget(key_t key, int flg);

若msgget用于创建消息队列,则与之关联的内核数据结构msqid_ds将被创建并初始化。msqid_ds结构体的定义如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
引用方式: #include <sys/msg.h>
msg_perm: 消息队列的操作权限
msg_stime: 最后一次调用msgsnd的时间
msg_rtime: 最后一次调用msgrcv的时间
msg_ctime: 最后一次被修改的时间
__msg_cbytes: 消息队列中已有的字节数
msg_qnum: 消息队列中已有的消息数
msg_qbytes: 消息队列允许的最大字节数
msg_lspid: 最后执行msgsnd的进程的PID
msg_lrpid: 最后执行msgrcv的进程的PID
*/
struct msqid_ds{
struct ipc_perm msg_perm;
__time_t msg_stime;
__time_t msg_rtime;
__time_t msg_ctime;
__syscall_ulong_t __msg_cbytes;
msgqnum_t msg_qnum;
msglen_t msg_qbytes;
__pid_t msg_lspid;
__pid_t msg_lrpid;
__syscall_ulong_t __unused4;
__syscall_ulong_t __unused5;
};

msgsnd系统调用

msgsnd系统调用将一条消息添加到消息队列中;其定义如下。

1
2
3
4
5
6
7
8
9
/*
引用方式: #include <sys/msg.h>
msqid: 指定消息队列的标识符(由msgget系统调用返回)
msg_ptr: 指向一个准备发送的消息, 消息格式见下文
msg_sz: 消息数据部分的长度(可为0, 代表没有消息数据)
flg: 控制msgsnd函数的行为(仅支持IPC_NOWAIT标志)
返回0: 成功 || 返回-1并设置errno: 失败
*/
int msgsnd(int msqid, const void * msg_ptr, size_t msg_sz, int flg);

msg_ptr必须按照如下格式。

1
2
3
4
5
6
7
8
/*
mtype: 指定消息类型
mtext: 消息数据
*/
struct msgbuf{
long mtype;
char mtext[512];
}

当消息队列已满,若flg参数指定了IPC_NOWAIT标志时,msgsnd系统调用将立即返回并设置errno为EAGAIN;默认情况下,msgsnd将阻塞。

处于阻塞状态的msgsnd系统调用会被如下两种异常情况中断。

  • msqid指定的消息队列被移除,msgsnd函数立即返回并设置errno为EIDRM;
  • 调用进程接收到信号,msgsnd函数立即返回并设置errno为EINTR;

msgsnd成功时将修改内核数据结构msqid_ds的部分字段。

  • msg_qnum加1;
  • 将msg_lspid设置为调用进程的PID;
  • 将msg_stime设置为当前时间;

msgrcv系统调用

msgrcv系统调用从消息队列中获取消息,其定义如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
引用方式: #include <sys/msg.h>
msqid: 指定消息队列的标识符(由msgget系统调用返回)
msg_ptr: 存储接收的信息
msg_sz: 消息数据部分的长度(可为0, 代表没有消息数据)
mtype: 指定接收何种类型的消息; mtype=0: 读取消息队列中第一个消息 || mtype>0: 读取消息队列第一个mtype类型的消息(除非flg指定了MSG_EXCEPT) || mtype<0: 读取消息队列中第一个类型值比mtype绝对值小的消息
flg: 控制msgrcv函数的行为; 如下标志的按位或
IPC_NOWAIT: 若消息队列中无消息, 则msgrcv函数立即返回并设置errno为ENOMSG
MSG_EXCEPT: 若mtype>0, 则接收消息队列中第一个非mtype类型的消息
MSG_NOERROR: 若消息数据部分的长度超过msg_sz,则将其截断
返回0: 成功 || 返回-1并设置errno: 失败
*/
int msgrcv(int msqid, void * msg_ptr, size_t msg_sz, long mtype, int flg);

处于阻塞状态的msgrcv系统调用会被如下两种异常情况中断。

  • msqid指定的消息队列被移除,msgsnd函数立即返回并设置errno为EIDRM;
  • 调用进程接收到信号,msgsnd函数立即返回并设置errno为EINTR;

msgsnd成功时将修改内核数据结构msqid_ds的部分字段。

  • msg_qnum减1;
  • 将msg_lrpid设置为调用进程的PID;
  • 将msg_rtime设置为当前时间;

msgctl系统调用

msgctl系统调用控制消息队列的某些属性。其定义如下。

1
2
3
4
5
6
7
8
/*
引用方式: #include <sys/msg.h>
msqid: 指定消息队列的标识符(由msgget系统调用返回)
command: 指定要执行的命令, 详见下表
buf: 存储结果
返回值与command有关 || 返回-1并设置errno: 失败
*/
int msgctl(int msqid, int command, struct msqid_ds * buf);

 

其他建议

IPC命令

ipcs命令输出系统当前拥有的共享资源实例。ipcrm删除遗留在系统中的共享资源。

进程间传递文件描述符

在之前提到,父进程创建的管道文件描述符在子进程中仍然打开。注意,父进程向子进程传递的并不是一个文件描述符的值,而是在子进程中创建一个新的文件描述符,之后与父进程中的文件描述符指向内核中相同的文件表项。

那么,两个不相关的进程之间,该怎么传递文件描述符呢?示例详见代码清单13-5。