【问题标题】:what happens after read is called for a Linux socketLinux 套接字调用 read 后会发生什么
【发布时间】:2012-04-19 10:36:21
【问题描述】:

调用 read 后实际发生的情况:

n = read(fd, buf, try_read_size);

这里的 fd 是一个 TCP 套接字描述符。 buf 是缓冲区。 try_read_size 是程序尝试读取的字节数。

我猜这最终可能会调用内核的系统调用。但是谁能提供一些细节?说glibc中的源码实现还是内核源码?

【问题讨论】:

  • 内核细节对于简短的 SO 答案来说太复杂了。顺便说一句,你为什么不自己检查一下?
  • @KarolyHorvath 我试过但完全迷路了。有什么方向或建议来获取详细信息吗?我将不胜感激。
  • 从 linux 内核源代码读取 fs/read_write.c 看看发生了什么。
  • 这只是通用的读取功能,不是吗? TCP 相关的东西必须在 net/ 某处。
  • @strkol 谢谢!我找到“SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)”(lxr.linux.no/#linux+v3.3.2/fs/read_write.c#L460)。深入研究其中的代码。

标签: linux sockets networking


【解决方案1】:

从高层次的角度来看,会发生这样的事情:

  • 调用 glibc 提供的包装函数
  • 包装函数将堆栈上传递的参数放入寄存器中,并在专用于该目的的寄存器中设置系统调用号(例如 x86 上的 EAX)
  • 包装函数执行陷阱或等效指令(例如 SYSENTER)
  • CPU 切换到 ring0,并调用陷阱处理程序
  • 陷阱处理程序检查系统调用号的有效性,并在内核函数的跳转表中查找它
  • 各自的内核函数检查参数是否有效(例如,bufbuf+try_read_size 的范围是指可访问的内存页,fd 实际上是一个文件描述符)。如果出现问题,则会生成一个负错误代码(例如 -EFAULT),cpu 会切换回用户模式,并且调用会返回到包装器。
  • 根据文件描述符的类型调用另一个函数(在您的情况下是套接字,但可以从块设备或 proc 条目或更奇特的东西中读取)
  • 检查套接字的输入缓冲区:
    • 如果缓冲区中有一些数据,min(available, try_read_size)被复制到buf,数量被写入返回码寄存器(EAX on x86),cpu切换回用户模式,调用返回包装器。
    • 如果输入缓冲区为空
      • 如果连接已关闭,则将零写入返回码寄存器,cpu 切换回用户模式,调用返回到包装器
      • 如果连接没有关闭
        • 如果套接字是非阻塞的,则将负错误代码 (-EAGAIN) 写入返回代码寄存器,CPU 切换回用户模式并且调用返回到包装器。
        • 如果套接字非阻塞,则进程暂停
  • 包装函数检查返回值是否为负(错误)。
    • 如果为正数或零,则返回该值。
    • 如果为负,则将 errno 设置为负值(报告 错误)并返回 -1

【讨论】:

  • +1,非常好。如果进程被挂起,它会阻塞直到至少一个字节的数据或接收到一个 FIN 或在套接字上发布一个错误,然后包含它的最外面的项目符号点重新启动。
猜你喜欢
  • 2020-11-22
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
  • 2021-08-22
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
  • 2010-10-21
相关资源
最近更新 更多