网络驱动开发样例snull详解(基于3.10.0)
网络驱动开发样例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)相关推荐
- 【Linux驱动开发】设备树详解(二)设备树语法详解
活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...
- 《验收测试驱动开发:ATDD实例详解》—第1章1.2节临时停车
本节书摘来自异步社区<验收测试驱动开发:ATDD实例详解>一书中的第1章1.2节临时停车,作者[德]Markus Gärtner,更多章节内容可以访问云栖社区"异步社区" ...
- Linux驱动开发_设备文件系统详解
目录 何为设备管理器? Linux下dev的作用 Devfs sysfs kobject udev proc 何为设备管理器? 设备管理器就是负责管理这台电脑上的外设,当我们通过电脑提供的USB口插入 ...
- TensorFlow中cnn-cifar10样例代码详解
TensorFlow是一个支持分布式的深度学习框架,在Google的推动下,它正在变得越来越普及.我最近学了TensorFlow教程上的一个例子,即采用CNN对cifar10数据集进行分类.在看源代码 ...
- C语言必知-指针数组(附程序样例和详解)
指针数组 除了类型之外,指针变量和其他的变量很相似,只不过加上指针标识就行 例如int *api[10],由于下标引用的优先级高于间接访问(就是解引用),因此 api是一个数组,数组中的元素的类型就是 ...
- DOS子程序汇编样例及详解
[目的] 理解汇编语言中的ASSUME 伪指令和标准的汇编程序 掌握Debug-P/G/T 的关系和区别 掌握将十六进制数转换为十进制数的方法和程序 学习和改进两位数加法的程序 [样例要求] 使用记事 ...
- java me基础教程 pdf_Java ME手机应用开发技术与案例详解 PDF
资源名称:Java ME手机应用开发技术与案例详解 PDF Java ME手机应用开发技术与案例详解基于Java ME,系统描述了Java ME手机应用开发的各个方面.全书按照Java ME程序的开发 ...
- 《Android 网络开发与应用实战详解》——2.3节Android系统架构
本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第2章,第2.3节Android系统架构,作者 王东华,更多章节内容可以访问云栖社区"异步社区"公 ...
- 《Android 网络开发与应用实战详解》——2.1节简析Android安装文件
本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第2章,第2.1节简析Android安装文件,作者 王东华,更多章节内容可以访问云栖社区"异步社区" ...
最新文章
- 用js方法做提交表单的校验
- 清华人工智能研究院成立,张钹姚期智分别任院长和主任
- linux on ios,Linux On iPhone 即将面世 iOS 设备将支持双启动
- Java中SeparatedListAdapter类的实现
- Android—Navigation的使用
- python标准库对象导入语句_Python标准库之Sys模块使用详解
- 超过4g的文件怎么上传到linux,怎么免费上传大于4G的文件到百度云 大于4G的文件不开会员怎么上传到百度云...
- Android应用程序键盘(Keyboard)消息处理机制分析(7)
- matlab矩阵倒数,求解:用MATLAB生成空间距离倒数矩阵后面板分析出现下面的几句警告...
- [2007-03下](Lgz独家秘笈)利用ACDSee5编辑,归档多部DC拍摄的图片
- 【视频检测】FlowNet: Learning Optical Flow with Convolutional Networks
- 剑指offer-20200226
- 学习笔记:Adaptive Convolutional Kernels
- 微服务体系中的分层设计和领域划分!
- ReflectionTestUtils.invokeMethod方法的使用
- 时空位置大数据AI平台技术实现架构设计
- Paper intensive reading (十三):Removing batch effects in analysis of expression microarray data
- Linux 网络延迟排查方法
- USB2.0端口设备静电防护方案设计压敏电阻或TVS管
- 工欲善其事必先利其器(Windows)
热门文章
- 导出报错cannot be resolved to absolute file path because it does not reside in the file system
- 关于软件试用的重要通知
- 安卓面试之轻松战胜内存优化问题
- 船说CMS4.0.1最新版无限制 内含安装教程伪静态
- Linux运维方向文章汇总
- 遥感计算机解释技术PPT,第六章遥感数字图像计算机解译ppt课件.ppt
- threejs重置模型的相机位置和角度
- Python 新手村练习——编写函数days(year,month,day)根据年月日计算这是一年的第几天,返回天数到主函数中输出
- 使用存储过程实现千万级的大批量数据插入
- linux g 编译目录下,你真的了解Linux下gcc编译器的工作过程吗?