Linux网络技术学习(五)—— 网络设备初始化(I)

2023-10-27


什么时候进行的设备初始化?

内核初始化图
在这里插入图片描述

1、当内核引导时,会执行start_kernel对一些子系统做初始化
2、start_kernel终止前会调用init内核线程,由其负责初始化的后续工作。

初始化任务中有三个部分:
1、引导期间选项
  调用两次parse_args(一次是直接调用,而另一次是通过parse_early_param间接调用)以处理引导加载程序(bootloaser)在引导期间传给内核的配置参数。
2、中断和定时器
  硬中断和软中断分别由init_IRQ和softirq_init做初始化。
3、初始化函数
  内核子系统及内建的设备驱动程序由do_initcalls初始化。free_init_mem会释放一块被无用程序所持有的内容。

run_init_process确定在系统上运行的第一进程,也就是其他进程的父进程(PID为1)一直运行直到系统做完工作。
通常运行程序是Init,管理员可以通过init=引导期间选项指定另一个不同程序。
不提供选项,内核会从一组众所周知的位置去执行init命令,如果找不到init,就会发生内核panic


设备注册和初始化

一个网络设备可以用,就必须被内核认可,并且关联正确的驱动程序。
驱动程序把驱动设备所需的所有信息存储在私有数据结构中,然后与其他需要此设备的内核组件交互。

1、注册和初始化任务的一部分由内核负责
2、其他部分由设备驱动程序负责

初始化分为:硬件初始化、软件初始化、功能初始化
1、硬件初始化
  由设备驱动程序和通用总线层(例,PCI或USB)合作完成。驱动有时会通过用户提供的参数协调,把每个设别的这类功能配置成IRQ和I/O地址,使其能与内核交互。
2、软件初始化
  在设备能使用之前,需要依赖开启和配置的网络协议(用户需要提供IP地址等配置信息)等。
3、功能初始化
  针对每个网络设备的配置。例:流量控制,可以决定封包加入及退出设备出口队列的方式。

NIC(网卡 Network Interface Card)初始化的基本目标

Linux内核中,每个设备都有一个net_device数据结构表示。net_device的分配以及内部字段的初始化,部分的是由设备驱动程序完成,部分是由内核函数完成。

设备驱动程序如何分配建立设备/内核通信所需的资源?
1、IRQ线
  NIC必须被分配一个IRQ,用于设备与内核之间的交互。虚拟设备不需要分配一个IRQ:例如内环设备,因其活动都在内部进行。
  /proc/interrupts文件可用于观察当前分派状态
2、I/O端口和内存注册
  驱动程序将其设备的一个内存区域(配置寄存器)映射到系统内存,使得驱动程序的读/写操作可以通过系统内存地址直接进行,简化代码。I/O端口和内存分别使用request_region和release_region注册和释放
在这里插入图片描述


设备与内核之间的交互

几乎所有设备(包括NIC)都采用两种与内核的交互方式:
1、轮询
  由内核端的驱动定期检查设备状态,查看是否发生了什么事情
2、中断
  由设备端驱动。当设备需要内核注意时,会向内核发送出一个硬件信号(产生中断事件)


硬件中断

每个中断事件都会运行一个函数,被称为中断处理函数,而中断处理函数必须按照设备的所需进行裁剪,因此由设备驱动程序安装。当设备驱动程序注册一个NIC时,会请求并分派一个IRQ。

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
        const char *name, void *dev)
void free_irq(unsigned int irq, void *dev_id)

request_irq函数会注册一个处理函数。首先确保所请求的中断是一个有效的中断,而且还没分配给另一个设备,除非这两个设备能够共享IRQ
free_irq函数会删除处理函数,给定的设备由dev_id标识。如果没有其他设备注册在该IRQ线,就关闭IRQ

在内核接收到中断通知时,会使用IRQ编号找出该驱动程序的中断处理函数并执行。为了找到处理函数,内核会把IRQ编号和函数处理函数之间的相连接关系存储在一张全局表中。相连关系可以是一对一或一对多。


中断类型

通过中断,NIC能够告知其驱动程序几种不同的事件:
1、接收一帧
  这是常见标准的情况
2、传输失败
  这种事件只有当被为二进制指数后退功能失败时,才由Ethernet设备产生(由NIC在硬件层实现)。驱动程序不会把这种通知信息转送到那些较高层的网络层,这些网络层会通过其他方式获取到传输失败。
3、DMA传输已成功完成
  使用同步传输时(无DMA),当该帧已上传至NIC,驱动程序会立刻知道
  使用DMA时,使用异步传输,设备驱动程序必须等待NIC发出明确的中断事件。
4、设备有足够内存处理新传输
  当出口队列没有足够空间保存一个最大尺寸的帧时,NIC设备驱动程序会停止出口队列而关闭传输。
  当内存可用时,该队列又会再次开启


传送节流方式为了改善效率

在系统中,设备驱动程序会在队列空间缺乏时关闭传输,同时要求NIC当可用内存大于给定量时(设备的MTU)发出一个中断,然后当中断到来时重启传输。
例:

// 平台: RK3568
// drivers/net/ethernet/3com/3c509.c
static netdev_tx_t
el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
   netif_stop_queue (dev);
   ......
    if (inw(ioaddr + TX_FREE) > 1536)
        netif_start_queue(dev);
    else
        /* Interrupt us when the FIFO has room for max-sized packet. */
        outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
    ......
}

驱动程序可以用netif_stop_queue停止设备队列,以此能禁止内核提交后续的传输请求。

驱动程序会检查该设备的内存是否足够的内存容纳一个1536个字节的包。

如果有,驱动程序会启动队列,允许内核再次提交传输请求。
否则,就会指示设备(配置寄存器),当条件满足时产生一个中断。中断处理函数将使用netif_start_queue重启设备队列。


中断共享

IRQ线是有限的资源。添加系统能容纳设备数目的简单方式,就是允许几台设备共享同一个IRQ。
每个设备会针对该IRQ将其自己的处理函数注册给内核,再由内核启用这些同一个共享IRQ的设备的所有函数。而不是由中断通知,寻找正确的设备,再启用其处理函数。

一组设备共享一条IRQ线时,所有这些设备的设备驱动程序都必须能力处理共享的IRQ(换言之,每当一个设备注册要使用一条IRQ线时,就必须明确说明其是否支持中断共享。)
当另一设备试图注册同一个IRQ编号时,如果此设备或者该IRQ当前所分派的设备无法共享IRQ,就会被拒绝。


IRQ处理函数映射的组织

IRQ处理函数的映射存储在一个向量表中,每一个IRQ都对应一个处理函数列表。
只有当多台设备共享同一个IRQ时,一个列表才会有一个以上的元素。向量的尺寸取决于具体的体系结构,可以从15变化到200以上。

// 定义在include/linux/interrupt.h
struct irqaction

// 定义在include/linux/irqdesc.h
struct irq_desc

// kernel/irq/manage.c
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)

在之前介绍的硬件中断函数request_irq函数就是包含__setup_irq的包裹函数,用一个irqaction结构体输入,然后将其插入至一个全局变量irq_desc。
handle_IRQ_event 处理中断并将其传给驱动程序的内核函数是依赖irq_desc结构体的。


irqaction结构体存储方式

在这里插入图片描述
对一个可能的IRQ都有一个irq_desc实例,每个成功注册的IRQ处理函数都有一个irqaction实例。irq_desc尺寸是由结构体中NR_IRQS指定
当给定IRQ编号(也就是irq_desc向量的给定元素)有一个以上的irqaction实例时,就需要中断共享。

irqaction数据结构的字段中存储哪些与IRQ处理例程相关的信息:

// 平台RK3288
typedef irqreturn_t (*irq_handler_t)(int, void *);
irq_handler_t      handler;                     
// handler成员为中断处理函数,由设备驱动程序所提供的函数,用于处理中断的通知信息
// 传入的参数: int irq(产生此通知信息的IRQ编号);void *dev_id(设备标识符。同一个驱动程序可能同时要负责不同的设备,需要设备ID来正确处理通知信息)

unsigned int       irq;
// 一组标识,取值为IRQF_*      定义在include/linux/interrupt.h
// IRQF_SHARED          当置位时,设备驱动程序可以处理共享的IRQ
// IRQF_IRQPOLL         中断用于轮询(出于性能考虑,只有首先在共享中断中注册的中断才被考虑)

void *dev_id
// 与此设备相关联的net_device数据结构的指针。声明为void *的原因是,不仅仅只有NIC设备使用IRQ,各种设备类型使用的不同的数据结构

struct irqaction    *next;
// 所有共享同一个IRQ编号的设备会用此指针链接成一个列表

const char *name;
// 设备名称。可以通过/proc/interrupts内容读取设备名称

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux网络技术学习(五)—— 网络设备初始化(I) 的相关文章

  • Ubuntu Python shebang 线不工作

    无法让 shebang 线在 Ubuntu 中为 python 脚本工作 我每次只收到命令未找到错误 test py usr bin env python print Ran which python usr bin python 在 sh
  • 如何在 Linux 上通过 FTP 递归下载文件夹 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • 如何才能将 TCP 连接返回到同一端口?

    机器是 RHEL 5 3 内核 2 6 18 有时我在 netstat 中注意到我的应用程序有连接 建立了 TCP 连接本地地址 and 国外地址是一样的 其他人也报告了同样的问题 症状与链接中描述的相同 客户端连接到本地运行的服务器的端口
  • grep 排除文件的数组参数

    我想从我的文件中排除一些文件grep命令 为此我使用参数 exclude excluded file ext 为了更容易阅读 我想使用包含排除文件的 bash 数组 EXCLUDED FILES excluded file ext 然后将
  • awk 在循环中使用时不打印任何内容[重复]

    这个问题在这里已经有答案了 我有一堆使用 file 1 a 1 txt 格式的文件 如下所示 A 1 B 2 C 3 D 4 并使用以下命令添加包含每个文件名称的新列 awk print FILENAME NF t 0 file 1 a 1
  • 为什么 Linux 原始套接字的 RX 环大小限制为 4GB?

    背景 我试图mmap 我的原始套接字的 RX 环形缓冲区64 bitLinux 应用程序 我的环由 4096 个块组成 每个块大小为 1MB 总共 4GB 请注意 每个 1MB 块中可以有许多帧 如果您好奇 请参阅此文档了解背景信息 htt
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String
  • 为什么 Linux 没有 DirectX API?

    在考虑现代显卡的 Windows 系统上 DirectX API 的驱动程序端实现时 我想知道为什么此实现在非 Windows 系统 尤其是 Linux 上不可用 由于明显缺乏此功能 我只能假设有一个我无视的充分理由 但在我的原始理解中 我
  • 如何为 Linux 桌面条目文件指定带有相对路径的图标?

    对于我的一个 Linux 应用程序 我有应用程序二进制文件 一个 launcher sh 脚本 针对 LD LIBRARY PATH 和一个 desktop 文件 所有这些都位于同一文件夹中 我想使用图标的相对路径而不是绝对路径 我试过了
  • 如何使用 Cloud Init 挂载未格式化的 EBS 卷

    Context 我正在使用https wiki jenkins io display JENKINS Amazon EC2 Plugin https wiki jenkins io display JENKINS Amazon EC2 Pl
  • 从 ttyUSB0 写入和读取,无法得到响应

    我对 Linux tty 不太有经验 我的环境是带有丰富 USB 串行的 Raspbian 什么有效 stty F dev ttyUSB0 38400 cu l dev ttyUSB0 s 38400 cu to dev ttyUSB0作品
  • 尽管 if 语句,Visual Studio 仍尝试包含 Linux 标头

    我正在尝试创建一个强大的头文件 无需更改即可在 Windows 和 Linux 上进行编译 为此 我的包含内容中有一个 if 语句 如下所示 if defined WINDOWS include
  • 使用 MAX_ORDER / 包含 mmzone.h

    根据https www kernel org doc Documentation networking packet mmap txt https www kernel org doc Documentation networking pa
  • 在 Mono 上运行 .Net MVC5 应用程序

    我正在 Windows 上的 Visual Studio 2013 中开发 Net 4 5 1 MVC5 应用程序 现在我想知道 是否可以在Linux Ubuntu 12 04 上运行这个应用程序 可以使用OWIN吗 Owin 可以自托管运
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • 配置tomat的server.xml文件并自动生成mod_jk.conf

    我在用apache 2 2 15 and tomcat6 6 0 24 on CentOS 6 4并希望使用 tomcat 服务器的功能 通过添加以下内容自动生成 mod jk conf 文件
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 使用 python 脚本更改 shell 中的工作目录

    我想实现一个用户态命令 它将采用其参数之一 路径 并将目录更改为该目录 程序完成后 我希望 shell 位于该目录中 所以我想实施cd命令 但需要外部程序 可以在 python 脚本中完成还是我必须编写 bash 包装器 Example t
  • 错误:“rjags”的包或命名空间加载失败

    在终端的 conda 环境之一中 我能够成功安装包 rjags 但是 当我在该环境中运行 R 并运行库 rjags 时 出现以下错误 加载所需的包 coda 错误 rjags 的包或命名空间加载失败 rjags 的 loadNamespac

随机推荐