mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-11-09 08:38:04 +08:00
...
This commit is contained in:
1 parent
f4ee9e89c5
commit
16a152f96a
6 files changed
+843
-22
No files matched your search
@@ -375,7 +375,7 @@ void net_drop_ns(void *p)
|
||||
* 网络命名空间定义了2个链表, `pernet_list`和`net_namespace_list`
|
||||
|
||||
`init_net`会被链接到`net_namespace_list`这个双向链表上
|
||||
`pernet_operations`结构将被链接到`pernet_list`这个双向链表上
|
||||
`pernet_operations`结构将被链接到`first_device = pernet_list`这个双向链表上
|
||||
|
||||
* 如果没自定义网络命名空间的话,所有想用网络命名空间时都将利用默认的`init_net`
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,21 +0,0 @@
|
||||
进程虚拟地址空间
|
||||
=======
|
||||
|
||||
| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN |
|
||||
| ------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| 2016-06-14 | [Linux-4.7](http://lxr.free-electrons.com/source/?v=4.7) | X86 & arm | [gatieme](http://blog.csdn.net/gatieme) | [LinuxDeviceDrivers](https://github.com/gatieme/LDD-LinuxDeviceDrivers) | [Linux内存管理](http://blog.csdn.net/gatieme/article/category/6225543) |
|
||||
|
||||
|
||||
#1 网络访问层
|
||||
-------
|
||||
|
||||
前面讲述了Linux内核中网络子系统的结构, 现在我们把注意力转向**网络实现的第一层, 即网络访问层**. 该层主要负责在计算机之间传输信息,与网卡的设备驱动程序直接协作.
|
||||
|
||||
本节不会讨论网卡驱动程序的实现和相关的问题, 因为其中采用的方法与第6章的描述仅稍有不同. 本节将详细介绍由各个网卡驱动程序提供、由网络实现代码使用的接口,它们提供了硬件的抽象视图.
|
||||
|
||||
这里根据以太网帧来解释如何在"线上"(on the cable)表示数据,并描述接收到一个分组之后, 将该分组传递到更高层之前,需要完成哪些步骤.
|
||||
|
||||
#2 网络设备的表示
|
||||
-------
|
||||
|
||||
在内核中, 每个网络设备都表示为net_device结构的一个实例。在分配并填充该结构的一个实例之后,必须用net/core/dev.c中的register_netdev函数将其注册到内核。该函数完成一些初始化任务,并将该设备注册到通用设备机制内。这会创建一个sysfs项(参见10.3节)/sys/class/net/<device>,关联到该设备对应的目录。如果系统包含一个PCI网卡和一个环回接口设备,则在
|
||||
@@ -0,0 +1,93 @@
|
||||
进程虚拟地址空间
|
||||
=======
|
||||
|
||||
| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN |
|
||||
| ------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| 2016-06-14 | [Linux-4.7](http://lxr.free-electrons.com/source/?v=4.7) | X86 & arm | [gatieme](http://blog.csdn.net/gatieme) | [LinuxDeviceDrivers](https://github.com/gatieme/LDD-LinuxDeviceDrivers) | [Linux内存管理](http://blog.csdn.net/gatieme/article/category/6225543) |
|
||||
|
||||
|
||||
#2 接受分组
|
||||
-------
|
||||
|
||||
|
||||
分组到达内核的时间是不可预测的. 所有现代的设备驱动程序都使用中断来通知内核(或系统)有分组到达. 网络驱动程序对特定于设备的中断设置了一个处理例程, 因此每当该中断被引发时(即分组到达), 内核都调用该处理程序, 将数据从网卡传输到物理内存, 或通知内核在一定时间后进行处理.
|
||||
|
||||
几乎所有的网卡都支持DMA模式, 能够自行将数据传输到物理内存. 但这些数据仍然需要解释和处理,这在稍后进行.
|
||||
|
||||
##2.1 传统方法
|
||||
-------
|
||||
|
||||
当前, 内核为分组的接收提供了两个框架. 其中一个很早以前就集成到内核中了, 因而称为传统方法. 但与超高速网络适配器协作时, 该API会出现问题,因而网络子系统的开发者已经设计了一种新的API(通常称为NAPI 1). 我们首先从传统方法开始, 因为它比较易于理解. 另外, 使用旧API的适配器较多, 而使用新API的较少. 这没有问题, 因为其物理传输速度没那么高, 不需要新方法. NAPI在稍后讨论
|
||||
|
||||
图12-10给出了在一个分组到达网络适配器之后,该分组穿过内核到达网络层函数的路径
|
||||
|
||||
|
||||
因为分组是在中断上下文中接收到的, 所以处理例程只能执行一些基本的任务,避免系统(或当前CPU)的其他任务延迟太长时间.
|
||||
|
||||
在中断上下文中, 数据由3个短函数2处理, 执行了下列任务.
|
||||
|
||||
![接收到的分组穿过内核的路径]()
|
||||
|
||||
|
||||
1. net_interrupt是由设备驱动程序设置的中断处理程序. 它将确定该中断是否真的是由接收到的分组引发的(也存在其他的可能性, 例如, 报告错误或确认某些适配器执行的传输任务). 如果确实如此,则控制将转移到`net_rx`.
|
||||
|
||||
2. `net_rx`函数也是特定于网卡的, 首先创建一个新的套接字缓冲区. 分组的内容接下来从网卡传输到缓冲区(也就是进入了物理内存), 然后使用内核源代码中针对各种传输类型的库函数来分析首部数据. 这项分析将确定分组数据所使用的网络层协议,例如IP协议.
|
||||
|
||||
3. 与上述两个方法不同, `netif_rx`函数不是特定于网络驱动程序的,该函数位于`net/core/dev.c`. 调用该函数,标志着控制由特定于网卡的代码转移到了网络层的通用接口部分.
|
||||
|
||||
该函数的作用在于, 将接收到的分组放置到一个特定于CPU的等待队列上, 并退出中断上下文, 使得CPU可以执行其他任务.
|
||||
|
||||
内核在全局定义的softnet_data数组中管理进出分组的等待队列, 数组项类型为softnet_data. 为提高多处理器系统的性能, 对每个CPU都会创建等待队列, 支持分组的并行处理.
|
||||
|
||||
不心使用显式的锁机制来保护等待队列免受并发访问, 因为每个CPU都只修改自身的队列, 不会干扰其他CPU的工作. 下文将忽略多处理器相关内容, 只考虑单"softnet_data等待队列", 避免过度复杂化.
|
||||
|
||||
目前只对该数据结构的一个成员感兴趣:
|
||||
|
||||
```cpp
|
||||
<netdevice.h>
|
||||
struct softnet_data
|
||||
{
|
||||
...
|
||||
struct sk_buff_head input_pkt_queue;
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
`input_pkt_queue`使用上文提到的`sk_buff_head`表头, 对所有进入的分组建立一个链表.
|
||||
|
||||
`netif_rx`在结束工作之前将软中断`NET_RX_SOFTIRQ`标记为即将执行, 然后退出中断上下文.
|
||||
|
||||
`net_rx_action`用作该软中断的处理程序. 其代码流程图在图12-11给出. 请记住, 这里描述的是一个简化的版本. 完整版包含了对高速网络适配器引入的新方法, 将在下文介绍.
|
||||
|
||||
|
||||
![图12-11 net_rx_action 的代码流程图]()
|
||||
|
||||
|
||||
在一些准备任务之后,工作转移到`process_backlog`, 该函数在循环中执行下列步骤. 为简化描述, 假定循环一直进行, 直至所有的待决分组都处理完成,不会被其他情况中断.
|
||||
|
||||
1. `__skb_dequeue`从等待队列移除一个套接字缓冲区, 该缓冲区管理着一个接收到的分组.
|
||||
|
||||
2. 由`netif_receive_skb`函数分析分组类型, 以便根据分组类型将分组传递给网络层的接收函数(即传递到网络系统的更高一层). 为此, 该函数遍历所有可能负责当前分组类型的所有网络层函数, 一一调用`deliver_skb`.
|
||||
|
||||
接下来`deliver_skb`函数使用一个特定于分组类型的处理程序`func`, 承担对分组的更高层(例如互联网络层)的处理.
|
||||
|
||||
`netif_receive_skb`也处理诸如桥接之类的专门特性, 但讨论这些边角情况是不必要的, 至少在平均水准的系统中, 此类特性都属于边缘情况.
|
||||
|
||||
所有用于从底层的网络访问层接收数据的网络层函数都注册在一个散列表中, 通过全局数组`ptype_base`实现.
|
||||
|
||||
新的协议通过`dev_add_pack`增加. 各个数组项的类型为`struct packet_type`, 定义如下:
|
||||
|
||||
```cpp
|
||||
<netdevice.h>
|
||||
struct packet_type {
|
||||
__be16
|
||||
struct net_device
|
||||
int
|
||||
```
|
||||
|
||||
type 指定了协议的标识符,处理程序会使用该标识符。 dev 将一个协议处理程序绑定到特定的网卡
|
||||
( NULL 指针表示该处理程序对系统中所有网络设备都有效)
|
||||
。
|
||||
func 是该结构的主要成员。它是一个指向网络层函数的指针,如果分组的类型适当,将其传递给
|
||||
该函数。其中一个处理程序就是 ip_rcv ,用于基于IPv4的协议,在下文讨论。
|
||||
netif_receive_skb 对给定的套接字缓冲区查找适当的处理
|
||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Reference in new issue
Block a user