概述

   内核的初始化过程过程中,与网络相关的工作如下所示:

    内核引导时执行start_kernel,start_kernel结束之前会调用rest_init,rest_init初始化内核线程init(在Linux3-12中为kernel_init)。
asmlinkage void __init start_kernel(void)
{
...
parse_early_param();//间接调用parse_args
parse_args(...); //处理内核引导程序(boot loader)在引导期间传给内核的参数,
...
init_IRQ(); //初始化硬件中断
tick_init();
init_timers(); //定时器用于支持后续初始化工作的运行。
hrtimers_init();
softirq_init(); //初始化软件中断
...
rest_init(); //这里会通过kernel_thread函数调用内核线程init(kernel_init).
}
static init __ref kernel_init(void *unused)
{
kernel_init_freeable(); //Linux-3.12 在这里调用do_basic_setup
free_initmem(); //用于释放已经不再需要的内存。
.....
run_init_process(execute_command)
...
}
static void __init do_basic_setup(void)
{
cpuset_init_smp();
usermodehelper_init();
shmem_init();
driver_init();
init_irq_proc();
do_ctors();
usermodehelper_enable();
do_initcalls(); //初始化内核子系统和内建的设备驱动
random_int_secret_init();
}


设备的注册和初始化

    一个设备要能够正常工作,他就必须被内核所识别,并且与正确的驱动关联起来。设备驱动程序以私有结构体的形式保存了驱动本设备所需要的所有信息,并且与其他和本设备有交互的组件相互影响。
    设备的注册和初始化一部分由内核完成,一部分由设备驱动程序完成。

      设备的初始化包括了硬件初始化、软件初始化、功能初始化三部分:硬件初始化:由设备驱动程序和通用总线层完成,有时也需要用户提供一些参数。主要任务是将硬件功能配置成IRQ和I/O地址,以便能够跟内核相互作用。软件初始化:设备使用之前必须关注当前配置或启用的网络协议,
一般需要用户提供诸如ip地址之类的参数。功能初始化: Linux内核提供了一系列的网络选项,有些网络选项对每一个设备都需要进行单独配置(如实现Qos的子系统),这些配置决定了数据包进入队列和离开设备的出口队列的方式 。
    NIC初始化目标
    网络设备在Linux中都是以net_device实例进行初始化的,本节先不讨论这个,本节主要介绍设备驱动程序如何分配/建立设备与内核通信所需要的资源。IRQ线:NIC必须分派一个IRQ,用于在必要时唤起内核的注意(虚拟设备不需要分配IRQ,因为它的工作都是在内部实现。)/proc/interrupts文件可用于观察当前中断线分派状态。I/O端口和内存注册: 驱动程序会将设备的一块内存映射到系统内存,使得驱动程序的读写操作能够通过系统内存直接进行。注册和释放操作分别由request_region和
release_region进行。

设备与内核之间的交互

几乎所有设备与内核的交互都是通过以下两种方式:
内核的轮询:
    内核定时检查设备的状态,判断设备是否有什么请求。
设备驱动的中断请求:
    设备驱动发送硬件信号引起内核注意

内核轮询在其他文章会介绍,本文主要介绍硬件中断中与网络有关的概念。
硬件中断
    每一个中断都会运行一个中断处理程序,这些中断响应程序都是设备驱动为设备量身定做的。一般而言,当设备注册一个NIC时,它首先会请求并分配一个IRQ,然后要为IRQ注册(如果设备被卸载了,则需要注销)一个IRQ响应程序。相应的内核代码在kernel/irq/manage.c和arch/XXX/kernel/irq.c。(其中XXX为处理器架构)
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
void free_irq(unsigned int irq, void *dev_id)

    注意:irq的注册和释放函数都带有参数dev_id。因为IRQ是可以共享的,因此需要IRQ number和dev_id共同来唯一表示中断。
    另,在注册IRQ时,必须保证IRQ还未有设备请求,除非所有设备都支持IRQ共享。
    内核接收到一个中断信号时,会通过IRQ number调用关联的中断响应程序。IRQ number与中断响应程序以表的形式保存。由于多个设备可能共享IRQ的关系,IRQ number与中断响应程序的关系可能是一对多的。
    中断类型:接收到数据帧、帧传输失败、DMA传输已成功完成、设备已经有足够内存来创建新的传输会话(可用NIC可用内存达到一定数值<一般为设备MTU>时产生一个中断)
    为了防止内核在设备内存不足时多次提交传输请求,设备驱动可以关闭内核出口队列,待到资源足够是才重启。下面是一个范例:
static netdev_tx_t
el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
……
netif_stop_queue (dev);
……
dev->trans_start = jiffies;
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);
……
}


    IRQ共享:IRQ线是很有限的资源,为了让一个系统能支持更多的设备,只能让多个设备共享IRQ线。IRQ共享的机制是这样的,内核收到中断请求,然后调用所有与该中断相关联的响应例程,然后有各个响应例程自行判断过滤是否对这个中断进行处理。(注意,IRQ与响应程序是一对多的,发生一个IRQ,哪些响应程序要处理,哪些不需要不是有内核去判断,而是各个中断响应程序自己判断,内核则是调用所有的响应程序。)
    IRQ与IRQ响应程序的组织:用全局的vector:irq_desc来组织,irq_desc包含所有IRQ,每个IRQ对应自己的链表,链表中是该IRQ关联的所有响应程序。只有IRQ共享时,IRQ链表的节点才会超过一个。整个组织如下图:



初始化选项

    所有的系统内建组件以及作为模块加载的设备都能通过用户的输入参数调整所实现的功能、重写其默认值,或者在引导前后有不同的值。
模块选项:(module_param系列的宏)
    模块加载时可以定义。如果是内建的组件,由于在引导期间无法配置,可以通过sys/进行运行时配置。
引导期间内核选项:(__setup系列宏)
    引导期间提供。用于可以内建到内核的模块

    

设备处理层的初始化

    网络代码初始化有以下重要的部分:流量控制,每个CPU输入队列初始化。这些初始化工作在引导期间由net_dev_init完成:
static int __init net_dev_init(void)
subsys_initcall(net_dev_init);

用户空间辅助程序

  • /sbin/modprobe 在内核需要加载某个模块时调用,判断内核传递的模块是不是/etc/modprobe.conf文件中定义的别名
  • /sbin/hotplug 在内核检测到一个新设备插入或拔出系统时调用,它的任务是根据设备标识加载正确的驱动

  1. 以模块方式加载

    kmod模块加载器允许内核组件通过调用request_module请求加载某个模块

    举个例子;如果系统管理员使用ifconfig配置某个网卡,但这个网卡驱动还没有加载,如eth0,内核就会给/sbin/modprobe发送一个请求,让它加载名称为

    eth0的模块。如果/etc/modprobe.conf中包含“alias eth0 xxx”的字符,/sbin/modprobe就会尝试加载xxx.ko模块。

    module_param 宏定义在引入sysfs后可以通过文件来访问得到模块参数

    模块选项有三项 , 第一项参数名称,第二项参数类型,第三项表示参数作为文件在sys文件系统中所有的权限。

    每个模块都会在sys/modules下生成对应的目录,通过目录下的文件可以获取模块参数。
  2. pnp热插拔

    hotplug允许内核检测热插拔设备的插入和拔出并通知用户进程(/sbin/hotplug),用户进程根据这些通知来加载相应的驱动

    在编译内核时,会在kernel目录下生成modules.pcimap和modules.usbmap两个文件,这两个文件分别包含了内核所支持设备的pci id和usb id,文件中还包

    含于每个设备的id相对应的内核模块名称,当用户进程收到内核关于pnp的通知后,会使用这个文件来查找正确的设备驱动

虚拟设备

    虚拟设备一般也使用net_device结构体进行实例化(也有一些例外,如别名接口设备)。
    虚拟设备一般会有用户空间配置工具来对其进行配置。尤其是无法使用ifconfig来进行配置的高级字段。
    虚拟设备一般会有一个/proc接口目录,其内容详细程度取决于虚拟设备的设计。
    虚拟设备与这是设备的对应关系不是一一对应的。这就导致了虚拟设备可能需要进行流量控制的配置。
    虚拟设备的流量是简介从真实物理设备获得的,因而不需要分配IRQ、IO端口和IO内存。
    虚拟设备与其他真实物理设备一样,能对特殊的事件通知做出相应的反应。

/proc调整

    













深入理解Linux网络技术内幕——网络设备初始化的更多相关文章

  1. 深入理解linux网络技术内幕读书笔记(三)--用户空间与内核的接口

    Table of Contents 1 概论 1.1 procfs (/proc 文件系统) 1.1.1 编程接口 1.2 sysctl (/proc/sys目录) 1.2.1 编程接口 1.3 sy ...

  2. 深入理解linux网络技术内幕读书笔记(五)--网络设备初始化

    Table of Contents 1 简介 2 系统初始化概论 2.1 引导期间选项 2.2 中断和定时器 2.3 初始化函数 3 设备注册和初始化 3.1 硬件初始化 3.2 软件初始化 3.3 ...

  3. 深入理解linux网络技术内幕读书笔记(八)--设备注册与初始化

    Table of Contents 1 设备注册之时 2 设备除名之时 3 分配net_device结构 4 NIC注册和除名架构 4.1 注册 4.2 除名 5 设备初始化 6 设备类型初始化: x ...

  4. 深入理解linux网络技术内幕读书笔记(七)--组件初始化的内核基础架构

    Table of Contents 1 引导期间的内核选项 2 注册关键字 3 模块初始化代码 引导期间的内核选项 linux运行用户把内核配置选项传给引导记录,然后引导记录再把选项传给内核. 在引导 ...

  5. 深入理解Linux网络技术内幕——设备的注册与初始化(二)

    设备注册于设备除名     设备注册与设备除名一般有 register_netdev和unregister_netdev完成.这两个是包裹函数,负责上锁,真正起作用的是其调用的register_net ...

  6. 深入理解Linux网络技术内幕——内核基础架构和组件初始化

    引导期间的内核选项     Linux允许用户把内核配置选项传给引导记录,再有引导记录传给内核,以便对内核进行调整.     start_kernel中调用两次parse_args,用于引导期间配置用 ...

  7. 深入理解linux网络技术内幕读书笔记(四)--通知链

    Table of Contents 1 概述 2 定义链 3 链注册 4 链上的通知事件 5 网络子系统的通知链 5.1 包裹函数 5.2 范例 6 测试实例 概述 [注意] 通知链只在内核子系统之间 ...

  8. 深入理解Linux网络技术内幕——中断与网络驱动程序

    接收到帧时通知驱动程序     在网络环境中.设备(网卡)接收到一个数据帧时,须要通知驱动程序进行处理. 有一下几种通知机制: 轮询:     内核不断检查设备是否有话要说.(比較耗资源,但在一些情况 ...

  9. 深入理解linux网络技术内幕读书笔记(十)--帧的接收

    Table of Contents 1 概述 1.1 帧接收的中断处理 2 设备的开启与关闭 3 队列 4 通知内核帧已接收:NAPI和netif_rx 4.1 NAPI简介 4.1.1 NAPI优点 ...

随机推荐

  1. iis发布,部署

    1.项目发布:选择iis:文件系统:文件路径:realese 2.iis添加: 3.host文件添加 问题1: 不能在此路径中使用此配置节.如果在父级别上锁定了该节,便会出现这种情况.锁定 在全新安装 ...

  2. QQ名片自动点赞

    2017-01-23 简介:QQ名片自动点赞,1秒左右可点完1屏好友的赞,每个好友10个赞. 尺寸: 720*1280 DPI:320 宿主:3.1.2.10711 系统:Android v5.11 ...

  3. UVa 12108 特别困的学生

    https://vjudge.net/problem/UVA-12108 题意:给出n个学生的“清醒—睡眠”周期和初始时间点,每个学生在睡眠时需要判断全班睡觉人数是否严格大于清醒人数,否则在坚持一个清 ...

  4. dev右下角增加弹框提示信息

    using System; using System.Drawing; using System.IO; using System.Threading; using System.Windows.Fo ...

  5. AttributeError: 'Request' object has no attribute 'json', cherrypy 无法接收到json字符串,解决方法

    @cherrypy.expose @cherrypy.tools.accept(media="application/json")   #加入这个装饰器 @cherrypy.too ...

  6. shell while 语句

    普通循环格式: while condition do command done 例子一 #!/bin/bash )) do echo $int let "int++" done 结 ...

  7. Charles Proxy License 破解

    // Charles Proxy License // 适用于Charles任意版本的注册码,谁还会想要使用破解版呢. // Charles 4.2目前是最新版,可用.   Registered Na ...

  8. stack_01

    A.添加/移除 A.1.void stack::push(elemValue); // 栈头 添加元素 A.2.void stack::pop(); // 栈头 移除第一个元素 B.随机存取 C.数据 ...

  9. [tixml]保存,读取

    保存: //xml的实体 TiXmlElement* rootElement = new TiXmlElement("spark"); rootElement->SetAtt ...

  10. C# WPF 利用NPOI读写Excel文件

    https://blog.csdn.net/a312024054/article/details/70139172 [各种样式] https://www.cnblogs.com/xwgli/archi ...