芯片运行在极限的边缘,怪异问题集(一)创建消息队列,错误码24

现象描述:板子中原本运行有A、B、C三个进程。其中A进程优先级比较高。现在将A的优先级恢复到普通优先级后,再添加D进程。发现B、C会随机启动不起来。将A优先级提高或删掉D后,又全部可以正常运行。

进一步分析log,返现B、C起不来是因为mq_open失败,返回24,即EMFILE。

解决方案:步骤1:按照常规解决问题的方法,先查EMFILE的原因,大概意思是进程打开文件数量超过系统限制(我的开发板是1024个),但是我的进程压根没有打开多少文件。所以,这应该是不可能的。增大文件数量的限制,果然没有效果。

步骤2:分析mq_open系统调用源码:sys_mq_open调用do_create,do_create调用vfs_create,vfs_create调用inode_operations的create,即mqueue_create

static const struct inode_operations mqueue_dir_inode_operations = {
\t.lookup = simple_lookup,
\t.create = mqueue_create,
\t.unlink = mqueue_unlink,
};

mqueue_create调用了我们的主角mqueue_get_inode,重点分析该函数:

static struct inode *mqueue_get_inode(struct super_block *sb,
\t\tstruct ipc_namespace *ipc_ns, umode_t mode,
\t\tstruct mq_attr *attr)
{
\tstruct user_struct *u = current_user();//获取当前用户
\tstruct inode *inode;
\tint ret = -ENOMEM;
\tinode = new_inode(sb);//为queue创建一个文件节点
\tif (!inode)
\t\tgoto err;
\tinode->i_ino = get_next_ino();
\tinode->i_mode = mode;
\tinode->i_uid = current_fsuid();
\tinode->i_gid = current_fsgid();

\tinode->i_mtime = inode->i_ctime = inode->i_atime = CURRENT_TIME;
\tif (S_ISREG(mode)) {
\t\tstruct mqueue_inode_info *info;
\t\tunsigned long mq_bytes, mq_treesize;
\t\tinode->i_fop = &mqueue_file_operations;
\t\tinode->i_size = FILENT_SIZE;
\t\t/* mqueue specific info */
\t\tinfo = MQUEUE_I(inode);
\t\tspin_lock_init(&info->lock);
\t\tinit_waitqueue_head(&info->wait_q);
\t\tINIT_LIST_HEAD(&info->e_wait_q[0].list);
\t\tINIT_LIST_HEAD(&info->e_wait_q[1].list);
\t\tinfo->notify_owner = NULL;
\t\tinfo->notify_user_ns = NULL;
\t\tinfo->qsize = 0;
\t\tinfo->user = NULL;\t/* set when all is ok */
\t\tinfo->msg_tree = RB_ROOT;
\t\tinfo->node_cache = NULL;
\t\tmemset(&info->attr, 0, sizeof(info->attr));
\t\tinfo->attr.mq_maxmsg = min(ipc_ns->mq_msg_max,
\t\t\t\t\t ipc_ns->mq_msg_default);
\t\tinfo->attr.mq_msgsize = min(ipc_ns->mq_msgsize_max,
\t\t\t\t\t ipc_ns->mq_msgsize_default);
\t\tif (attr) {
\t\t\tinfo->attr.mq_maxmsg = attr->mq_maxmsg;
\t\t\tinfo->attr.mq_msgsize = attr->mq_msgsize;
\t\t}
\t
\t\tmq_treesize = info->attr.mq_maxmsg * sizeof(struct msg_msg) +
\t\t\tmin_t(unsigned int, info->attr.mq_maxmsg, MQ_PRIO_MAX) *
\t\t\tsizeof(struct posix_msg_tree_node);
\t\tmq_bytes = mq_treesize + (info->attr.mq_maxmsg *
\t\t\t\t\t info->attr.mq_msgsize);
\t\tspin_lock(&mq_lock);
//下面这个坑爹的判断,同一个用户创建的所有queue的需要的内存大小如果超过了当前进程的最大queue的大小限制的话,就会失败。错误码24!!!!!!!!!!!!
\t\tif (u->mq_bytes + mq_bytes < u->mq_bytes ||
\t\t u->mq_bytes + mq_bytes > rlimit(RLIMIT_MSGQUEUE)) {
\t\t\tspin_unlock(&mq_lock);
\t\t\t/* mqueue_evict_inode() releases info->messages */
\t\t\tret = -EMFILE;//错误码24!!!!!!!!!!!!!
\t\t\tgoto out_inode;
\t\t}
\t\tu->mq_bytes += mq_bytes;
\t\tspin_unlock(&mq_lock);
\t\t/* all is ok */

\t\tinfo->user = get_uid(u);
\t} else if (S_ISDIR(mode)) {
\t\tinc_nlink(inode);
\t\t/* Some things misbehave if size == 0 on a directory */
\t\tinode->i_size = 2 * DIRENT_SIZE;
\t\tinode->i_op = &mqueue_dir_inode_operations;
\t\tinode->i_fop = &simple_dir_operations;
\t}
\treturn inode;
out_inode:
\tiput(inode);
err:
\treturn ERR_PTR(ret);
}

到这里,原因已经找到了。因此,将所有进程的RLIMIT_MSGQUEUE搞大就可以解决问题。这些进程都是由同一个父进程启动的,因此只要在父进程中修改RLIMIT_MSGQUEUE。

但是这里还有最后一个问题,为什么去掉A进程的优先级后,也会影响queue size的限制呢?

查看A进程的代码返现,它中间调用了如下代码改变了自己创建的queue的所有者,去掉实时调度优先级后,下面的代码来不及执行,加大了出现问题的概率,(A进程有实时调度也会出现该问题,只是概率很小),坑爹的外包啊。

if (chown(inputChannel.c_str(), 0, userInfo->pw_gid) == -1) {
\t\t\tMM_LOG_ERROR(ATB3_CONTEXT_INBOUND, MM_MSG("Failed to change group id on '" << inputChannel << "' (" << strerror(errno) << ")."));
\t\t\tthrow std::runtime_error("Failed to change group id");
\t\t}
芯片运行在极限的边缘,怪异问题集(一)创建消息队列,错误码24


分享到:


相關文章: