1 引言 Linux网络设备驱动程序是Linux操作系统网络应用中的一个重要的组成部分,分析其运行机理,对于设计Linux网络应用程序是很有帮助的。我们可以在网络驱动程序这一级做一些与应用相关联的特殊事情,例如,在设计Linux防火墙和网络入侵检测系统时可以在网络驱动程序的基础上拦截网络数据包,继而对其进行分析。由于Linux是开放源代码的,这给我们提供了一个绝好的机会来分析和改造网络驱动程序使其满足自己的特殊应用。本文就Linux内核中的网络驱动程序部分进行了详细的讨论,并给出了实现Linux网络驱动程序的重要过程及一种实现模式和具体实例。 2 运行机理 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)把接收到的数据包传输到网络协议的上层进行处理。 3 实现模式实现Linux网络设备驱动功能主要有两种形式,一是通过内核来进行加载,当内核启动的时候,就开始加载网络设备驱动程序,内核启动完成之后,网络驱动功能也随即实现了,再就是通过模块加载的形式。比较两者,第二种形式更加灵活,在此着重对模块加载形式进行讨论。模块设计是Linux中特有的技术,它使Linux内核功能更容易扩展。采用模块来设计Linux网络设备驱动程序会很轻松,并且能够形成固定的模式,任何人只要依照这个模式去设计,都能设计出优良的网络驱动程序。先简要概述一下基于模块加载的网络驱动程序的设计步骤,后面还结合具体实例来讲解。首先通过模块加载命令insmod来把网络设备驱动程序插入到内核之中。然后insmod将调用init_module()函数首先对网络设备的init函数指针初始化,再通过调用register_netdev()函数在Linux系统中注册该网络设备,如果成功,再调用init函数指针所指的网络设备初始化函数来对设备初始化,将设备的device数据结构插入到dev_base链表的末尾。最后可以通过执行模块卸栽命令rmmod 来调用网络驱动程序中的cleanup_module()函数来对网络驱动程序模块卸载。具体实现过程见图2所示。通过模块初始化网络接口是在编译内核时标记为编译为模块,系统在启动时并不知道该接口的存在,需要用户在/etc/rc.d/目录中定义的初始启动脚本中写入命令或手动将模块插入内核空间来激活网络接口。这也给我们在何时加载网络设备驱动程序提供了灵活性。 4 应用实例我们以ne2000兼容网卡为例,来具体介绍基于模块的网络驱动程序的设计过程。可以参考文件linux/drivers/net/ne.c和linux/drivers/net/8390.c。 1)模块加载和卸载 ne2000网卡的模块加载功能由init_module()函数完成,具体过程及解释如下: int init_module(void) { int this_dev, found = 0; //循环检测ne2000类型的网络设备接口 for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { //获得网络接口对应的net-device结构指针 struct net_device *dev = &dev_ne[this_dev]; dev->irq = irq[this_dev]; //初始化该接口的中断请求号 dev->mem_end = bad[this_dev]; //初始化接收缓冲区的终点位置 dev->base_addr = io[this_dev]; //初始化网络接口的I/O基地址 dev->init = ne_probe; //初始化init为ne_probe,后面介绍此函数 //调用registre_netdevice()向系统登记网络接口,在这个函数中将分配给网络接口在系统中惟一的名称。并且将该网络接口设备添加到系统管理的链表dev-base中进行管理。 if (register_netdev(dev) == 0) { found++; continue; } … //省略 } return 0;} 模块卸载功能由cleanup_module()函数来实现。如下所示: void cleanup_module(void) { int this_dev; //遍历整个dev-ne数组 for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { //获得net-device结构指针 struct net_device *dev = &dev_ne[this_dev]; if (dev->priv != NULL) { void *priv = dev->priv; struct pci_dev *idev = (struct pci_dev *)ei_status.priv; //调用函数指针 idev->deactive将已经激活的网卡关闭使用 if (idev) idev->deactivate(idev); free_irq(dev->irq, dev); //调用函数release_region()释放该网卡占用的I/O地址空间 release_region(dev->base_addr, NE_IO_EXTENT); //调用unregister_netdev()注销 这个net_device()结构 unregister_netdev(dev); kfree(priv); //释放priv空间 } } } 2)网络接口初始化实现此功能是由ne_probe()函数来完成的,前面已经提到过,在init_module()函数中用它来初始化init函数指针。它主要对网卡进行检测,并且初始化系统中网络设备信息用于后面的网络数据的发送和接收。具体过程及解释如下: int __init ne_probe(struct net_device *dev) { unsigned int base_addr = dev->base_addr; //初始化dev-owner成员,因为使用模块类型驱动,会将dev-owner指向对象modules结构指针。 SET_MODULE_OWNER(dev); //检测dev->base_addr是否合法,是则执行ne-probe1()函数检测过程。不是,则需要自动检测。 if (base_addr > 0x1ff) return ne_probe1(dev, base_addr); else if (base_addr != 0) return -ENXIO; //如果有ISAPnP设备,则调用ne_probe_isapnp()检测这种类型的网卡。 if (isapnp_present() && (ne_probe_isapnp(dev) == 0)) return 0; …//省略 return -ENODEV; } 这其中两个函数ne_probe_isapnp()和ne_probe19()的区别在于检测中断号上面。PCI方式只需指定I/O基地址就可以自动获得irq,是由BIOS自动分配的。而ISA方式需要获得空闲的中断资源才能分配。 3)网络接口设备打开和关闭网络接口设备打开就是激活网络接口,使它能接收来自网络的数据并且传递到网络协议栈的上面,也可以将数据发送到网络上。设备关闭就是停止操作。在ne2000网络驱动程序中网络设备打开由dev_open()和ne_open()完成。设备关闭有dev_close()和ne_close()完成。它们相应调用底层函数ei_open()和ei_close()来完成。其实现过程相对简单,不再赘述。 4)数据包接收和发送在驱动程序层次上的发送和接收数据都是通过低层对硬件的读写来完成的。当网络上的数据到来时,将触发硬件中断,根据注册的中断向量表确定处理函数,进入中断向量处理程序,将数据送到上层协议进行处理。对ne网卡的数据接收过程是在ne_probe()函数中的中断处理函数ei_interrupt来完成的,在进入ei_interrupt()之后再通过ei_receive()从8309的接收缓冲区获得数据,并组合成sk_buff结构,再通过netif_rx()函数将接收到的数据存放在系统的饿接收队列之中。 Ei-interrupt()的函数原型如下:void ei_interrupt(int irq,void *dev_id, struct pt_regs *regs) 其中irq为中断号,dev-id是表示产生中断的网络接口设备对应的结构指针,regs表示当前的寄存器内容。数据发送是由dev_dev_start_xmit函数指针对应的函数为ei_start_xmit函数,由它来完成数据包的发送。在函数ethdev_init()把net_device结构的hard_start_xmit指针初始化为ei_start_xmit。 5 小结设计Linux网络设备驱动程序有一定的模式,遵循这个模式,将会大大减轻我们设计程序的工作量。本文分析了网络驱动程序在内核中的工作机理,并提出了设计网络驱动程序的一种模式并给出了实例,这对设计复杂的网络驱动程序是很有帮助的。 参考文献 [1] Alessandro Rubini著. LISOLEG译.Linux设备驱动程序. 北京:中国电力出版社. 2000 [2] 雷澍等编著. Linux的内核与编程. 北京:机械工业出版社,2000 [3] 李善平,刘文蜂等. Linux内核2.4版源代码分析大全. 北京:机械工业出版社,2002

Linux网络设备驱动程序设计----刘文涛相关推荐

  1. Linux USB驱动程序设计

    Linux USB驱动程序设计 1. USB发展史 USB(Universal Serial Bus ),通用串行总线,是一种外部总线标准,用于规范电脑与外部设备的连接和通讯. USB是在1994年底 ...

  2. Linux网络设备驱动结构概述

    2019独角兽企业重金招聘Python工程师标准>>> 网络设备驱动相比字符型设备的驱动要复杂一些,除了总体上驱动的框架有一些相似外,有很多地方都是不同,但网络设备驱动有一个很大的特 ...

  3. STM32MP157驱动开发——Linux 网络设备驱动

    STM32MP157驱动开发--Linux 网络设备驱动 一.简介 STM32MP1 GMAC 接口简介 YT8511C 详解 二.驱动开发 1.网络外设的设备树 2.设备驱动 三.测试 网速测试 参 ...

  4. linux pci 网卡驱动,linux网络设备驱动_pci网卡

    <linux网络设备驱动_pci网卡>由会员分享,可在线阅读,更多相关<linux网络设备驱动_pci网卡(12页珍藏版)>请在技术文库上搜索. 1. LinuxLinux 网 ...

  5. Linux网络设备驱动-以太网驱动分析

    1.概述 网络上数据包的发送和接收由网络设备完成.网络设备与字符设备.块设备不同,其并不对应于/dev目录下的文件,也不能使用常规的操作方法操作网络设备.现在比较通用的做法是通过套接字访问网络设备.网 ...

  6. 编写Linux网络设备驱动(上)

    编写Linux网络设备驱动(上) <编写Linux网络设备驱动(上)> 来自:刘建文 | 学术半·IT歌·文 作者:刘建文 关键字:Linux 驱动程序 永久链接地址:http://art ...

  7. 《Linux设备驱动开发详解》——第16章 Linux网络设备驱动一

    Linux系统对网络设备驱动定义了4各层次:网络协议接口层,网络设备接口层,提供实际功能的设备驱动功能层,网络设备与媒介层 16.1 Linux网络设备驱动的结构 (1)网络协议接口层:提供统一的数据 ...

  8. linux网络设备驱动结构体,Linux网络设备驱动之设备驱动的注册与注销(二)

    网络设备驱动的注册与注销由 register_netdev( ) 和 unregister_netdev( ) 函数完成,这两个函数的原型为: int register_netdev(struct n ...

  9. Linux 网络设备驱动开发(一) —— linux内核网络分层结构

    Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又便于扩展和维护. ...

  10. Linux网络设备驱动概述

    一.概述     Linux网络设备驱动程序遵循通用的接口,设计时采用的是面向对象的方法,一个设备就是一个对象(net_device结构).一个网络设备最基本的方法有初始化.发送和接收等. Linux ...

最新文章

  1. Jsp—01—Java server page 全解
  2. python中BeautifulSoup简单使用
  3. 面向对象回顾(异常(try、catch、throw、throws和finally)、接口和抽象类、面向对象特征、泛型(extends/super))
  4. 【渝粤题库】陕西师范大学200301几何学作业(高起本)
  5. 分布问题(二元,多元变量分布,Beta,Dir)
  6. HTTPSConnectionPool(host=‘api.github.com‘, port=443): Max retries exceeded with url
  7. matlab|已知多点坐标,求两两之间的距离
  8. Volatility3 windows插件详解
  9. 我的Verilog HDL学习历程(二) 组合逻辑电路的一个实例:基于EGO1板子
  10. pyserial库是python语言用于,python的pyserial模块
  11. 【技术类】2019校招技术类岗位面经汇总
  12. 《广西经济社会发展报告(2019)》正式发布 聚焦发展热点
  13. dw cs5 html,dreamweaver cs5网页制作图文教程
  14. Revit二次开发_类别对应的BuiltInCategory枚举
  15. arccatalog点要素显示不完_shp文件转为dwg之后在arcgis下打开属性表有高程信息但在cad里面打开为何没高程 - 地学 - 小木虫 - 学术 科研 互动社区...
  16. logo怎么设计才好看?好看的logo设计方法大公开。
  17. [高项]已知风险VS未知风险
  18. Ubuntu新装系统必装软件
  19. 图像分类模型 GoogLeNet
  20. .aspx简单易懂的介绍

热门文章

  1. free源码分析---1
  2. python生成可执行文强案文件
  3. 你不知道的几种素数判断方法,由浅入深,详解。(附送程序员必读好书)
  4. 你真的理解:“学而不思则罔”? 什么是学习吗?
  5. Python 求解超越方程
  6. SpringBoot实现短信验证码校验
  7. CVPR 2018值得一看的25篇论文,都在这里了 | 源码 解读
  8. ubuntu20.04 虚拟机连不上网
  9. 机器学习_深度学习毕设题目汇总——漫画
  10. 今天不谈技术,分享一个引起业内轩然大波的月饼事件。