4--消息队列(报文队列)实践到内核--消息队列的控制
来源:互联网 发布:mac lol 国服下载 编辑:程序博客网 时间:2024/06/06 07:33
http://blog.csdn.net/embeddedfly/article/details/6411669
case MSGCTL:
return sys_msgctl (first, second, (struct msqid_ds __user *) ptr);
可以看到是进入了sys_msgctl()函数并从此返回,我们追踪一下这个函数
第一参数不言而喻,朋友们通过前三节的阅读这里应该知道是消息队列的ID号,第二个参数我们需要说明一下,应用程序传递过来的是IPC_RMID
asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
{
struct msg_queue *msq;
int err, version;
struct ipc_namespace *ns;
if (msqid < 0 || cmd < 0)
return -EINVAL;
version = ipc_parse_version(&cmd);
ns = current->nsproxy->ipc_ns;
switch (cmd) {
case IPC_INFO:
case MSG_INFO:
{
struct msginfo msginfo;
int max_id;
if (!buf)
return -EFAULT;
/*
* We must not return kernel stack data.
* due to padding, it's not enough
* to set all member fields.
*/
err = security_msg_queue_msgctl(NULL, cmd);
if (err)
return err;
memset(&msginfo, 0, sizeof(msginfo));
msginfo.msgmni = ns->msg_ctlmni;
msginfo.msgmax = ns->msg_ctlmax;
msginfo.msgmnb = ns->msg_ctlmnb;
msginfo.msgssz = MSGSSZ;
msginfo.msgseg = MSGSEG;
down_read(&msg_ids(ns).rw_mutex);
if (cmd == MSG_INFO) {
msginfo.msgpool = msg_ids(ns).in_use;
msginfo.msgmap = atomic_read(&ns->msg_hdrs);
msginfo.msgtql = atomic_read(&ns->msg_bytes);
} else {
msginfo.msgmap = MSGMAP;
msginfo.msgpool = MSGPOOL;
msginfo.msgtql = MSGTQL;
}
max_id = ipc_get_maxid(&msg_ids(ns));
up_read(&msg_ids(ns).rw_mutex);
if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
return -EFAULT;
return (max_id < 0) ? 0 : max_id;
}
case MSG_STAT: /* msqid is an index rather than a msg queue id */
case IPC_STAT:
{
struct msqid64_ds tbuf;
int success_return;
if (!buf)
return -EFAULT;
if (cmd == MSG_STAT) {
msq = msg_lock(ns, msqid);
if (IS_ERR(msq))
return PTR_ERR(msq);
success_return = msq->q_perm.id;
} else {
msq = msg_lock_check(ns, msqid);
if (IS_ERR(msq))
return PTR_ERR(msq);
success_return = 0;
}
err = -EACCES;
if (ipcperms(&msq->q_perm, S_IRUGO))
goto out_unlock;
err = security_msg_queue_msgctl(msq, cmd);
if (err)
goto out_unlock;
memset(&tbuf, 0, sizeof(tbuf));
kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);
tbuf.msg_stime = msq->q_stime;
tbuf.msg_rtime = msq->q_rtime;
tbuf.msg_ctime = msq->q_ctime;
tbuf.msg_cbytes = msq->q_cbytes;
tbuf.msg_qnum = msq->q_qnum;
tbuf.msg_qbytes = msq->q_qbytes;
tbuf.msg_lspid = msq->q_lspid;
tbuf.msg_lrpid = msq->q_lrpid;
msg_unlock(msq);
if (copy_msqid_to_user(buf, &tbuf, version))
return -EFAULT;
return success_return;
}
case IPC_SET:
case IPC_RMID:
err = msgctl_down(ns, msqid, cmd, buf, version);
return err;
default:
return -EINVAL;
}
out_unlock:
msg_unlock(msq);
return err;
}
在分析这段代码之前我们要看一下参数cmd
/*
* Control commands used with semctl, msgctl and shmctl
* see also specific commands in sem.h, msg.h and shm.h
*/
#define IPC_RMID 0 /* remove resource */
#define IPC_SET 1 /* set ipc_perm options */
#define IPC_STAT 2 /* get ipc_perm options */
#define IPC_INFO 3 /* see ipcs */
这些命令代码不仅是为消息队列所使用的,还为整个IPC通讯所使用,包括信号量和共享内存,后边二种我们以后进行实践和分析,另外,也还有消息队列专用的命令
/* ipcs ctl commands */
#define MSG_STAT 11
#define MSG_INFO 12
我们还是那种方式在代码中去理解这些宏的作用,不过聪明的读者可能从定义的字面上能看出其作用,为了避免死记硬背,死搬硬套的学习方法,我们就从实践中记忆吧
这里还有第三个参数struct msqid_ds ,它是为了保持兼容以前的代码所用的,我们暂且放一放,避免跑了主题,可以看到在我们的应用程序传递下来的这个参数是空指针。
好了我们进入函数内部看一下,因为使用的命令代码是IPC_RMID,所以会进入这段case语句
case IPC_RMID:
err = msgctl_down(ns, msqid, cmd, buf, version);
return err;
static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
struct msqid_ds __user *buf, int version)
{
struct kern_ipc_perm *ipcp;
struct msqid64_ds msqid64;
struct msg_queue *msq;
int err;
if (cmd == IPC_SET) {
if (copy_msqid_from_user(&msqid64, buf, version))
return -EFAULT;
}
ipcp = ipcctl_pre_down(&msg_ids(ns), msqid, cmd,
&msqid64.msg_perm, msqid64.msg_qbytes);
if (IS_ERR(ipcp))
return PTR_ERR(ipcp);
msq = container_of(ipcp, struct msg_queue, q_perm);
err = security_msg_queue_msgctl(msq, cmd);
if (err)
goto out_unlock;
switch (cmd) {
case IPC_RMID:
freeque(ns, ipcp);
goto out_up;
case IPC_SET:
if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
!capable(CAP_SYS_RESOURCE)) {
err = -EPERM;
goto out_unlock;
}
msq->q_qbytes = msqid64.msg_qbytes;
ipc_update_perm(&msqid64.msg_perm, ipcp);
msq->q_ctime = get_seconds();
/* sleeping receivers might be excluded by
* stricter permissions.
*/
expunge_all(msq, -EAGAIN);
/* sleeping senders might be able to send
* due to a larger queue size.
*/
ss_wakeup(&msq->q_senders, 0);
break;
default:
err = -EINVAL;
}
out_unlock:
msg_unlock(msq);
out_up:
up_write(&msg_ids(ns).rw_mutex);
return err;
}
上面这段代码的重要作用就是删除消息队列,清除消息,我们重点看里面的几个调用的函数,其余的代码很简单,有基础的朋友可以看的懂,抓住重点来看,直接进入
static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
{
struct list_head *tmp;
struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
expunge_all(msq, -EIDRM);
ss_wakeup(&msq->q_senders, 1);
msg_rmid(ns, msq);
msg_unlock(msq);
tmp = msq->q_messages.next;
while (tmp != &msq->q_messages) {
struct msg_msg *msg = list_entry(tmp, struct msg_msg, m_list);
tmp = tmp->next;
atomic_dec(&ns->msg_hdrs);
free_msg(msg);
}
atomic_sub(msq->q_cbytes, &ns->msg_bytes);
security_msg_queue_free(msq);
ipc_rcu_putref(msq);
}
首先是expunge_all()函数
static void expunge_all(struct msg_queue *msq, int res)
{
struct list_head *tmp;
tmp = msq->q_receivers.next;
while (tmp != &msq->q_receivers) {
struct msg_receiver *msr;
msr = list_entry(tmp, struct msg_receiver, r_list);
tmp = tmp->next;
msr->r_msg = NULL;
wake_up_process(msr->r_tsk);
smp_mb();
msr->r_msg = ERR_PTR(res);
}
}
expunge_all是擦去的意思,这个函数的的作用就是循环从消息队列中的等待接收的进程开始,依次唤醒他们让他们返回,也就是消息的主体清场开始了,等待消息的客户请回吧。里面的smp与多处理器smp机制有关,这里不用关心,以后我们会接触它。然后执行下一个函数
static void ss_wakeup(struct list_head *h, int kill)
{
struct list_head *tmp;
tmp = h->next;
while (tmp != h) {
struct msg_sender *mss;
mss = list_entry(tmp, struct msg_sender, list);
tmp = tmp->next;
if (kill)
mss->list.next = NULL;
wake_up_process(mss->tsk);
}
}
清除所有在消除队列中的等待发送的进程,唤醒他们清场。下面执行
static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
{
ipc_rmid(&msg_ids(ns), &s->q_perm);
}
void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
{
int lid = ipcid_to_idx(ipcp->id);
idr_remove(&ids->ipcs_idr, lid);
ids->in_use--;
ipcp->deleted = 1;
return;
}
这里重点的函数就是idr_remove
void idr_remove(struct idr *idp, int id)
{
struct idr_layer *p;
/* Mask off upper bits we don't use for the search. */
id &= MAX_ID_MASK;
sub_remove(idp, (idp->layers - 1) * IDR_BITS, id);
if (idp->top && idp->top->count == 1 && (idp->layers > 1) &&
idp->top->ary[0]) { // We can drop a layer
p = idp->top->ary[0];
idp->top->bitmap = idp->top->count = 0;
free_layer(idp, idp->top);
idp->top = p;
--idp->layers;
}
while (idp->id_free_cnt >= IDR_FREE_MAX) {
p = alloc_layer(idp);
kmem_cache_free(idr_layer_cache, p);
}
return;
- 4--消息队列(报文队列)实践到内核--消息队列的控制
- 4--消息队列(报文队列)实践到内核--消息队列的控制
- 2--消息队列(报文队列)实践到内核--消息的发送
- 3--消息队列(报文队列)实践到内核--消息的接收
- 2--消息队列(报文队列)实践到内核--消息的发送
- 3--消息队列(报文队列)实践到内核--消息的接收 .
- 1--消息队列(报文队列)实践到内核--消息队列的创建
- 1--消息队列(报文队列)实践到内核--消息队列的创建 .
- 1消息队列(报文队列)实践到内核消息队列的创建
- 消息队列(报文队列)实践到内核
- linux消息队列的控制
- 消息队列ZeroMQ实践
- 阿里云消息队列的实践应用
- linux IPC-消息队列 的内核限制
- linux消息队列的内核限制
- linux IPC-消息队列 的内核限制
- linux消息队列的内核限制
- linux消息队列的内核限制
- android“设置”里的版本号
- 解决IE8 Session共享问题
- 1--消息队列(报文队列)实践到内核--消息队列的创建 .
- 2--消息队列(报文队列)实践到内核--消息的发送
- 3--消息队列(报文队列)实践到内核--消息的接收 .
- 4--消息队列(报文队列)实践到内核--消息队列的控制
- 大数相加
- 1--共享内存的实践到内核--共享内存的创建
- java反射详解
- 2--共享内存的实践到内核--共享内存的映射
- MarshalAs
- Displaying the current Vim environment
- 非阻塞Connect对于select时应注意问题(转)
- html转义字符