消息队列

Download Report

Transcript 消息队列

进程间通讯(IPC)机制(二)
——消息队列
(注:基于2.4.0核心源代码)
(王科峰)
消息队列

1.数据结构
 2.创建/查找消息队列 sys_msgget
 3.发送消息sys_msgsnd
 4.接收消息sys_msgrcv
 5.消息机制的控制和设置sys_msgctl
ipc_ids
semaphore
通用数据结构
int size
atomic_t count
int in_use
int sleepers
wait_queue
wait
struct task_struct *task
long __magic
struct wait_queue *next
int max_id
short seq
short seq_max
sem
spinlock_t
ary
* entries
ipc_id
*p
*p
msg_queue
q_perm
time_t q_stime pid_t q_lspid
time_t q_rtime pid_t q_lrpid
*p
*p
time_t q_ctime q_messages
long q_cbytes
q_receivers
long q_qnum
q_senders
long q_qbytes
msg_ids(ipc_ids)
2.4版本消息队列数据结构关系
int size
msg_queue
kern_ipc_perm
q_perm
key_t key
time_t q_stime
* entries
ipc_id
time_t q_rtime
*p
time_t q_ctime
ipc_id
long q_cbytes
*p
long q_qnum
long q_qbytes
pid_t q_lspid
pid_t q_lrpid
q_messages
q_receivers
q_senders
ushort uid
ushort gid
ushort cuid
ushort cgid
ushort mode
ushort seg
2.4版本消息队列数据结构关系
msg_ids(ipc_ids)
int size
msg_queue
kern_ipc_perm
q_perm
key_t key
time_t q_stime
ipc_id
time_t q_rtime
*p
time_t q_ctime
ipc_id
long q_cbytes
* entries
*p
long q_qnum
long q_qbytes
msg_msg
m_list
msg_msg
m_type
msg_msg
m_ts
msg_msgseg
*next
*next
pid_t q_lspid
pid_t q_lrpid
q_messages
q_receivers
q_senders
ushort uid
ushort gid
ushort cuid
ushort cgid
ushort mode
ushort seg
2.4版本消息队列数据结构关系
msg_ids(ipc_ids)
int size
msg_queue
kern_ipc_perm
q_perm
key_t key
time_t q_stime
ipc_id
time_t q_rtime
*p
time_t q_ctime
ipc_id
long q_cbytes
* entries
*p
long q_qnum
long q_qbytes
msg_msg
m_list
msg_msg
m_type
msg_msg
m_ts
msg_msgseg
*next
*next
pid_t q_lspid
ushort uid
ushort gid
ushort cuid
ushort cgid
ushort mode
ushort seg
msg_sender
pid_t q_lrpid
list
q_messages
* tsk
q_receivers
msg_sender
q_senders
task_struct
2.4版本消息队列数据结构关系
msg_ids(ipc_ids)
int size
msg_queue
kern_ipc_perm
q_perm
key_t key
time_t q_stime
ipc_id
time_t q_rtime
*p
time_t q_ctime
ipc_id
long q_cbytes
* entries
*p
long q_qnum
long q_qbytes
msg_msg
m_list
msg_msg
m_type
msg_msg
m_ts
msg_msgseg
*next
*next
msg_msg
pid_t q_lspid
pid_t q_lrpid
q_messages
ushort uid
ushort gid
ushort cuid
ushort cgid
ushort mode
ushort seg
msg_receiver
msg_receiver
r_list
* r_tsk
q_receivers
r_mode
q_senders
r_msgtype
r_maxsize
* r_msg
task_stru
2.4版本消息队列数据结构关系
ipc_ids
msgbuf
* entries
1
2
3
4
*p
msg_queue
q_perm
*p
q_perm
*p
q_perm
mtype
mtext[1]
r_list
msg_queue
q_perm
32767
……
msg_msg
……
* r_msg
……
msg_msg
pid_t q_lrpid
msg_receiver
q_messages
q_receivers
…… msg_sender
msg_sender
q_senders
msg_receiver
消息队列

1.数据结构
 2.创建/查找消息队列 sys_msgget
 3.发送消息sys_msgsnd
 4.接收消息sys_msgrcv
 5.消息机制的控制和设置sys_msgctl
sys_msgget
asmlinkage long sys_msgget
(key_t key, int msgflg)
锁队列
(down)
//key反映队列的特定信息,
msgflg记录了队列的控制信息
需要私
有队列?
是
创建新队列
newque()
否
是 找队列( ipc_findkey)
否
创建队列?
是
创建新队列
newque()
否
否
创建队列?
在通讯之前,可由用户进程用
ftok函数产生,可以确保无关的
进程用相同的参数产生相同的
key值,从而使他们可以通过
msgget查到相同的消息队列。
是
解锁
(up)
取得已找到队列
(ipc_lock)
访问权限检查
(ipcperms)
通过
构造队列标识号
( ipc_buildid )
没通
过
返回队列标识号
或者错误代码
消息队列

1.数据结构
 2.创建消息队列 sys_msgget
 3.发送消息sys_msgsnd
 4.接收消息sys_msgrcv
 5.消息机制的控制和设置sys_msgctl
消息队列数据结构关系
ipc_ids
msgbuf
* entries
1
2
3
4
ipc_id
*p
ipc_id
*p
msg_queue
q_perm
ipc_id
*p
msg_queue
q_perm
mtype
mtext[1]
q_perm
msg_receiver
r_list
msg_queue
q_perm
32767
……
msg_msg
……
* r_msg
……
msg_msg
pid_t q_lrpid
msg_receiver
q_messages
q_receivers
…… msg_sender
msg_sender
q_senders
msg_receiver
消息发送、接收流程通讯图
msg_queue
q_perm
……
pid_t q_lrpid
q_messages
q_receivers
q_senders
发送进程
从用户空间中
取得消息
msg_msg
找到消息队列
并检查访问权限
否
队列满?
是
是
等待?
有否要接受
该消息的睡眠进程?
msg_receiver
msg_receiver
找到消息队列
并检查访问权限
msg_receiver
……
* r_msg
查找消息
否
找到?
是
是
否
否
接收进程
唤醒等待接收
消息进程
睡眠
消息入队列
并更新队列
睡眠
取得消息
并更新队列
上台
唤醒所有等待
发送消息进程
收到消息?
是
消息送回用户
否
上台
重试?
返回
否
是
返回
是否等待?
否
检查参数 将 消 息 从 用 户
是
空间复制到系
预备进入睡眠
复制消息 统中,
ss_add
load_msg()同 时 根 据 消 息
长度,将超过
检查并取得 一 个 页 面 的 消 解锁
消息队列指针息 分 段 , 将msg_unlock
所
msg_lock ()有 的 段 组 成 一
是
个消息,返
回
schedule()
为Null? 其指针
否
苏醒
失败 检查队列序号
msg_checkid ()
成功
失败 检查访问权限
msg_lock ()
ipcperms
成功
检查队列
容量
苏醒后检查并
取得消息队列指针
sys_msgsnd
从发送等待队列
中删除ss_del
是否有信号等待
signal_pending
否
是
检查接受队列
pipelined_send
失败
消息加入找到
的消息队列
成功
如果有等
待该消息
的进程,
则把该消
息直接送
给进程并
唤醒该进
程,而不
再入队列
更新消息队列
控制信息
失败
成功
成功
各函数解释详见《消息队列—Code.doc》源代码中注解
队列解锁
msg_unlock
释放消息空间
返回
sys_msgrcv
之 查找消息
消息队列循环头
检查参数
所需消息?
testmsg()
消息查找策略
mode = convert_mode()
否
是
否
检查并取得
消息队列指针
msg_lock ()
是
需类型值
最小的消息?
if( mode == SEARCH_LESSEQUAL && msg->m_type != 1 )
retry:
检查队列序号
msg_checkid ()
找到,跳出
found_msg=msg;
msgtyp=msg->m_type-1
检查访问权限
Ipcperms()
各函数解释详见
《消息队列—Code.doc》源代码中注解
消息队列循环尾
见下页
消息队列循环尾
sys_msgrcv
之 查找消息后
从 msgrvc 过 来 的
kill = 0,所以并不脱
连,但情景分析中
解释此处时认为接
受进程已将等待进
程脱连了,但其实
并不影响,因为在
该等待发送的进程
上台后,还要把其
自身从等待队列中
脱连。
实际接受的报文类
型送回给用户空间
将接受到的报文送
复制到用户空间
是
是否找到
接上页
否
检查缓存
等待?
否
消息脱链
list_del( )
更新消息所在
消息队列信息
唤醒所有等待
发送消息的进程
ss_wakeup()解释
out_success:
put_user()
store_msg()解释
是
链入等待
接受队列解释
设置睡眠
msg_receiver msr_d
链入msq消息队列的
等待接受队列,并
否
设置该等待者的各
参数,其
中msr_d.r_msg=
ERR_PTR(-GAIN);
重试标志,待其
上台后有用。
队列解锁
msg_unlock
schedule()
释放消息空间
out_unlock:队列解锁
msg_unlock
各函数解释详见《消息队列—Code.doc》源代码中注解
返回
见下页
之 等待接收醒来
由于加锁过程中也
可能又收到消息
接上页
schedule()
取msg
msg = (struct msg_msg*) msr_d.r_msg
err = PTR_ERR(msg)
收到消息?
进程重新上台
if(!IS_ERR(msg))
否
是否重试?
否
sys_msgrcv
if(err == -EAGAIN)
否
收到消息?
if(!IS_ERR(msg))
是
收到消息开始过户
goto out_success
消息队列加锁
msg_lock()
队列在否? 是
否
队列解锁
msg_unlock
从等待队列中脱链
list_del()
收到消息开始过户
goto out_success
由于发送进程直接
将消息的指针赋给
msr_d.r_msg,所
以和消息队列已经
没有关系了
各函数解释详见《消息队列—Code.doc》源代码中注解
有信号?
否
goto retry
out_unlock:队列解锁
msg_unlock
返回
否
取msg
msg = (struct msg_msg*) msr_d.r_msg
消息队列数据结构关系
ipc_ids
msgbuf
* entries
1
2
3
4
ipc_id
*p
ipc_id
*p
msg_queue
q_perm
ipc_id
*p
msg_queue
q_perm
mtype
mtext[1]
q_perm
msg_receiver
r_list
msg_queue
q_perm
32767
……
msg_msg
……
* r_msg
……
msg_msg
pid_t q_lrpid
msg_receiver
q_messages
q_receivers
…… msg_sender
msg_sender
q_senders
msg_receiver
消息队列

1.数据结构
– 2.创建消息队列 `sys_msgget

3.发送消息sys_msgsnd
 4.接收消息sys_msgrcv
 5.消息机制的控制和设置sys_msgctl
sys_msgctl(int msqid, int cmd, struct msqid_ds *buf)
cmd
IPC_STAT
作用:
取得IPC状态和属性
ipc_ids
IPC_SET
设置IPC状态
* entries
1
MSG_INFO
取得消息队列信息
2
3
4
IPC_RMID
msgbu
f
mtype
*p
msg_que
ue
q_perm
*p
q_perm
*p
q_perm
mtext[1]
r_list
msg_queue
撤销消息队列
q_perm
32767
……
msg_msg
* r_msg
……
msg_msg
pid_t q_lrpid
msg_receiver
q_messages
q_receivers
……
……
msg_sender msg_sender
q_senders
msg_receiver
消息队列

1.数据结构
 2.创建消息队列 `sys_msgget
 3.发送消息sys_msgsnd
 4.接收消息sys_msgrcv
 5.消息机制的控制和设置sys_msgctl
知识点提醒(2):
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
type
0
*next
r_list
*prev
list_entry(list_head * next, struct
msg_receiver, r_list)
知识点提醒(3):
Msgsnd
…
if(IS_ERR(msg)) {
…
if(msgflg&IPC_NOWAIT) {
err=-EAGAIN;
…
Msgsnd --> pipelined_send
…
if(msr->r_maxsize < msg->m_ts) {
msr->r_msg = ERR_PTR(-E2BIG);
wake_up_process(msr->r_tsk);
…
Msgrcv
…
if(!IS_ERR(msg))
goto out_success;
…
err = PTR_ERR(msg);
if(err == -EAGAIN)
…
static inline void *ERR_PTR(long error)
{
return (void *) error;
}
static inline long PTR_ERR(const void *ptr)
{
return (long) ptr;
}
static inline long IS_ERR(const void *ptr)
{
return (unsigned long)ptr > (unsigned
long)-1000L; ?
}
#define E2BIG 7 /* Arg list too long */
#define EAGAIN 11 /* Try again */
知识点提醒(3)(续):
测例:
long *p = (void *)-7;
long b = (unsigned long)p > (unsigned long)-1000L;
printf(“biger: %d,",b);
结果: biger: 1
long *p = (void *)0;
long b = (unsigned long)p > (unsigned long)-1000L;
printf(“biger: %d,",b);
结果: biger: 0
long *p = (void *)3;
long b = (unsigned long)p > (unsigned long)-1000L;
printf(“biger: %d,",b);
结果: biger: 0
Thank You Very Much!