在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层。

网络堆栈是硬件中独立出来的部分。主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层和网络硬件的中间层。

网络设备驱动程序的主要功能是:

(1)模块载入或内核启动相关的初始化处理

(2)清除模块时的处理

(3)网络设备的检索和探測

(4)网络设备的初始化和注冊

(5)打开或关闭网络设备

(6)发送网络数据

(7)接收网络数据

(8)中断处理(在发送完数据时。硬件向内核产生一个中断。告诉内核数据已经发送完成。在网络设备接收到数据时,也要发生一个中断,告诉内核。数据已经到达,请及时处理)

(9)超时处理

(10)多播处理

(11)网络设备的控制ioctl

而Linux网络设备驱动的主要功能就是网络设备的初始化,网络设备的配置,数据包的收发。

以下代码是关于虚拟硬件的网络驱动的样例

2、代码

  1. #undef PDEBUG /* undef it, just in case */
  2. #ifdef SNULL_DEBUG
  3. # ifdef __KERNEL__
  4. /* This one if debugging is on, and kernel space */
  5. # define PDEBUG(fmt, args...) printk( KERN_DEBUG "snull: " fmt, ## args)
  6. # else
  7. /* This one for user space */
  8. # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
  9. # endif
  10. #else
  11. # define PDEBUG(fmt, args...) /* not debugging: nothing */
  12. #endif
  13. #undef PDEBUGG
  14. #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
  15.  
  16. /* These are the flags in the statusword */
  17. #define SNULL_RX_INTR 0x0001
  18. #define SNULL_TX_INTR 0x0002
  19. /* Default timeout period */
  20. #define SNULL_TIMEOUT 6 /* In jiffies */
  21. #include <linux/module.h>
  22. #include <linux/sched.h>
  23. #include <linux/kernel.h> /* printk() */
  24. #include <linux/slab.h> /* kmalloc() */
  25. #include <linux/errno.h> /* error codes */
  26. #include <linux/types.h> /* size_t */
  27. #include <linux/interrupt.h> /* mark_bh */
  28. #include <linux/in.h>
  29. #include <linux/netdevice.h> /* struct device, and other headers */
  30. #include <linux/etherdevice.h> /* eth_type_trans */
  31. #include <linux/ip.h> /* struct iphdr */
  32. #include <linux/tcp.h> /* struct tcphdr */
  33. #include <linux/skbuff.h>
  34. #include <linux/if_ether.h>
  35. #include <linux/in6.h>
  36. #include <asm/uaccess.h>
  37. #include <asm/checksum.h>
  38.  
  39. static int lockup = 0;
  40. static int timeout = SNULL_TIMEOUT;
  41. struct net_device snull_devs[2];//这里定义两个设备,一个是snull0,一个是snull1
  42. //网络设备结构体,作为net_device->priv
  43. struct snull_priv {
  44. struct net_device_stats stats;//实用的统计信息
  45. int status;//网络设备的状态信息。是发完数据包。还是接收到网络数据包
  46. int rx_packetlen;//接收到的数据包长度
  47. u8 *rx_packetdata;//接收到的数据
  48. int tx_packetlen;//发送的数据包长度
  49. u8 *tx_packetdata;//发送的数据
  50. struct sk_buff *skb;//socket buffer结构体,网络各层之间传送数据都是通过这个结构体来实现的
  51. spinlock_t lock;//自旋锁
  52. };
  53.  
  54. void snull_tx_timeout (struct net_device *dev);
  55.  
  56. //网络接口的打开函数
  57. int snull_open(struct net_device *dev)
  58. {
  59. printk("call snull_open/n");
  60. memcpy(dev->dev_addr, "/0SNUL0", ETH_ALEN);//分配一个硬件地址,ETH_ALEN是网络设备硬件地址的长度
  61.  
  62. netif_start_queue(dev);//打开传输队列,这样才干进行传输数据
  63.  
  64. return 0;
  65. }
  66. int snull_release(struct net_device *dev)
  67. {
  68. printk("call snull_release/n");
  69. netif_stop_queue(dev); //当网络接口关闭的时候,调用stop方法。这个函数表示不能再发送数据
  70. return 0;
  71. }
  72.  
  73. //接包函数
  74. void snull_rx(struct net_device *dev, int len, unsigned char *buf)
  75. {
  76.  
  77. struct sk_buff *skb;
  78. struct snull_priv *priv = (struct snull_priv *) dev->priv;
  79.  
  80. /*
  81. * The packet has been retrieved from the transmission
  82. * medium. Build an skb around it, so upper layers can handle it
  83. */
  84.  
  85. skb = dev_alloc_skb(len+2);//分配一个socket buffer,而且初始化skb->data,skb->tail和skb->head
  86. if (!skb) {
  87. printk("snull rx: low on mem - packet dropped/n");
  88. priv->stats.rx_dropped++;
  89. return;
  90. }
  91. skb_reserve(skb, 2); /* align IP on 16B boundary */
  92. memcpy(skb_put(skb, len), buf, len);//skb_put是把数据写入到socket buffer
  93. /* Write metadata, and then pass to the receive level */
  94. skb->dev = dev;
  95. skb->protocol = eth_type_trans(skb, dev);//返回的是协议号
  96. skb->ip_summed = CHECKSUM_UNNECESSARY; //此处不校验
  97. priv->stats.rx_packets++;//接收到包的个数+1
  98.  
  99. priv->stats.rx_bytes += len;//接收到包的长度
  100. netif_rx(skb);//通知内核已经接收到包。而且封装成socket buffer传到上层
  101. return;
  102. }
  103.  
  104. /*
  105. * The typical interrupt entry point
  106. */
  107. //中断处理。此程序中没有硬件,因此,没有真正的硬件中断,仅仅是模拟中断,在发送完网络数据包之后。会产生中断
  108. //用来通知内核已经发送完数据包,当新的数据包到达网络接口时,会发生中断。通知新的数据包已经到来了
  109. void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  110. {
  111.  
  112. int statusword;//用来标识是发送完成还是接收到新的数据包
  113. struct snull_priv *priv;
  114. /*
  115. * As usual, check the "device" pointer for shared handlers.
  116. * Then assign "struct device *dev"
  117. */
  118. struct net_device *dev = (struct net_device *)dev_id;
  119. /* ... and check with hw if it's really ours */
  120. if (!dev /*paranoid*/ ) return;
  121. /* Lock the device */
  122. priv = (struct snull_priv *) dev->priv;
  123. spin_lock(&priv->lock);
  124. /* retrieve statusword: real netdevices use I/O instructions */
  125. statusword = priv->status;
  126. if (statusword & SNULL_RX_INTR) {//假设是接收
  127. /* send it to snull_rx for handling */
  128. snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata);
  129. }
  130. if (statusword & SNULL_TX_INTR) {//假设发送完成
  131. /* a transmission is over: free the skb */
  132. priv->stats.tx_packets++;
  133. priv->stats.tx_bytes += priv->tx_packetlen;
  134. dev_kfree_skb(priv->skb);//释放skb 套接字缓冲区
  135. }
  136. /* Unlock the device and we are done */
  137. spin_unlock(&priv->lock);
  138. return;
  139. }
  140.  
  141. /*
  142. * Transmit a packet (low level interface)
  143. */
  144. //真正的处理的发送数据包
  145. //模拟从一个网络向还有一个网络发送数据包
  146. void snull_hw_tx(char *buf, int len, struct net_device *dev)
  147.  
  148. {
  149.  
  150. /*
  151. * This function deals with hw details. This interface loops
  152. * back the packet to the other snull interface (if any).
  153. * In other words, this function implements the snull behaviour,
  154. * while all other procedures are rather device-independent
  155. */
  156. struct iphdr *ih;//ip头部
  157. struct net_device *dest;//目标设备结构体。net_device存储一个网络接口的重要信息,是网络驱动程序的核心
  158. struct snull_priv *priv;
  159. u32 *saddr, *daddr;//源设备地址与目标设备地址
  160. /* I am paranoid. Ain't I? */
  161. if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
  162. printk("snull: Hmm... packet too short (%i octets)/n",
  163. len);
  164. return;
  165. }
  166.  
  167. /*
  168. * Ethhdr is 14 bytes, but the kernel arranges for iphdr
  169. * to be aligned (i.e., ethhdr is unaligned)
  170. */
  171. ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
  172. saddr = &ih->saddr;
  173. daddr = &ih->daddr;
  174. //在同一台机器上模拟两个网络。不同的网段地址,进行发送网络数据包与接收网络数据包
  175. ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) ^是位异或操作符把第三个部分的网络地址与1进行异或,因为同一网络的数据不进行转发*/
  176. ((u8 *)daddr)[2] ^= 1;
  177. ih->check = 0; /* and rebuild the checksum (ip needs it) */
  178. ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
  179. if (dev == snull_devs)
  180. PDEBUGG("%08x:%05i --> %08x:%05i/n",
  181. ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
  182. ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
  183. else
  184. PDEBUGG("%08x:%05i <-- %08x:%05i/n",
  185. ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
  186. ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));
  187.  
  188. /*
  189. * Ok, now the packet is ready for transmission: first simulate a
  190. * receive interrupt on the twin device, then a
  191. * transmission-done on the transmitting device
  192. */
  193. dest = snull_devs + (dev==snull_devs ? 1 : 0);//假设dev是0,那么dest就是1,假设dev是1。那么dest是0
  194. priv = (struct snull_priv *) dest->priv;//目标dest中的priv
  195. priv->status = SNULL_RX_INTR;
  196. priv->rx_packetlen = len;
  197. priv->rx_packetdata = buf;
  198. snull_interrupt(0, dest, NULL);
  199. priv = (struct snull_priv *) dev->priv;
  200. priv->status = SNULL_TX_INTR;
  201. priv->tx_packetlen = len;
  202. priv->tx_packetdata = buf;
  203. if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {
  204. /* Simulate a dropped transmit interrupt */
  205. netif_stop_queue(dev);
  206.  
  207. PDEBUG("Simulate lockup at %ld, txp %ld/n", jiffies,
  208. (unsigned long) priv->stats.tx_packets);
  209. }
  210. else
  211. snull_interrupt(0, dev, NULL);
  212. }
  213.  
  214. /*
  215. * Transmit a packet (called by the kernel)
  216. */
  217.  
  218. //发包函数
  219. int snull_tx(struct sk_buff *skb, struct net_device *dev)
  220. {
  221.  
  222. int len;
  223. char *data;
  224. struct snull_priv *priv = (struct snull_priv *) dev->priv;
  225.  
  226. if ( skb == NULL) {
  227. PDEBUG("tint for %p, skb %p/n", dev, skb);
  228. snull_tx_timeout (dev);
  229. if (skb == NULL)
  230. return 0;
  231. }
  232.  
  233. len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;//ETH_ZLEN是所发的最小数据包的长度
  234. data = skb->data;//将要发送的数据包中数据部分
  235. dev->trans_start = jiffies; //保存当前的发送时间
  236. priv->skb = skb;
  237. snull_hw_tx(data, len, dev);//真正的发送函数
  238. return 0; /* Our simple device can not fail */
  239. }
  240. /*
  241. * Deal with a transmit timeout.
  242. */
  243.  
  244. //一旦超出watchdog_timeo就会调用snull_tx_timeout
  245. void snull_tx_timeout (struct net_device *dev)
  246. {
  247. printk("call snull_tx_timeout/n");
  248. struct snull_priv *priv = (struct snull_priv *) dev->priv;
  249. PDEBUG("Transmit timeout at %ld, latency %ld/n", jiffies,
  250. jiffies - dev->trans_start);
  251. priv->status = SNULL_TX_INTR;
  252. snull_interrupt(0, dev, NULL);//超时后发生中断
  253. priv->stats.tx_errors++;//发送的错误数
  254. netif_wake_queue(dev); //为了再次发送数据,调用此函数,又一次启动发送队列
  255. return;
  256. }
  257.  
  258. /*
  259. * Ioctl commands
  260. */
  261. int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
  262. {
  263.  
  264. PDEBUG("ioctl/n");
  265. return 0;
  266. }
  267. /*
  268. * Return statistics to the caller
  269. */
  270. struct net_device_stats *snull_stats(struct net_device *dev)
  271. {
  272. struct snull_priv *priv = (struct snull_priv *) dev->priv;
  273. return &priv->stats;//得到统计资料信息
  274. }
  275.  
  276. //设备初始化函数
  277. int snull_init(struct net_device *dev)
  278. {
  279. printk("call snull_init/n");
  280.  
  281. /*
  282. * Then, assign other fields in dev, using ether_setup() and some
  283. * hand assignments
  284. */
  285. ether_setup(dev);//填充一些以太网中的设备结构体的项
  286. dev->open = snull_open;
  287. dev->stop = snull_release;
  288. //dev->set_config = snull_config;
  289. dev->hard_start_xmit = snull_tx;
  290. dev->do_ioctl = snull_ioctl;
  291. dev->get_stats = snull_stats;
  292. //dev->change_mtu = snull_change_mtu;
  293. // dev->rebuild_header = snull_rebuild_header;
  294. //dev->hard_header = snull_header;
  295.  
  296. dev->tx_timeout = snull_tx_timeout;//超时处理
  297. dev->watchdog_timeo = timeout;
  298.  
  299. /* keep the default flags, just add NOARP */
  300. dev->flags |= IFF_NOARP;
  301. dev->hard_header_cache = NULL; /* Disable caching */
  302. SET_MODULE_OWNER(dev);
  303. /*
  304. * Then, allocate the priv field. This encloses the statistics
  305. * and a few private fields.
  306. */
  307. //为priv分配内存
  308. dev->priv = kmalloc(sizeof(struct snull_priv), GFP_KERNEL);
  309. if (dev->priv == NULL)
  310. return -ENOMEM;
  311. memset(dev->priv, 0, sizeof(struct snull_priv));
  312. spin_lock_init(& ((struct snull_priv *) dev->priv)->lock);
  313. return 0;
  314. }
  315.  
  316. struct net_device snull_devs[2] = {
  317. { init: snull_init, }, /* init, nothing more */
  318. { init: snull_init, }
  319. };
  320.  
  321. int snull_init_module(void)
  322. {
  323. int i,result=0;
  324. strcpy(snull_devs[0].name,"snull0");//net_device结构体中的name表示设备名
  325. strcpy(snull_devs[1].name,"snull1");//即定义了两个设备,snull0与snull1
  326. for (i=0; i<2; i++)
  327. if ( (result = register_netdev(snull_devs+i)) )//注冊设备
  328. printk("snull: error %i registering device /"%s/"/n",
  329. result, snull_devs[i].name);
  330. return 0;
  331. }
  332. void snull_cleanup(void)
  333. {
  334. int i;
  335.  
  336. for (i=0; i<2; i++) {
  337. kfree(snull_devs[i].priv);
  338. unregister_netdev(snull_devs+i);
  339. }
  340. return;
  341. }
  342.  
  343. module_init(snull_init_module);
  344. module_exit(snull_cleanup);

Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)的更多相关文章

  1. input子系统驱动学习之中的一个

        刚開始学习linux这门课就被分配编写一个设备的input子系统驱动.这对我的确有点困难.只是实际的操作中发现困难远比我想象的要大的多.本以为依照老师课上的步骤就行非常快的完毕这项任务.后来发 ...

  2. Linux网卡聚合时,其中一个网卡有两种配置的解决方法

    先来看看: ficonfig 其中第一网卡是ssh使用: 第二个网卡是在Linux 最小化安装后IP的配置(手动获取静态IP地址)这个文章中配置过ip是192.168.1.2:在Linux重命名网卡名 ...

  3. Solr 6.7学习笔记(02)-- 配置文件 managed-schema (schema.xml) -- 样例(6)

    managed-schema 样例: <?xml version="1.0" encoding="UTF-8" ?> <!-- License ...

  4. VB.net数据库编程(03):一个SQLserver连接查询的简单样例

    这个样例,因为在ADO.net入门已经专门学了,再次进行复习 一下. 主要掌握连接字串的情况. 过程就是: 1.引用System.Data.SqlClient.而Access中引用 的是System. ...

  5. Hibernate学习---第十节:Hibernate之QBC、样例查询&离线查询

    一.QBC (Query By Criteria) 主要有Criteria,Criterion,Oder,Restrictions类组成 1.java 代码如下: /** * 查询所有 */ @Tes ...

  6. Linux摄像头驱动学习之:(四)UVC-摄像头驱动框架分析

    UVC: USB Video ClassUVC驱动:drivers\media\video\uvc\ uvc_driver.c分析:1. usb_register(&uvc_driver.dr ...

  7. Linux驱动学习(编写一个最简单的模块)

    在Linux中想做驱动开发,那么一定要先熟悉module的使用和编写 一.什么是module 从名字上看就是模块的意思,我个人的理解就是一个一个的小程序,可以进行动态的安装和卸载,而在这里面就实现一些 ...

  8. Linux 网卡驱动学习(二)(网络驱动接口小结)

    [摘要]前文我们分析了一个虚拟硬件的网络驱动例子,从中我们看到了网络设备的一些接口,其实网络设备驱动和块设备驱动的功能比较类似,都是发送和接收数据包(数据请求).当然它们实际是有很多不同的. 1.引言 ...

  9. linux设备驱动第三篇:如何实现一个简单的字符设备驱动

    在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...

随机推荐

  1. [ Openstack ] Openstack-Mitaka 高可用之 Dashboard

    目录 Openstack-Mitaka 高可用之 概述    Openstack-Mitaka 高可用之 环境初始化    Openstack-Mitaka 高可用之 Mariadb-Galera集群 ...

  2. serialVersionUID的作用(zz)

    http://www.cnblogs.com/guanghuiqq/archive/2012/07/18/2597036.html 简单来说,Java的序列化机制是通过在运行时判断类的serialVe ...

  3. Spring:基于注解的依赖注入的使用

    1.什么是pojo?什么是bean? 首先,在之前几篇Spring的介绍文章当中,自己都提到了一个名词叫做POJO类,但是在回顾Spring的注解的使用的时候,去形容java当中的对象还有一个名词是叫 ...

  4. Appium +Python 连接真机测试

    1.数据线连接电脑和手机: 2.用adb获取手机的UUID:cmd-> adb devices 前面的就是你手机的UUID 3.打开appium,选择手机的安卓版本(关于手机中查看),填写手机的 ...

  5. HDU 2503 (数论,最大公约数)

    a/b + c/d Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  6. [BZOJ4530]大融合

    LCT维护子树信息 维护两个子树信息,$vinf_x$表示节点$x$的所有轻儿子子树信息,$inf_x$表示以$x$为根的LCT子树(包含虚边)的信息 对$vinf$: access时,断开$x$的原 ...

  7. 【贪心】bzoj3850 ZCC Loves Codefires

    类似某noip国王游戏. 考虑交换两个题目的顺序,仅会对这两个题目的贡献造成影响. 于是sort,比较时计算两个题目对答案的贡献,较小的放在前面. #include<cstdio> #in ...

  8. MR实现--矩阵乘法

    import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.io ...

  9. 13南理工test01:进制转化

    #include<iostream> #include<cstdlib> using namespace std; int main() { //cout<<5/2 ...

  10. Saga的实现模式——进化(Saga implementation patterns – variations)

    在之前的几个博客中,我主要讲了两个saga的实现模式: 基于command的控制者模式 基于事件的观察者模式 当然,这些都不是实现saga的唯一方式.我们甚至可以将这些结合起来. 发布者——收集者 回 ...