课程设计论文Linux网络设备驱动程序设计.doc
《课程设计论文Linux网络设备驱动程序设计.doc》由会员分享,可在线阅读,更多相关《课程设计论文Linux网络设备驱动程序设计.doc(24页珍藏版)》请在三一办公上搜索。
1、彭胜 Linux网络驱动程序设计 第24页 共24页1 引 言Linux网络设备驱动程序是Linux操作系统网络应用中的一个重要的组成部分,分析其运行机理,对于设计Linux网络应用程序是很有帮助的。我们可以在网络驱动程序这一级做一些与应用相关联的特殊事情,例如,在设计Linux防火墙和网络入侵检测系统时可以在网络驱动程序的基础上拦截网络数据包,继而对其进行分析。由于Linux是开放源代码的,这给我们提供了一个绝好的机会来分析和改造网络驱动程序使其满足自己的特殊应用。本文就Linux内核中的网络驱动程序部分进行了详细的讨论,并给出了实现Linux网络驱动程序的重要过程及一种实现模式和具体实例。
2、随着当今世界经济的发展,计算机网络被广泛应用到各个领域,而网络设备的应用都必须要驱动程序来驱动,因而设计功能强大的网络设备驱动程序设计成为开发和设计的关键,而Linux 操作系统源代码的开放性和较强的可移植性味操作人员提供了一个分析和编写网络驱动程序的优越环境,可以达到特殊应用的要求。1.1 Linux设备驱动程序分类 Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。在2.0.xx到2.2.xx的变动里,驱动程序的编写做了一些改变,但是从2.0.xx的驱动到2.2.xx的移
3、植只需做少量的工作。Linux系统的设备分为字符设备(char device),块设备(block device)和网络设备(net work device)三种。字符设备是指存取时没有缓存的设备。块设备的读写都有缓存来支持,并且块设备必须能够随机存取(random access),字符设备则没有这个要求。典型的字符设备包括鼠标,键盘,串行口等。块设备主要包括硬盘软盘设备,CD-ROM等。一个文件系统要安装进入操作系统必须在块设备上。网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进
4、行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持【1】。 1.2 驱动程序的一些基本概念无论是什么操作系统的驱动程序,都有一些通用的概念。操作系统提供给驱动程序的支持也大致相同。下面简单介绍一下网络设备驱动程序的一些基本要求。(1)发送和接收这是一个网络设备最基本的功能。一块网卡所做的无非就是收发工作。所以驱动程序里要告诉系统你的发送函数在哪里,系统在有数据要发送时就会调用你的发 送程序。还有驱动程序由于是直接操纵硬件的,所以网络硬件有数据收到最先能得到这个数据的也就是驱动程序,它负责把这些原始数据进行必要的处理然后送给系统。这里,操作系统必须要提供两个
5、机制,一个是找到驱动程序的发送函数,一个是驱动程序把收到的数据送给系统【2】。(2)中断中断在现代计算机结构中有重要的地位。操作系统必须提供驱动程序响应中断的能力。一般是把一个中断处理程序注册到系统中去。操作系统在硬件中断发生后 调用驱动程序的处理程序。Linux支持中断的共享,即多个设备共享一个中断。(3)时钟在实现驱动程序时,很多地方会用到时钟。如某些协议里的超时处理,没有中断机制的硬件的轮询等。操作系统应为驱动程序提供定时机制。一般是在预定的时 间过了以后回调注册的时钟函数。在网络驱动程序中,如果硬件没有中断功能,定时器可以提供轮询(poll)方式对硬件进行存取。或者是实现某些协议时需要
6、的超时重传等【3】。2 驱动程序设计原理2.1 体系结构dev_queue_xmit()netif_rx() struct device数据包发送hard_start_xmit()网络物理设备媒介中断处理(数据包接收)网络协议接口层网络设备接口层设备驱动功能层设备媒介层图1 Linux网络驱动程序体系结构Linux网络驱动程序的体系结构如图1所示。可以划分为四层,从上到下分别为协议接口层,网络设备接口层,再就是提供实际功能的设备驱动功能层,以及网络设备和网络媒介层。我们在设计网络驱动程序时,最主要的工作就是完成设备驱动功能层,使其满足我们自己所需的功能。在Linux中对所有网络设备都抽象为一个
7、接口,这个接口提供了对所有网络设备的操作集合。由数据结构struct device来表示网络设备在内核中的运行情况,即网络设备接口,它既包括纯软件网络设备接口,如环路(Loopback),也可以包括硬件网络设备接口,如以太网卡。而由以dev_base为头指针的设备链表来集体管理所有网络设备,该设备链表中的每个元素代表一个网络设备接口。数据结构device中有很多供系访问和协议层调用的设备方法,包括供设备初始化和往系统注册用的init函数,打开和关闭网络设备的open和stop函数,处理数据包发送的函数hard_start_xmit,以及中断处理函数等。有关device数据结构(在内核中也就是n
8、et_device)的详细内容,请参看/linux/include/linux/netdevice.h。2.2 初始化网络设备的初始化主要是由device数据结构中的init函数指针所指的初始化函数来完成的,当内核启动或加载网络驱动模块的时候,就会调用初始化过程。在这其中将首先检测网络物理设备是否存在,这是通过检测物理设备的硬件特征来完成,然后再对设备进行资源配置,这些完成之后就要构造设备的device数据结构,把检测到的数值来对device中的变量初始化,这一步很重要。最后向Linux内核中注册该设备并申请内存空间。2.3 数据包的发送与接收数据包的发送和接收是实现Linux网络驱动程序中两
9、个最关键的过程,对这两个过程处理的好坏将直接影响到驱动程序的整体运行质量。图1中也很明确地说明了网络数据包的传输过程。首先在网络设备驱动加载时,通过device域中的init函数指针调用网络设备的初始化函数对设备进行初始化,如果操作成功就可以通过device域中的open函数指针调用网络设备的打开函数打开设备,再通过device域中的建立硬件包头函数指针hard_header来建立硬件包头信息。最后通过协议接口层函数dev_queue_xmit(详见/linux/net/core/dev.c)来调用device域中的hard_start_xmit函数指针来完成数据包的发送。该函数将把存放在套接
10、字缓冲区中的数据发送到物理设备,该缓冲区是由数据结构sk_buff (详见/linux/include/linux/sk_buff.h)来表示的。数据包的接收是通过中断机制来完成的,当有数据到达时,就产生中断信号,网络设备驱动功能层就调用中断处理程序,即数据包接收程序来处理数据包的接收,然后网络协议接口层调用netif_rx函数(详见/linux/net/core/dev.c)把接收到的数据包传输到网络协议的上层进行处理。3 实现模式实现Linux网络设备驱动功能主要有两种形式,一是通过内核来进行加载,当内核启动的时候,就开始加载网络设备驱动程序,内核启动完成之后,网络驱动功能也随即实现了,再
11、就是通过模块加载的形式。比较两者,第二种形式更加灵活,在此着重对模块加载形式进行讨论。模块设计是Linux中特有的技术,它使Linux内核功能更容易扩展。采用模块来设计Linux网络设备驱动程序会很轻松,并且能够形成固定的模式,任何人只要依照这个模式去设计,都能设计出优良的网络驱动程序。先简要概述一下基于模块加载的网络驱动程序的设计步骤,后面还结合具体实例来讲解。首先通过模块加载命令insmod来把网络设备驱动程序插入到内核之中。然后insmod将调用init_module()函数首先对网络设备的init函数指针初始化,再通过调用register_netdev()函数在Linux系统中注册该网
12、络设备,如果成功,再调用init函数指针所指的网络设备初始化函数来对设备初始化,将设备的device数据结构插入到dev_base链表的末尾。最后可以通过执行模块卸栽命令rmmod 来调用网络驱动程序中的cleanup_module()函数来对网络驱动程序模块卸载。具体实现过程见图2所示。insmod命令init_module()打开网络接口设备init指针调用初始化函数register_netdev()关闭网络接口设备模块卸载数据包发送与接收操作图2 Linux网络设备驱动程序实现模式通过模块初始化网络接口是在编译内核时标记为编译为模块,系统在启动时并不知道该接口的存在,需要用户在/etc/
13、rc.d/目录中定义的初始启动脚本中写入命令或手动将模块插入内核空间来激活网络接口。这也给我们在何时加载网络设备驱动程序提供了灵活性。4 详细设计4.1 网络驱动程序的结构所有的Linux网络驱动程序遵循通用的接口。设计时采用的是面向对象的方法。一个设备就是一个对象(device 结构),它内部有自己的数据和方法。每一个设备的方法被调用时的第一个参数都是这个设备对象本身。这样这个方法就可以存取自身的数据(类似面向对象程序设计时的this引用)。一个网络设备最基本的方法有初始化、发送和接收。- - |deliver packets | |receive packets queue| |(dev_
14、queue_xmit() | |them(netif_rx() | - - | | / / | | -| methods and variables(initialize,open,close,hard_xmit,| | interrupt handler,config,resources,status.) | - | | / / | | - - |send to hardware | |receivce from hardware| - - | | / / | | - | hardware media | - 初始化程序完成硬件的初始化、device中变量的初始化和系统资源的申请。发送程序是
15、在驱动程序的上层协议层有数据要发送时自动调用的。一般驱动程序中不对发送数据进行缓存,而是直接使用硬件的发送功能把数据发送出去。接收数据一般是通过硬件中断来通知的。在中断处理程序里,把硬件帧信息填入一skbuff结构中,然后调用netif_rx()传递给上层处理。4.2 网络驱动程序的基本方法网络设备做为一个对象,提供一些方法供系统访问。正是这些有统一接口的方法,掩蔽了硬件的具体细节,让系统对各种网络设备的访问都采用统一的形式,做到硬件无关性。下面解释最基本的方法。(1)初始化(initialize)驱动动程序必须有一个初始化方法。在把驱动程序载入系统的时候会调用这个初始化程序。它做以下几方面的
16、工作。检测设备。在初始化程序里你可以根据硬件的特征检查硬件是否存在,然后决定是否启动这个驱动程序。配置和初始化硬件。在初始化程序里你可以完成对硬件资源的配置,比如即插即用的硬件就可以在这个时候进行配置(Linux内核对PnP功能没有很好的支持,可以在驱动程序里完成这个功能)。配置或协商好硬件占用的资源以后,就可以向系统申请这些资源。有些资源是可以和别的设备共享的,如中断。有些是不能共享的,如IO、DMA。接下来你要初始化device结构中的变量。最后,你可以让硬件正式开始工作。(2)打开(open)open这个方法在网络设备驱动程序里是网络设备被激活的时候被调用(即设备状态由dow n-up)
17、。所以实际上很多在initialize中的工作可以放到这里来做。比如资源的申请,硬件的激活。如果dev-open返回非0(error),则硬件的状态还是down。open方法另一个作用是如果驱动程序做为一个模块被装入,则要防止模块卸载时设备处于打开状态。在open方法里要调用MOD_INC_USE_COUNT宏。(3)关闭(stop)close方法做和open相反的工作。可以释放某些资源以减少系统负担。close是在设备状态由up转为down时被调用的。另外如果是做为模块装入的驱动程序,close里应该调用M OD_DEC_USE_COUNT,减少设备被引用的次数,以使驱动程序可以被卸载。另外
18、close方法必须返回成功(0=success)。(4)发送(hard_start_xmit)所有的网络设备驱动程序都必须有这个发送方法。在系统调用驱动程序的xmit时,发送的数据放在一个sk_buff结构中。一般的驱动程序把数据传给硬件发出去。也有一些特殊的设备比如loopback把数据组成一个接收数据再回送给系统,或者dummy设备直接丢弃数据。如果发送成功,hard_start_xmit方法里释放sk_buff,返回0(发送成功)。如果设备暂时无法处理,比如硬件忙,则返回1。这时如果dev-tbusy置为非0,则系统认为硬件忙,要等到dev-tbusy置0以后才会再次发送。tbusy的置
19、0任务一般由中断完成。硬件在发送结束后产生中断,这时可以把tbusy置0,然后用mark_bh()调用通知系统可以再次发送。 在发送不成功的情况下,也可以不置dev-tbusy为非0,这样系统会不断尝试重发。如果hard_start_xmit发送不成功,则不要释放sk_buff。传送下来的sk_buff中的数据已经包含硬件需要的帧头。所以在发送方法里不需要再填充硬件帧头,数据可以直接提交给硬件发送。sk_buff是被锁住的(locked),确保其他程序不会存取它。(5)接收(reception)驱动程序并不存在一个接收方法。有数据收到应该是驱动程序来通知系统的。一般设备收到数据后都会产生一个中
20、断,在中断处理程序中驱动程序申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。接下来填充sk_buff中 的一些信息。skb-dev= dev,判断收到帧的协议类型,填入skb-protocol(多协 议的支持)。把指针skb-m ac.raw指向硬件数据然后丢弃硬件帧头(skb_pull)。还要设置skb-pkt_type,标明第二层(链路层)数据类型。可以是以下类型: PACKET_BROADCAST : 链路层广播PACKET_MULTICAST : 链路层组播PACKET_SELF : 发给自己的帧PACKET_OTHERHOST : 发给别人的帧(监听模式时会有
21、这种帧)最后调用netif_rx()把数据传送给协议层。netif_rx()里数据放入处理队列然后返回,真正的处理是在中断返回以后,这样可以减少中断时间。调用netif_rx()以后,驱动程序就不能再存取数据缓冲区skb。(6)硬件帧头(hard_header) 硬件一般都会在上层数据发送之前加上自己的硬件帧头,比如以太网(Ethernet)就有14字节的帧头。这个帧头是加在上层ip、ipx等数据包的前面的。驱动程序提供一个hard_ header方法,协议层(ip、ipx、arp等)在发送数据之前会调用这段程序。硬件帧头的长度必须填在dev-hard_header_len,这样协议层回在数据
22、之前保留好硬件帧头的空间。这样hard_header程序只要调用skb_push然后正确填入硬件帧头就可以了。在协议层调用hard_header时,传送的参数包括(2.0.xx):数据的sk_buff,device指针,protocol,目的地址(daddr),源地址(saddr),数据长度(len)。数据长度不要使用s k_buff中的参数,因为调用hard_header时数据可能还没完全组织好。saddr是NULL的话是使用缺省地址(default)。daddr是NULL表明协议层不知道硬件目的地址。如果hard_header完全填好了硬件帧头,则返回添加的字节数。如果硬件帧头中的信息还不
23、完全(比如daddr为NULL,但是帧头中需要目的硬件地址。典型的情况是以太网需要地址解析(arp),则返回负字节数。hard_header返回负数的情况下,协议层会做进一步的build header的工作。目前Linux系统里就是做arp (如果hard_header返回正,dev-arp=1,表明不需要做arp,返回负,dev-arp=0,做arp)。对hard_header的调用在每个协议层的处理程序里。如ip_output。(7)地址解析(xarp)有些网络有硬件地址(比如Ethernet),并且在发送硬件帧时需要知道目的硬件地址。这样就需要上层协议地址(ip、ipx)和硬件地址的对应
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 课程设计 论文 Linux 网络设备 驱动程序 设计
链接地址:https://www.31ppt.com/p-4867426.html