在以太网端口上接收网络数据和 apache2 做某事之间会发生什么?
What happens between receiving network data on the ethernet port and apache2 doing something?
这个问题有点模糊,因为我自己的理解也差不多。我感兴趣的是网络电缆上的零星电压需要发生什么才能导致计算机上的程序 运行 执行某些操作。
假设我在我的网络服务器上 运行 apache2。有人在他们自己的联网计算机上触发了正确的事件序列,从而导致网络数据到达服务器。然后呢?
我的猜测是主板上有一些外围组件侦听数据,然后在 CPU 中引发中断。不知何故,在中断服务例程中,Linux 必须要求 apache2 代码做一些事情。这个对吗?如果是这样,有人愿意分享一些额外的细节吗?
谢谢
我将自下而上地概述发生的事情,尽可能引用代码。
第 1 层 (PHY)
以太网卡(NIC)接收并解码线上的信号,并将其压入移位寄存器
- 有关 *BASE-T 以太网每个变体的线路代码详细信息,请参阅 Ethernet over twisted-pair
当接收到完整的以太网帧时,它被放入硬件中的接收 (RX) 队列
- 网卡提出 interrupt, using bus-specific mechanism (either PCI IRQ line, or message-signaled interrupt)
- Interrupt controller (APIC) 接收中断并将其定向到 CPU
- CPU保存运行上下文并切换到中断上下文
- CPU 加载中断处理程序向量并开始执行它
IRQ 可以被多个设备共享。内核必须弄清楚哪个设备实际上正在中断。我将参考 e100.c
driver,因为它是在一个 C 文件中实现的,并且有很好的注释。
- Linux 内核查看共享此 IRQ 的所有设备,调用它们的驱动程序以确定哪个设备实际引发了此中断。调用的驱动程序函数是驱动程序传递给
request_irq
的任何内容。 (参见 __handle_irq_event_percpu()
中的 for_each_action_of_desc
())。
- 共享此 IRQ 的设备的每个驱动程序都将查看其设备的状态寄存器,以查看它们是否有待处理的中断
- NIC 驱动程序中断处理程序(例如
e100_intr()
) sees the NIC indeed interrupted. It disables the device interrupt (e.g. e100_disable_irq()
) and schedules a NAPI callback (__napi_schedule()
). NIC driver "claims" the interrupt by returning IRQ_HANDLED
。中断结束。
- Linux 内核 NAPI 子系统回调 NIC 驱动程序(例如
e100_poll
) which reads the packet from the NIC RX queue and puts it into a struct sk_buff
(SKB), and pushes it into the kernel network stack (e.g. e100_rx_indicate()
)。
出于性能原因,整个 TCP/IP 堆栈在 Linux 内核中实现:
第 2 层(MAC)
- 内核以太网层查看以太网数据包并验证它发往本机的 MAC 地址
- Ethernet以太网层看到Ethertype == IP,交给IP层
- 注意,协议实际上是由设备驱动程序设置的(例如
e100_indicate()
)。
第 3 层(IP)
- 内核IP层接收数据包(
ip_rcv()
)
- 内核IP层对所有IP片段进行排队
- 收到所有 IP 碎片后,它会处理 IP 数据包。它查看协议字段,发现是TCP,交给TCP层
第 4 层 (TCP)
- 内核 TCP 层接收数据包 (
tcp_v4_rcv()
)。
- 内核 TCP 层查看 src/dst IP/port 并将其与打开的 TCP 连接(套接字)匹配(
tcp_v4_rcv()
调用 __inet_lookup_skb()
)。
如果是SYN包(新连接):
- TCP 会看到有一个 listening socket open for port 80
- TCP 为此新连接创建一个新的连接对象
- 内核唤醒正在休眠的任务,阻塞在
accept
调用 - 或 select
如果不是SYN包(有数据):
- 内核在套接字上排队来自该段的 TCP 数据
- 内核唤醒睡眠中的任务,阻塞在
recv
调用 - 或 select
(sock_def_readable()
)
第 5 层(应用程序 - HTTP)
Apache (httpd
) 将唤醒,具体取决于它被阻塞的系统调用:
accept()
returns 当新的子连接可用时(这由一个名为 apr_socket_accept()
的包装器处理)
recv()
returns 当套接字有新数据时,已读入用户空间缓冲区
- Apache 处理缓冲区,解析 HTTP 协议字符串
其他资源
这个问题有点模糊,因为我自己的理解也差不多。我感兴趣的是网络电缆上的零星电压需要发生什么才能导致计算机上的程序 运行 执行某些操作。
假设我在我的网络服务器上 运行 apache2。有人在他们自己的联网计算机上触发了正确的事件序列,从而导致网络数据到达服务器。然后呢?
我的猜测是主板上有一些外围组件侦听数据,然后在 CPU 中引发中断。不知何故,在中断服务例程中,Linux 必须要求 apache2 代码做一些事情。这个对吗?如果是这样,有人愿意分享一些额外的细节吗?
谢谢
我将自下而上地概述发生的事情,尽可能引用代码。
第 1 层 (PHY)
以太网卡(NIC)接收并解码线上的信号,并将其压入移位寄存器
- 有关 *BASE-T 以太网每个变体的线路代码详细信息,请参阅 Ethernet over twisted-pair
当接收到完整的以太网帧时,它被放入硬件中的接收 (RX) 队列
- 网卡提出 interrupt, using bus-specific mechanism (either PCI IRQ line, or message-signaled interrupt)
- Interrupt controller (APIC) 接收中断并将其定向到 CPU
- CPU保存运行上下文并切换到中断上下文
- CPU 加载中断处理程序向量并开始执行它
IRQ 可以被多个设备共享。内核必须弄清楚哪个设备实际上正在中断。我将参考 e100.c
driver,因为它是在一个 C 文件中实现的,并且有很好的注释。
- Linux 内核查看共享此 IRQ 的所有设备,调用它们的驱动程序以确定哪个设备实际引发了此中断。调用的驱动程序函数是驱动程序传递给
request_irq
的任何内容。 (参见__handle_irq_event_percpu()
中的for_each_action_of_desc
())。 - 共享此 IRQ 的设备的每个驱动程序都将查看其设备的状态寄存器,以查看它们是否有待处理的中断
- NIC 驱动程序中断处理程序(例如
e100_intr()
) sees the NIC indeed interrupted. It disables the device interrupt (e.g.e100_disable_irq()
) and schedules a NAPI callback (__napi_schedule()
). NIC driver "claims" the interrupt by returningIRQ_HANDLED
。中断结束。 - Linux 内核 NAPI 子系统回调 NIC 驱动程序(例如
e100_poll
) which reads the packet from the NIC RX queue and puts it into astruct sk_buff
(SKB), and pushes it into the kernel network stack (e.g.e100_rx_indicate()
)。
出于性能原因,整个 TCP/IP 堆栈在 Linux 内核中实现:
第 2 层(MAC)
- 内核以太网层查看以太网数据包并验证它发往本机的 MAC 地址
- Ethernet以太网层看到Ethertype == IP,交给IP层
- 注意,协议实际上是由设备驱动程序设置的(例如
e100_indicate()
)。
- 注意,协议实际上是由设备驱动程序设置的(例如
第 3 层(IP)
- 内核IP层接收数据包(
ip_rcv()
) - 内核IP层对所有IP片段进行排队
- 收到所有 IP 碎片后,它会处理 IP 数据包。它查看协议字段,发现是TCP,交给TCP层
第 4 层 (TCP)
- 内核 TCP 层接收数据包 (
tcp_v4_rcv()
)。 - 内核 TCP 层查看 src/dst IP/port 并将其与打开的 TCP 连接(套接字)匹配(
tcp_v4_rcv()
调用__inet_lookup_skb()
)。
如果是SYN包(新连接):
- TCP 会看到有一个 listening socket open for port 80
- TCP 为此新连接创建一个新的连接对象
- 内核唤醒正在休眠的任务,阻塞在
accept
调用 - 或select
如果不是SYN包(有数据):
- 内核在套接字上排队来自该段的 TCP 数据
- 内核唤醒睡眠中的任务,阻塞在
recv
调用 - 或select
(sock_def_readable()
)
第 5 层(应用程序 - HTTP)
Apache (httpd
) 将唤醒,具体取决于它被阻塞的系统调用:
accept()
returns 当新的子连接可用时(这由一个名为apr_socket_accept()
的包装器处理)recv()
returns 当套接字有新数据时,已读入用户空间缓冲区- Apache 处理缓冲区,解析 HTTP 协议字符串