2

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

n = read(fd, buf, try_read_size);

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

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

4

1 回答 1

6

从高层次的角度来看,会发生以下情况:

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