linux网络设备初始化

NIC注册和注销的通用架构

     Linux系统中NIC网络设备驱动程序利用网络代码进行注册和注销有其通用的架构,这里以PCI Ethernet NIC为例,其他设备类型只是所以函数名称和调用方式不同,主要依据于设备总线提供的接口。

image

其中(a)为设备注册的大致流程图,而(b)为设备注销的流程图。

       在PCI Ethernet NIC设备驱动程序的探测函数(热插拔设备)或模块初始化函数中,首先要为设备分配一个net_device数据结构,并对其中的成员进行必要的初始化,对其中与设备类型密切相关的特殊成员利用驱动程序自己实现的setup函数进行初始化;Ethernet NIC设备驱动程序还需要调用netdev_boot_setup_check检查是否在系统启动参数中对网络设备进行了设置;然后调用register_netdev完成设备的注册。

      在分配net_device数据结构时,驱动程序一般不直接调用alloc_netdev函数,而是调用为其类型封装后的函数,如Ethernet NIC设备直接调用alloc_etherdev函数,使用更加方便简单。

       而Ethernet NIC设备的注销则是相反的过程,首先调用unregister_netdev在系统中注销设备,然后将分配的net_device数据结构释放。

       在释放net_device数据结构时,设备也可能不直接调用free_netdev函数中,而是调用net_device数据结构中的成员函数:

      /* Called from unregister, can be used to call free_netdev */ 
       void (*destructor)(struct net_device *dev);

       虚拟设备驱动程序一般采用这种方式,实现自己的destructor函数来释放net_device数据结构。

系统初始化概括:

   

When the kernel boots up, it executesstart_kernel;which initializes a bunch of subsystems, as partiallyshown in Figure 5-1. Before start_kernel terminates, it invokes theinit kernel thread, which takes
care of the rest of the initializations. Most of the initialization activities related to this chapter happen to beinside
do_basic_setup.

run_init_process determines the first process run on the system, the parent of all other processes; it has
a PID of 1 and never halts until the system is done;

在网络设备可以使用之前,一定要能够被内核所识别,而且要连接上正确的驱动程序;网络设备的注册,一部分是由内核来完成,一部分是由设备驱动程序来完成。

Hardware initialization
This is done by the device driver in cooperation with the generic bus layer (e.g., PCI or USB).configures such features of each device as the IRQ and I/O address so that they can interact with the kernel。

Software initialization
Before the device can be used, depending on what network protocols are enabled and configured, the
user may need to provide some other configuration parameters, such as IP addresses。


Each network device is represented in the Linux kernel by an instance of the net_device data structure在内核中,我们为每一个网络设备分配一个net_device的数据结构,来描述网络设备,这个数据结构的初始化由两个对象来完成,一个是相应的驱动设备,一个是内核。


设备和内核的交互通过两种方式:

Polling
Driven on the kernel side. The kernel checks the device status at regular intervals to see if it has
anything to say.
Interrupt
Driven on the device side. The device sends a hardware signal (by generating an interrupt) to the
kernel when it needs the kernel's attention.


一种是轮询,还有一种是中断。

Linux网络协议栈的内核初始化工作大致分为设备链路层(e100_module_init、net_dev_init)、网络层(inet_init)、传输层(proto_init)、应用层(sock_init)初始化。由于物理层为具体的网络设备,所以内核对网络协议栈的实现以及网卡驱动实现等没有物理层(L1),驱动以及设备的初始化函数都应该化为L2层。前面介绍了e100系列网卡驱动的实现,下面我们看看内核初始化的时候对设备初始化的另一个函数net_dev_init。
/*设备处理层的初始化函数*/
static int __init net_dev_init(void)
{  www.2cto.com  
         int i, rc = -ENOMEM;
         /*没有被初始化*/
         BUG_ON(!dev_boot_phase);
 
         /*该函数在/proc目录下创建三个文件,主要用于读取网络相关统计数据
         正如我们看到的,/proc下的文件基本都为只读的,这里提供的三个文件
         都没有写操作*/
         if (dev_proc_init())
                   goto out;
 
         /*在/sysfs设备文件 系统的class中注册net节点*/
         if (netdev_sysfs_init())
                   goto out;
        
         /*初始化网络处理函数链表和散列表,这些函数是用来处理接收到的不同
         协议族报文*/
         INIT_LIST_HEAD(&ptype_all);
         for (i = 0; i < 16; i++)
                   INIT_LIST_HEAD(&ptype_base[i]);
         www.2cto.com  
         /*下面为初始化存放网络设备的散列表*/
         /*散列表关键字由设备名称计算获得*/
         for (i = 0; i < ARRAY_SIZE(dev_name_head); i++)
                   INIT_HLIST_HEAD(&dev_name_head[i]);
 
         /*散列表关键字由设备接口索引计算获得*/
         for (i = 0; i < ARRAY_SIZE(dev_index_head); i++)
                   INIT_HLIST_HEAD(&dev_index_head[i]);
 
         /*
          *     Initialise the packet receive queues.
          */
         /*初始化与CPU相关的数据接收队列*/
         for_each_possible_cpu(i) {
                   struct softnet_data *queue;
 
                   queue = &per_cpu(softnet_data, i);
                   skb_queue_head_init(&queue->input_pkt_queue);
                   queue->completion_queue = NULL;
                   INIT_LIST_HEAD(&queue->poll_list);
                   set_bit(__LINK_STATE_START, &queue->backlog_dev.state);
                   queue->backlog_dev.weight = weight_p;
                   queue->backlog_dev.poll = process_backlog;
                   atomic_set(&queue->backlog_dev.refcnt, 1);
         }
         /*注册网络DMA客户端*/
         netdev_dma_register();
 
         /*标志已经初始化*/
         dev_boot_phase = 0;
 
         /*注册两个软件中断用于数据接收和发送*/
         open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
         open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
         /*在通知链表中注册一个回调函数,用于相应CPU热插拔事件
         由回调函数可以看出,一旦接到通知,CPU输入队列中的包逐一
         交由netif_rx()处理*/  www.2cto.com  
         hotcpu_notifier(dev_cpu_callback, 0);
         /*初始化目的路由缓存,通知链的方式*/
         dst_init();
         /*初始化网络链路层的组播模块,在/proc/net下创建文件dev_mcast
         用来存放内核中网络设备与IP组播相关的参数*/
         dev_mcast_init();
         rc = 0;
out:
         return rc;
}
Net_dev_init函数在驱动程序之前调用,用于初始化一些必要的信息,包括两种设备队列、数据输入输出队列以及注册两个用于接收和发送数据的软中断等。另外,上面各种初始化函数的层次划分可能不正确,只是便于理解。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值