【问题标题】:recvmmsg() with timeout = 0 is equivalent to nonblocking recvmsg()?超时 = 0 的 recvmsg() 等价于非阻塞 recvmsg()?
【发布时间】:2014-11-23 09:39:01
【问题描述】:

我在 CentOS 6.5 上,内核版本为 kernel-2.6.32-431.el6。

我试图找到 recvmmsg() 的实现,希望我没有看错源 ~/rpmbuild/BUILD/kernel-2.6.32-431.el6/linux-2.6.32-431。 el6.x86_64/net/socket.c。如果我是,请指出正确的来源。

int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
                   unsigned int flags, struct timespec *timeout)
{
        int fput_needed, err, datagrams;
        struct socket *sock;
        struct mmsghdr __user *entry;
        struct compat_mmsghdr __user *compat_entry;
        struct msghdr msg_sys;
        struct timespec end_time;

        if (timeout &&
            poll_select_set_timeout(&end_time, timeout->tv_sec,
                                    timeout->tv_nsec))
                return -EINVAL;

        datagrams = 0;

        sock = sockfd_lookup_light(fd, &err, &fput_needed);
        if (!sock)
                return err;

        err = sock_error(sock->sk);
        if (err)
                goto out_put;

        entry = mmsg;
        compat_entry = (struct compat_mmsghdr __user *)mmsg;

        while (datagrams < vlen) {
                /*
                 * No need to ask LSM for more than the first datagram.
                 */
                if (MSG_CMSG_COMPAT & flags) {
                        err = __sys_recvmsg(sock, (struct msghdr __user *)compat_entry,
                                            &msg_sys, flags, datagrams);
                        if (err < 0)
                                break;
                        err = __put_user(err, &compat_entry->msg_len);
                        ++compat_entry;
                } else {
                        err = __sys_recvmsg(sock, (struct msghdr __user *)entry,
                                            &msg_sys, flags, datagrams);
                        if (err < 0)
                                break;
                        err = put_user(err, &entry->msg_len);
                        ++entry;
                }

                if (err)
                        break;
                ++datagrams;

                if (timeout) {
                        ktime_get_ts(timeout);
                        *timeout = timespec_sub(end_time, *timeout);
                        if (timeout->tv_sec < 0) {
                                timeout->tv_sec = timeout->tv_nsec = 0;
                                break;
                        }

                        /* Timeout, return less than vlen datagrams */
                        if (timeout->tv_nsec == 0 && timeout->tv_sec == 0)
                                break;
                }

                /* Out of band data, return right away */
                if (msg_sys.msg_flags & MSG_OOB)
                        break;
        }

out_put:
        fput_light(sock->file, fput_needed);

        if (err == 0)
                return datagrams;

        if (datagrams != 0) {
                /*
                 * We may return less entries than requested (vlen) if the
                 * sock is non block and there aren't enough datagrams...
                 */
                if (err != -EAGAIN) {
                        /*
                         * ... or  if recvmsg returns an error after we
                         * received some datagrams, where we record the
                         * error to return on the next call or if the
                         * app asks about it using getsockopt(SO_ERROR).
                         */
                        sock->sk->sk_err = -err;
                }

                return datagrams;
        }

        return err;
}

假设套接字上有 2 个数据包,我尝试:

timespec t = {0, 0};
recvmmsg(fd, mmsg, vlen, 0, &t);

然后,因为这些行:

                    if (timeout->tv_nsec == 0 && timeout->tv_sec == 0)
                            break;

recvmmsg() 只会返回 1 个数据包而不是 2 个。我说的对吗?

这似乎违背了 recvmmsg() 的主要目的之一——试图在数据包排队时减少系统调用的次数。

也许我错过了什么?

欢迎提出任何建议。

谢谢!

=============================

更新:

我尝试编写一小段 sn-p 代码,等待数据包排队,然后调用 recvmmsg() 并设置 timeout=0(非 NULL)。它仍然只拾取 1 个数据包。因此,我认为我正在查看正确的源代码。

【问题讨论】:

    标签: c sockets linux-kernel glibc centos6.5


    【解决方案1】:

    是的,你是对的。如果要读取队列中的所有数据包,我认为最好使用 NULL timeout + O_NONBLOCK

    【讨论】:

      【解决方案2】:

      没有。零超时意味着无穷大。

      【讨论】:

      • 从 recvmmsg 联机帮助页中引用:“如果超时为 NULL,则操作将无限期阻塞。”所以我的原始代码中的代码不会无限期地阻止recvmmsg。
      • 这是一个空超时参数。您的问题是关于零超时,我的回答也是如此。
      • 手册页和内核源代码都没有表明零超时将使 recvmmsg() 无限期阻塞。你能指出我在哪里指示吗?
      • Man recvmmsg:超时参数指向一个结构时间规范(参见clock_gettime(2)),它定义了接收操作的超时(秒加纳秒)(但请参见BUGS!)。 (这个时间间隔会向上取整到系统时钟粒度,内核调度延迟意味着阻塞间隔可能会被少量溢出。)如果超时为NULL,则操作无限期阻塞。
      【解决方案3】:

      忽略 recvmmsg 超时参数,必要时使用 SOL_SOCKET、SO_RCVTIMEO。工作正常。

      【讨论】:

        【解决方案4】:

        如果您想在接收缓冲区中接收最多 vlen 个消息而不阻塞,这将解决您的问题:

        recvmmsg(fd, mmsg, vlen, MSG_DONTWAIT, NULL);
        

        它将循环直到内部 recvmsg 调用返回一个错误代码(如 EAGAIN/EWOULDBLOCK)或直到 vlen 个消息在缓冲区中填充而没有阻塞。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-05-22
          • 1970-01-01
          • 1970-01-01
          • 2015-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多