Chinaunix首页 | 论坛 | 博客
  • 博客访问: 892240
  • 博文数量: 139
  • 博客积分: 10016
  • 博客等级: 上将
  • 技术积分: 932
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-31 02:15
文章存档

2008年(19)

2007年(73)

2006年(46)

2005年(1)

我的朋友

分类: LINUX

2007-08-30 15:47:54

Linux网络设备驱动程序是Linux操作系统网络应用中的一个重要组成部分。分析其运行机理,对于设计Linux网络应用程序是很有帮助的。我们可以在网络驱动程序这一级做一些与应用相关联的特殊事情,例如在设计Linux防火墙和网络入侵检测系统时,可以在网络驱动程序的基础上拦截网络数据包,继而对其进行分析。由于Linux是开放源代码的,所以给我们提供了一个分析和改造网络驱动程序,并使其满足特殊应用的绝好机会。本文对Linux内核中的网络驱动程序部分进行了详细讨论,并给出了实现Linux网络驱动程序的重要过程、一种实现模式和具体实例。

运行机理

1.体系结构

Linux网络驱动程序的体系结构如图1所示。可以划分为四层,从上到下分别为协议接口层、网络设备接口层、提供实际功能的设备驱动功能层,以及网络设备和网络媒介层。在设计网络驱动程序时,最主要的工作就是完成设备驱动功能层,使其满足我们自己所需的功能。在Linux中,把所有网络设备都抽象为一个接口。这个接口提供了对所有网络设备的操作集合。由数据结构 struct device来表示网络设备在内核中的运行情况,即网络设备接口。它既包括纯软件网络设备接口,如环路(Loopback),也可以包括硬件网络设备接口,如以太网卡。它由以dev_base为头指针的设备链表来集中管理所有网络设备。该设备链表中的每个元素代表一个网络设备接口。数据结构device中有很多供系统访问和协议层调用的设备方法,包括供设备初始化和往系统注册用的init函数、打开和关闭网络设备的open和stop函数、处理数据包发送的函数hard_ start_xmit,以及中断处理函数等。有关device数据结构(在内核中也就是net_device)的详细内容,请参看/linux/include/linux/netdevice.h。

2.初始化

网络设备的初始化主要是由device数据结构中的init函数指针所指的初始化函数来完成的。当内核启动或加载网络驱动模块的时候,就会调用初始化过程。这个过程将首先检测网络物理设备是否存在。它通过检测物理设备的硬件特征来完成,然后再对设备进行资源配置。这些完成之后就要构造设备的device数据结构,用检测到的数值来对device中的变量初始化。这一步很重要。最后向Linux内核注册该设备并申请内存空间。

3. 数据包的发送与接收

数据包的发送和接收是实现Linux网络驱动程序中两个最关键的过程。对这两个过程处理的好坏将直接影响到驱动程序的整体运行质量。图1中也很明确地说明了网络数据包的传输过程。首先在网络设备驱动加载时,通过device域中的init函数指针调用网络设备的初始化函数,对设备进行初始化。如果操作成功就可以通过device域中的open函数指针调用网络设备的打开函数打开设备,再通过device域中的建立硬件包头函数指针hard_header来建立硬件包头信息。最后通过协议接口层函数dev_queue_xmit(详见/linux/net/core/dev.c)来调用device域中的hard_start_xmit函数指针,完成数据包的发送。该函数将把存放在套接字缓冲区中的数据发送到物理设备。该缓冲区是由数据结构sk_buff (详见/linux/include/linux/sk_buff.h)来表示的。

数据包的接收是通过中断机制来完成的。当有数据到达时,就产生中断信号,网络设备驱动功能层就调用中断处理程序,即数据包接收程序来处理数据包的接收。然后,网络协议接口层调用netif_rx函数(详见/linux/net/core/dev.c),把接收到的数据包传输到网络协议的上层进行处理。

实现模式

实现Linux网络设备驱动功能主要有两种形式:一是通过内核来进行加载,当内核启动的时候,就开始加载网络设备驱动程序,内核启动完成之后,网络驱动功能也随即实现了;再就是通过模块加载的形式。比较两者,第二种形式更加灵活。在此着重对模块加载形式进行讨论。

模块设计是Linux中特有的技术,它使Linux内核功能更容易扩展。采用模块来设计Linux网络设备驱动程序会很轻松,并且能够形成固定的模式。任何人只要依照这个模式去设计,都能设计出优良的网络驱动程序。先简要介绍一下基于模块加载网络驱动程序的设计步骤,后面还结合具体实例来讲解。首先通过模块加载命令insmod来把网络设备驱动程序插入到内核之中。然后,insmod将调用init_module()函数首先对网络设备的init函数指针初始化,再通过调用register_netdev()函数在Linux系统中注册该网络设备。如果成功,再调用init函数指针所指的网络设备初始化函数来对设备初始化,将设备的device数据结构插入到dev_base链表的末尾。最后可以通过执行模块卸载命令rmmod,来调用网络驱动程序中的cleanup_module()函数,对网络驱动程序模块进行卸载。具体实现过程见图2所示。

通过模块初始化网络接口是在编译内核时标记为编译为模块。系统在启动时并不知道该接口的存在,需要用户在/etc/rc.d/目录中定义的初始启动脚本中写入命令或手动将模块插入内核空间来激活网络接口。这也给我们在何时加载网络设备驱动程序提供了灵活性。

阅读(2987) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~