首页 > 解决方案 > 引擎盖下的阻塞和非阻塞文件描述符

问题描述

我已经花了几个星期研究 Linux tcp/ip 堆栈的工作原理。对我来说最困难的部分是网络套接字。我有很多问题,但我会尽我所能,尽可能具体。为简单起见,让我们谈谈 udp 套接字。

  1. 据我了解recvfrom(),这只是read()将数据从内核内的套接字缓冲区复制到用户空间的系统调用的一个例子。我查看了实现,所有用户空间操作都在该点结束sock->ops->some-protocol-dependent-operation(即sock->ops->recvmsg)。如果我错了,请纠正我。
  2. 接下来会发生什么?从逻辑上讲,我们需要检查套接字是否有数据给我们,然后根据内核 TCP/IP 堆栈中实现的协议(UDP)结构返回(复制)它。如果我错过了重要的事情,请再次纠正我。
  3. 这对我来说是“最黑暗”的部分。我知道套接字(粗略地说打开的文件描述符)可能是阻塞的和非阻塞的。我知道默认情况下网络套接字是阻塞的(为什么会这样?)。我无法理解的是内核如何阻止它们。我试图在 Linux 内核源代码中找到它,但这很难。我从一堆相同的问题中了解到:当 NIC 有数据到达时,它会向 cpu 发出信号,并通过网络堆栈将其传递到 TCP 层。在那个级别上,我们打开了套接字,我们的网络堆栈将传入的数据复制到套接字缓冲区。然后一些神奇的系统(名称是什么以及它如何轮询缓冲区?)表示fd上有一个事件,因此recvfrom变得畅通无阻。下一个问题:是recvfrom只是让进程休眠(使用什么机制?sigwait?)。或者这个堆栈跟踪内部的某个地方是否有一个长轮询?
  4. 将 fd 转换为非阻塞模式是否意味着在内部某处使用 epoll?为什么我不能只在我的程序中使用它来检查 fd 是否准备好然后调用 recvfrom ?或者这是一个不好的做法?

伙计们,真的很抱歉这么广泛的话题,但一切都是相互关联的,我不能把它分成不同的部分。

标签: clinuxsocketstcpnetwork-programming

解决方案


我认为您对第一个问题有正确的方法

然后,UDP 没有连接 -> 你只需要一个 recvfrom ()。但是 TCP 已连接,因此您将“保持”您的连接,因此您可能还需要几个 recv() (因此进行循环)[我真的不知道这是否是您想知道的... ]

recv() 和 recvfrom() 是阻塞函数(它们等待新消息),因此要解除阻塞,您需要 fcntl () [例如:fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK)]。然后,您可以验证您是否有带有循环的新消息。但我认为使用 select () 更容易(你不必使用 fcntl() 并且它只允许在活动 [reception here] 激活时使用套接字)。

我希望这能回答你的问题(我希望我说的没什么不好)


推荐阅读