网络驱动开发样例snull详解(基于3.10.0)

  本章素材为ldd3书中的网络驱动snull部分。由于现在内核的更新,导致其在最新的内核中无法编译该网络驱动,需要针对修改,顾为此文(内核3.10.0)。另外,网络驱动的开发对理解linux内核网络协议栈有较大帮助,文中涉及设备虽为虚拟设备,但提供了网络驱动必备的知识。

1.1.1 snull编译问题

1、CFLAGS变为EXTRA_CFLAGS

2、config.h头文件替换注释掉

3、struct net_device 的结构在新版本内核中发生了变化,删去了quota成员;将(open,stop,set_config,hard_start_xmit等)封装进了 struct net_device_ops;将(poll,weight等)封装进了 struct napi_struct;将(hard_header,hard_header_cache,rebuild等)封装进了struct header_ops。并对部分函数名和参数进行了变动。

4、netif_rx_complete和netif_rx_schedule属于poll机制中的函数,接口名称已经变为napi_complete和napi_schedule。

具体修改过的文件可以从:

https://download.csdn.net/download/notbaron/10285251中下载

1.1.2 snull使用

  回环地址的实现在drivers/net/loopback.c文件中。

在/etc/network文件中增加如下内容:

snullnet0 172.168.0.0

snullnet1 172.168.1.0

在/etc/hosts中:

172.168.0.1 local0

172.168.0.2 remote0

172.168.1.2 local1

172.168.1.1 remote1

配置虚拟网络设备:sn0和sn1.

#ifconfig sn0 local0

#ifconfig sn1 local1

测试虚拟设备:

#ping local0

#ping local1

1.1.3 驱动链接到内核

网络驱动通过模块初始化函数注册的方式与字符和块驱动是不同的,驱动为接口在一个全局的网络设备列表里插入一个数据结构。

每个接口由一个结构net_device项描述,定义在

include/linux/netdevice.h文件中。

通过内核函数alloc_netdev来进行分配,定义如下:

在3.10.0-514中为如下:

struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,

void (*setup)(struct net_device *),

unsigned int txqs, unsigned int rxqs);

  在4.14.14的内核中如下:

struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,

unsigned char name_assign_type,

void (*setup)(struct net_device *),

unsigned int txqs, unsigned int rxqs);

其中sizeof_priv表示私有数据区的大小。name是接口的名字。setup是设备初始化后的回调函数指针。如果是在该版本内核下那么snull.c中源码就存在问题了。

也可以直接调用alloc_etherdev(定义在文件net/ethernet/eth.c)函数,该函数也是调用alloc_netdev,只是把相关参数给你设置好了,例如回调函数为ether_setup,名字为eth开头。初始化函数主要是对net_device结构的例行初始化,大部分是存储我们的各种驱动函数指针。主要特点是禁用的ARP,因为是模拟的远程系统并不存在,所以去掉arp.

dev->flags  |= IFF_NOARP; (定义在文件include/uapi/linux/if.h  中,在4.14.14代码中可以看到该变量可以被sysfs进行设置,但是不是持久的,重启后失效)

完成net_device结构初始化后,将该结构传递给register_netdev,来注册设备。

for (i = 0; i < 2;  i++)

if ((result = register_netdev(snull_devs[i])))

printk("snull: error %i registering device \"%s\"\n",

result, snull_devs[i]->name);

else

ret = 0;

分配net_device之后,需要进行初始化。因为net_device 结构庞大,内核负责以太网范围中的缺省值,通过ether_setup函数(由alloc_etherdev调用)。

当初始化完设备后,调用register_netdev时候,驱动可能马上被调用来操作设备。

其中函数snull_rx_ints用于使能接收中断(设置snull_priv中的使能变量)。

snull_setup_pool设置包(snull_packet)缓冲池,是一个单向链表结构。

netif_napi_add函数依赖于模块参数use_napi,主要是实现实现napi结构的初始化以及与设备的关联。定义如下net/core/dev.c

void netif_napi_add(struct net_device *dev, struct napi_struct *napi,

int (*poll)(struct napi_struct *, int), int weight)

1.1.4 卸载驱动

调用unregister_netdev调用,从系统中去除接口,free_netdev归还net_device结构给内核。释放初始化时候申请的缓冲区。

调用函数:snull_cleanup

释放缓冲区的函数为:snull_teardown_pool,该函数在注销了设备后才能进行.而且需要在net_device结构返回给系统之间进行,因为一旦调用free_netdev就不能再使用设备或者自己的私有数据做任何引用了。

1.1.5 设备方法

除了设备的初始化,注册和卸载方法之外,还需要定义设备相关的方法。网络设备能声明操作它的函数。

基本方法包括那些必须能够使用接口的,例如。

int dev_open(struct net_device *dev);

struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,

struct netdev_queue *txq, int *ret);

1.1.5.1  基本方法

打开函数请求任何它需要的系统资源并且告知接口启动;关闭接口并释放系统资源。

int dev_open(struct net_device *dev);

  硬件地址在open是复制到设备.同时启动接口的发送队列。内核提供函数netif_start_queue来启动队列,定义在include/linux/netdevice.h文件中

int snull_open(struct net_device *dev)

{

memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN);

if (dev == snull_devs[1])

dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */

netif_start_queue(dev);

return 0;

}

netif_start_queue函数来告诉内核网络子系统,现在可以开始数据包的发送。

在虚拟设备中open操作动作很有限,就是初始化设备的MAC地址,调用netif_start_queue启动队列。

关闭函数就是逆操作:

int snull_release(struct net_device *dev)

{

netif_stop_queue(dev); /* can't transmit any more */

return 0;

}

调用netif_stop_queue后,就不能再继续发送了。在接口关闭时调用。

1.1.5.2  可选方法

 例如:NAPI驱动提供的方法,在中断关闭下,用来在查询模式下操作接口。

change_mtu方法支持改变硬件地址的能力。

1.1.5.3  公用方法

例如:由接口使用来持有有用的状态信息。有些是ifconfig和netstat用来提供给用户关于当前配置的信息。

watchdog_timeo是设置的一个超时值,超时后调用驱动tx_timeout函数。

1.1.6 报文传送

驱动可以在模块加载时或者内核启动时探测接口。但是在接口能承载报文前,内核必须打开并分配一个地址给接口。使用的命令为ifconfig命令。需要做两个步骤,一个通过ioctl(SIOSCIFADDR)是安排地址,另一个通过iocatl(SIOCSIFFLAGS)设置IFF_UP来打开接口。

  关闭时候调用iocatl(SIOCSIFFLAGS)来清除IFF_UP。

其发送函数为snull_tx,函数中保存发送时间,同时保存该skb结构体用于在中断时候释放。snull_tx最后调用snull_hw_tx函数,该函数本与硬件相关,而本snull是在虚拟设备上的,所以未有硬件相关细节,只是将包转发送其他的虚拟接口上,该函数的功能就是snull驱动的主要功能。

监视检测包的合法性,然后修改包中的源和目的ip地址,然后重新计算ip检验值。

其中函数snull_get_tx_buffer从缓冲池中获取包,过程中会进制软中断。如果缓冲池为空,则调用netif_stop_queue函数。

获取后,往获取的结构体中复制数据包内容,然后调用snull_enqueue_buf。

snull_enqueue_buf函数,将包放入到发送队列中(定义在私有数据结构snull_priv中的*rx_queue),此过程中会禁止软中断。

如果中断使能,则调用:snull_interrupt(0, dest, NULL);在未定义use_napi时使用snull_regular_interrupt函数,否则使用snull_napi_interrupt函数。定义分别如下:

static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs)

static void snull_napi_interrupt(int irq, void *dev_id, struct pt_regs *regs)

中断中会去检查是接收中断还是发送中断。终端中会更新统计数值,并释放skb,最后调用snull_release_buffer函数。snull_release_buffer函数释放pkt缓存。

snull_napi_interrupt函数,在接收触发中断时,禁用中断(在snull_regular_interrupt中并未禁用中断),然后调用napi_schedule函数使用轮询函数snull_poll。在发送时,统计包的发送个情况,调用dev_kfree_skb函数来释放skb.

snull_poll循环从发送队列中获取需要发送的包。最后使用napi_complete函数来结束查询,并使能中断。

超时时候调用驱动的tx_timeout方法。超时设置在watchdog_timeo即可,驱动不用去检测,由网络层来最终调用这个tx_timeout函数。

1.1.7 接收发送

网络的接收报文比发送要难一点。中断处理会调用函数snull_rx函数。

先分配一个缓存区来保存报文,就是skb,调用的函数是dev_alloc_skb,该函数的返回值必须检查,不然可能会遭殃。然后复制包内容到分配的skb内存结构中。

驱动更新统计计数来记录收到的一个报文。统计结构主要由以下几个成员组成:rx_packet,rx_bytes和tx_bytes,表示收到的报文数目,发送的数目和发送的字节总数。

最后有netif_rx函数将skb给上层。

1.1.8 链接状态改变

  大部分涉及实际的物理连接的网络技术提供有一个载波状态。当驱动检测到一个设备载波丢失,它应当调用netif_carrier_off来通知内核这个改变。当载波回来时,应当调用netif_carrier_on。

1.1.9 统计信息

调用net_device_stats函数可以返回统计信息。

1.1.10          查看当前网卡驱动

查看网卡生产厂商和信号

# lspci | grep -i ethernet

使用lspci -vvv可以查看到驱动信息。当然pci设备也是可以查看到的。

1.1.11          查看驱动实例

appletalk驱动如下:

drivers/net/appletalk/cops.c

e1000驱动如下:

drivers/net/ethernet/intel/e1000/

1.1.12          加载驱动参数

module_param(name, type, perm);

其中,name:表示参数的名字;

type:表示参数的类型;

perm:表示参数的访问权限;

网络驱动开发样例snull详解(基于3.10.0)相关推荐

  1. 【Linux驱动开发】设备树详解(二)设备树语法详解

    ​ 活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...

  2. 《验收测试驱动开发:ATDD实例详解》—第1章1.2节临时停车

    本节书摘来自异步社区<验收测试驱动开发:ATDD实例详解>一书中的第1章1.2节临时停车,作者[德]Markus Gärtner,更多章节内容可以访问云栖社区"异步社区" ...

  3. Linux驱动开发_设备文件系统详解

    目录 何为设备管理器? Linux下dev的作用 Devfs sysfs kobject udev proc 何为设备管理器? 设备管理器就是负责管理这台电脑上的外设,当我们通过电脑提供的USB口插入 ...

  4. TensorFlow中cnn-cifar10样例代码详解

    TensorFlow是一个支持分布式的深度学习框架,在Google的推动下,它正在变得越来越普及.我最近学了TensorFlow教程上的一个例子,即采用CNN对cifar10数据集进行分类.在看源代码 ...

  5. C语言必知-指针数组(附程序样例和详解)

    指针数组 除了类型之外,指针变量和其他的变量很相似,只不过加上指针标识就行 例如int *api[10],由于下标引用的优先级高于间接访问(就是解引用),因此 api是一个数组,数组中的元素的类型就是 ...

  6. DOS子程序汇编样例及详解

    [目的] 理解汇编语言中的ASSUME 伪指令和标准的汇编程序 掌握Debug-P/G/T 的关系和区别 掌握将十六进制数转换为十进制数的方法和程序 学习和改进两位数加法的程序 [样例要求] 使用记事 ...

  7. java me基础教程 pdf_Java ME手机应用开发技术与案例详解 PDF

    资源名称:Java ME手机应用开发技术与案例详解 PDF Java ME手机应用开发技术与案例详解基于Java ME,系统描述了Java ME手机应用开发的各个方面.全书按照Java ME程序的开发 ...

  8. 《Android 网络开发与应用实战详解》——2.3节Android系统架构

    本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第2章,第2.3节Android系统架构,作者 王东华,更多章节内容可以访问云栖社区"异步社区"公 ...

  9. 《Android 网络开发与应用实战详解》——2.1节简析Android安装文件

    本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第2章,第2.1节简析Android安装文件,作者 王东华,更多章节内容可以访问云栖社区"异步社区" ...

最新文章

  1. 用js方法做提交表单的校验
  2. 清华人工智能研究院成立,张钹姚期智分别任院长和主任
  3. linux on ios,Linux On iPhone 即将面世 iOS 设备将支持双启动
  4. Java中SeparatedListAdapter类的实现
  5. Android—Navigation的使用
  6. python标准库对象导入语句_Python标准库之Sys模块使用详解
  7. 超过4g的文件怎么上传到linux,怎么免费上传大于4G的文件到百度云 大于4G的文件不开会员怎么上传到百度云...
  8. Android应用程序键盘(Keyboard)消息处理机制分析(7)
  9. matlab矩阵倒数,求解:用MATLAB生成空间距离倒数矩阵后面板分析出现下面的几句警告...
  10. [2007-03下](Lgz独家秘笈)利用ACDSee5编辑,归档多部DC拍摄的图片
  11. 【视频检测】FlowNet: Learning Optical Flow with Convolutional Networks
  12. 剑指offer-20200226
  13. 学习笔记:Adaptive Convolutional Kernels
  14. 微服务体系中的分层设计和领域划分!
  15. ReflectionTestUtils.invokeMethod方法的使用
  16. 时空位置大数据AI平台技术实现架构设计
  17. Paper intensive reading (十三):Removing batch effects in analysis of expression microarray data
  18. Linux 网络延迟排查方法
  19. USB2.0端口设备静电防护方案设计压敏电阻或TVS管
  20. 工欲善其事必先利其器(Windows)

热门文章

  1. 导出报错cannot be resolved to absolute file path because it does not reside in the file system
  2. 关于软件试用的重要通知
  3. 安卓面试之轻松战胜内存优化问题
  4. 船说CMS4.0.1最新版无限制 内含安装教程伪静态
  5. Linux运维方向文章汇总
  6. 遥感计算机解释技术PPT,第六章遥感数字图像计算机解译ppt课件.ppt
  7. threejs重置模型的相机位置和角度
  8. Python 新手村练习——编写函数days(year,month,day)根据年月日计算这是一年的第几天,返回天数到主函数中输出
  9. 使用存储过程实现千万级的大批量数据插入
  10. linux g 编译目录下,你真的了解Linux下gcc编译器的工作过程吗?