当前位置:首页 > 嵌入式 > Linux阅码场
[导读]本系列文章1-4,来源于陈莉君老师公众号“Linux内核之旅”1.前言本文首先从宏观上概述了数据包发送的流程,接着分析了协议层注册进内核以及被socket的过程,最后介绍了通过socket发送网络数据的过程。2.数据包发送宏观视角从宏观上看,一个数据包从用户程序到达硬件网卡的整个...

本系列文章1-4,来源于陈莉君老师公众号“Linux内核之旅”

1. 前言

本文首先从宏观上概述了数据包发送的流程,接着分析了协议层注册进内核以及被socket的过程,最后介绍了通过 socket 发送网络数据的过程。

2. 数据包发送宏观视角

从宏观上看,一个数据包从用户程序到达硬件网卡的整个过程如下:

  1. 使用系统调用(如 sendtosendmsg 等)写数据

  2. 数据穿过socket 子系统,进入socket 协议族(protocol family)系统

  3. 协议族处理:数据穿过协议层,这一过程(在许多情况下)会将数据(data)转换成数据包(packet)

  4. 数据穿过路由层,这会涉及路由缓存和 ARP 缓存的更新;如果目的 MAC 不在 ARP 缓存表中,将触发一次 ARP 广播来查找 MAC 地址

  5. 穿过协议层,packet 到达设备无关层(device agnostic layer)

  6. 使用 XPS(如果启用)或散列函数选择发送队列

  7. 调用网卡驱动的发送函数

  8. 数据传送到网卡的 qdisc(queue discipline,排队规则)

  9. qdisc 会直接发送数据(如果可以),或者将其放到队列,下次触发NET_TX 类型软中断(softirq)的时候再发送

  10. 数据从 qdisc 传送给驱动程序

  11. 驱动程序创建所需的DMA 映射,以便网卡从 RAM 读取数据

  12. 驱动向网卡发送信号,通知数据可以发送了

  13. 网卡从 RAM 中获取数据并发送

  14. 发送完成后,设备触发一个硬中断(IRQ),表示发送完成

  15. 硬中断处理函数被唤醒执行。对许多设备来说,这会触发 NET_RX 类型的软中断,然后 NAPI poll 循环开始收包

  16. poll 函数会调用驱动程序的相应函数,解除 DMA 映射,释放数据

3. 协议层注册

协议层分析我们将关注 IP 和 UDP 层,其他协议层可参考这个过程。我们首先来看协议族是如何注册到内核,并被 socket 子系统使用的。

当用户程序像下面这样创建 UDP socket 时会发生什么?

sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
简单来说,内核会去查找由 UDP 协议栈导出的一组函数(其中包括用于发送和接收网络数据的函数),并赋给 socket 的相应字段。准确理解这个过程需要查看 AF_INET 地址族的代码。

内核初始化的很早阶段就执行了 inet_init 函数,这个函数会注册 AF_INET 协议族 ,以及该协议族内的各协议栈(TCP,UDP,ICMP 和 RAW),并调用初始化函数使协议栈准备好处理网络数据。inet_init 定义在net/ipv4/af_inet.c 。

AF_INET 协议族导出一个包含 create 方法的 struct net_proto_family 类型实例。当从用户程序创建 socket 时,内核会调用此方法:

static const struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
inet_create 根据传递的 socket 参数,在已注册的协议中查找对应的协议:

/* Look for the requested type/protocol pair. */
lookup_protocol:
err = -ESOCKTNOSUPPORT;
rcu_read_lock();
list_for_each_entry_rcu(answer,
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读
关闭