徐州做网站的培训机构,如何网上赚点零花钱,谭木记网页制作教程,海兴网站建设1.网络接口卡接收和发送数据在Linux内核中的处理流程如下#xff1a; 1. 网络接口卡#xff08;Network Interface Card, NIC#xff09; 作用#xff1a;负责物理层的数据传输#xff0c;将数据包从网络介质#xff08;如以太网线#xff09;读取到内存中#xff0c;或…1.网络接口卡接收和发送数据在Linux内核中的处理流程如下 1. 网络接口卡Network Interface Card, NIC 作用负责物理层的数据传输将数据包从网络介质如以太网线读取到内存中或者将内存中的数据包发送到网络介质上。过程 接收数据包当NIC接收到数据包时它会触发一个中断ei_interrupt通知内核有数据包到达。发送数据包当NIC需要发送数据包时它会从内存中读取数据包并通过网络介质发送出去。 2. 中断处理ei_interrupt 作用处理NIC触发的中断将接收到的数据包传递给内核协议栈。过程 当NIC接收到数据包时会触发ei_interrupt中断处理程序。中断处理程序会将数据包封装成一个SKBSocket Buffer并将其传递给内核协议栈进行进一步处理。 3. Socket BufferSKB 作用SKB是Linux内核中用于存储网络数据包的数据结构它包含了数据包的头部信息和数据内容。过程 在接收数据包时ei_interrupt会将数据包封装成SKB。在发送数据包时ei_start_xmit会从SKB中读取数据包并传递给NIC进行发送。 4. 内核协议栈Kernel Protocol Stack 作用负责处理网络协议如TCP/IP对数据包进行解析、封装和转发。过程 接收数据包从SKB中读取数据包进行协议解析和处理然后将数据传递给用户空间的应用程序。发送数据包从用户空间的应用程序接收数据进行协议封装然后将数据包封装成SKB并传递给NIC进行发送。 5. 用户空间应用程序User Space Application 作用运行在用户空间的应用程序与网络进行交互。过程 接收数据包从内核协议栈接收数据包进行应用层处理。发送数据包将数据传递给内核协议栈进行网络传输。 6. ei_start_xmit 作用负责将数据包从内核协议栈传递给NIC进行发送。过程 当内核协议栈需要发送数据包时会调用ei_start_xmit函数。ei_start_xmit会从SKB中读取数据包并将其传递给NIC进行发送。 7. net_device和hard_start_xmit 作用net_device是Linux内核中表示网络设备的数据结构hard_start_xmit是net_device中的一个函数指针用于实际的数据包发送操作。过程 当ei_start_xmit需要发送数据包时会调用net_device中的hard_start_xmit函数。hard_start_xmit会将数据包传递给NIC进行实际的发送操作。 Linux内核直接把中断分成了两个部分中断上半部和中断下半部。 在Linux内核中中断处理被分为两个部分中断上半部Top Half和中断下半部Bottom Half。这种设计的主要目的是为了提高系统的响应速度和效率避免在中断处理过程中长时间占用CPU导致其他任务无法执行。下面是对这两个部分的详细讲解 1. 中断上半部Top Half 作用处理中断的紧急部分确保系统的实时性和响应速度。特点 优先级高中断上半部的优先级非常高可以立即抢占其他任务的执行。执行时间短中断上半部的执行时间非常短通常只进行一些简单的操作如保存现场、读取硬件状态等。不能睡眠中断上半部不能执行任何可能引起睡眠的操作如等待I/O完成、申请内存等。 过程 当硬件设备如网络接口卡触发中断时CPU会立即停止当前任务的执行转而执行中断上半部的处理程序。中断上半部会进行一些紧急的操作如保存现场、读取硬件状态、记录中断信息等。完成紧急操作后中断上半部会立即返回继续执行被中断的任务。 2. 中断下半部Bottom Half 作用处理中断的非紧急部分完成中断的后续处理工作。特点 优先级低中断下半部的优先级较低不会立即抢占其他任务的执行。执行时间长中断下半部的执行时间较长可以进行一些复杂的操作如数据处理、内存申请等。可以睡眠中断下半部可以执行可能引起睡眠的操作如等待I/O完成、申请内存等。 过程 中断上半部在完成紧急操作后会将一些后续处理工作交给中断下半部。中断下半部会在适当的时候如当前任务执行完毕、系统空闲时被调度执行。中断下半部会进行一些复杂的操作如数据处理、内存申请等完成中断的后续处理工作。 2.Linux内核网络设备驱动框架分为四个模块分别为网络协议接口模块、网络设备接口模块、设备驱动功能模块、网络设备与媒介模块。 1. 网络协议接口模块 dev_queue_xmit(): 这个函数负责将数据包从协议层传递到设备层。它会调用设备驱动程序的发送函数将数据包放入设备的发送队列中。netif_rx(): 这个函数负责将数据包从设备层传递到协议层。它通常在中断处理程序中被调用将接收到的数据包传递给协议栈进行处理。 2. 网络设备接口模块 net_device结构体类型: 这是Linux内核中表示网络设备的数据结构。它包含了设备的各种属性和方法如设备名、硬件地址、发送和接收函数等。这个结构体是连接协议层和设备层的关键。 3. 网络驱动功能模块 hard_start_xmit(): 这是设备驱动程序中的发送函数。它负责将数据包从设备的发送队列中取出并将其发送到物理设备上。中断处理: 当物理设备接收到数据包时会产生中断。中断处理程序会调用netif_rx()函数将接收到的数据包传递给协议栈进行处理。 4. 网络设备与媒介模块 网络物理设备: 这是实际的网络硬件设备如网卡、无线网卡等。它负责将数据包转换为电信号或无线信号并通过物理媒介如网线、无线信号进行传输。 3.网络协议接口模块 主要功能给上层协议提供透明的数据包发送和接收的接口dev_queue_xmit()用于发送数据包netif_rx()/netif_recieve_skb()用于接收数据包。不管是发送还是接收数据包都会使用到sk_buff结构体类型(套接字缓冲区)主要用在网络子系统中别的各层之间传输数据。 1. 数据包发送接口 dev_queue_xmit 功能与原型 功能将构造好的数据包sk_buff加入发送队列最终由底层驱动通过ndo_start_xmit方法发送。原型 int dev_queue_xmit(struct sk_buff *skb);参数skb为包含待发送数据的缓冲区指针。返回值0表示成功加入队列负值表示失败。 使用步骤 构造sk_buff需填充协议头如IP、TCP/UDP和数据内容并关联网络设备skb-dev。调用发送接口 struct sk_buff *skb alloc_skb(len, GFP_ATOMIC);// 填充skb数据如通过skb_put、skb_push等skb-dev dev; // 关联网络设备int ret dev_queue_xmit(skb);if (ret 0) {// 错误处理如释放skb}注意dev_queue_xmit最终调用驱动实现的ndo_start_xmit函数完成实际发送。 2. 数据包接收接口 netif_rx 功能与原型 功能将底层驱动接收到的数据包sk_buff提交给协议栈处理。原型 int netif_rx(struct sk_buff *skb);参数skb为包含接收数据的缓冲区指针。返回值表示接收队列状态如NET_RX_SUCCESS或NET_RX_DROP。 使用步骤 分配并填充sk_buff struct sk_buff *skb dev_alloc_skb(len 2); // 分配缓冲区if (!skb) {// 内存不足处理return;}skb_reserve(skb, 2); // 预留协议头空间// 从硬件读取数据到skb-dataskb_put(skb, len); // 设置数据长度skb-protocol eth_type_trans(skb, dev); // 设置协议类型如ETH_P_IP[[15, 18]]提交到协议栈 int result netif_rx(skb);if (result NET_RX_DROP) {// 数据包被丢弃处理}注意调用netif_rx后驱动不可再访问skb协议栈会接管其生命周期。 功能和作用 数据包封装与解封装sk_buff 结构体用于封装和解封装网络数据包包括各种网络协议头如MAC头、网络层头、传输层头等。数据包传输在网络栈中数据包在不同层之间传递时sk_buff 结构体作为数据包的载体确保数据包在传输过程中的完整性和一致性。数据包管理sk_buff 结构体还包含了数据包的长度、引用计数、缓存区管理等信息用于数据包的管理和调度。网络协议处理在数据包的发送和接收过程中sk_buff 结构体用于存储和处理各种网络协议相关的数据和状态信息 1. alloc_skb 功能 alloc_skb 是一个用于分配网络缓冲区sk_buff的函数。它是一个方便的封装函数用于简化 _alloc_skb 的调用。alloc_skb 会根据指定的大小和优先级分配一个 sk_buff 结构体。 参数 size: 需要分配的缓冲区大小。priority: 分配掩码用于指定分配的优先级。 使用案例 #include linux/skbuff.hstatic inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)
{return __alloc_skb(size, priority, 0, NUMA_NO_NODE);
}// 使用示例
struct sk_buff *skb alloc_skb(1024, GFP_ATOMIC);
if (skb) {// 成功分配了缓冲区可以进行后续操作// ...kfree_skb(skb); // 使用完毕后释放缓冲区
}2. dev_alloc_skb 功能 dev_alloc_skb 是一个用于分配网络设备缓冲区的函数。它是一个遗留的辅助函数用于简化 netdev_alloc_skb 的调用。 参数 length: 需要分配的缓冲区长度。 使用案例 #include linux/skbuff.hstatic inline struct sk_buff *dev_alloc_skb(unsigned int length)
{return netdev_alloc_skb(NULL, length);
}// 使用示例
struct sk_buff *skb dev_alloc_skb(1024);
if (skb) {// 成功分配了缓冲区可以进行后续操作// ...kfree_skb(skb); // 使用完毕后释放缓冲区
}3. netdev_alloc_skb 功能 netdev_alloc_skb 是一个用于分配网络设备缓冲区的函数。它根据指定的网络设备和长度分配一个 sk_buff 结构体。 参数 dev: 指定的网络设备。length: 需要分配的缓冲区长度。 使用案例 #include linux/skbuff.hstatic inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, unsigned int length)
{return netdev_alloc_skb(dev, length, GFP_ATOMIC);
}// 使用示例
struct net_device *dev ...; // 获取网络设备
struct sk_buff *skb netdev_alloc_skb(dev, 1024);
if (skb) {// 成功分配了缓冲区可以进行后续操作// ...kfree_skb(skb); // 使用完毕后释放缓冲区
}4. struct net_device 功能 struct net_device 是一个用于描述网络设备的结构体。它包含了网络设备的各种属性和配置信息。 字段 name: 网络设备的名称。name_node: 网络设备名称节点。ifalias: 网络设备的别名。mem_end: 共享内存的结束地址。mem_start: 共享内存的起始地址。base_addr: 基地址。irq: 中断号。 使用案例 #include linux/netdevice.h// 使用示例
struct net_device *dev ...; // 获取网络设备// 打印网络设备的名称
printk(KERN_INFO Network device name: %s\n, dev-name);// 打印网络设备的中断号
printk(KERN_INFO Network device IRQ: %d\n, dev-irq);5.NAPI 结构体的整体功能
napi_struct结构体是Linux内核中用于网络设备驱动程序的一种机制旨在优化网络数据包的处理。它的主要功能包括
减少中断处理频率通过将数据包处理从中断上下文转移到轮询上下文减少中断处理的频率从而提高系统的性能。权重控制通过设置权重值控制在一次轮询中可以处理的数据包数量确保系统资源的合理分配。轮询机制提供一个轮询函数用于在轮询上下文中处理数据包避免在中断上下文中进行大量数据包处理。
NAPI 数据包信息的循环流程如下
数据接收中断发生当网络设备接收到数据包时触发数据接收中断。减半接收中断通过NAPI机制减少接收中断的频率将数据包处理转移到轮询上下文。以轮询试接收所有数据包或轮询权重耗尽在轮询上下文中通过轮询函数处理数据包直到处理完所有数据包或达到权重限制。开启接收中断当轮询处理完成后重新开启接收中断等待下一次数据接收中断的发生。
#include linux/netdevice.h
#include linux/napi.hstruct my_net_device {struct net_device *netdev;struct napi_struct napi;
};static int my_poll(struct napi_struct *napi, int budget)
{struct my_net_device *priv container_of(napi, struct my_net_device, napi);int work_done 0;// 处理数据包while (work_done budget) {// 从硬件中读取数据包struct sk_buff *skb my_read_skb_from_hardware();if (!skb)break;// 将数据包传递给上层协议栈netif_receive_skb(skb);work_done;}if (work_done budget) {// 如果处理的数据包数量小于预算值就停止轮询napi_complete_done(napi, work_done);}return work_done;
}static irqreturn_t my_irq_handler(int irq, void *dev_id)
{struct my_net_device *priv dev_id;// 触发轮询napi_schedule(priv-napi);return IRQ_HANDLED;
}static int my_netdev_open(struct net_device *netdev)
{struct my_net_device *priv netdev_priv(netdev);// 初始化napi_structnetif_napi_add(netdev, priv-napi, my_poll, 64);// 启用中断enable_irq(netdev-irq);return 0;
}static int my_netdev_close(struct net_device *netdev)
{struct my_net_device *priv netdev_priv(netdev);// 禁用中断disable_irq(netdev-irq);// 删除napi_structnetif_napi_del(priv-napi);return 0;
}