⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch14-网络驱动程序(下)(转载).htm

📁 html格式
💻 HTM
📖 第 1 页 / 共 4 页
字号:
            color=#ffffff>联系</FONT></A></DIV></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE borderColor=#666666 cellPadding=2 width="90%" align=center border=2>
  <TBODY>
  <TR>
    <TD bgColor=#000000>
      <P align=center><A href="http://joyfire.net/lsdp/index.htm"><FONT 
      color=#ffffff size=2>目录页</FONT></A> | <A 
      href="http://joyfire.net/lsdp/17.htm"><FONT color=#ffffff 
      size=2>上一页</FONT></A> | <A href="http://joyfire.net/lsdp/19.htm"><FONT 
      color=#ffffff size=2>下一页</FONT></A></P>
      <P align=center><FONT face=黑体 color=#ffffff size=6>(LDD) 
      Ch14-网络驱动程序(下)(转载) </FONT></P><SPAN 
      style="LINE-HEIGHT: 1; LETTER-SPACING: 0pt"><FONT color=#ffffff size=3>
      <P>发信人:&nbsp;Altmayer&nbsp;(alt),&nbsp;信区:&nbsp;GNULinux<BR>标&nbsp;&nbsp;题:&nbsp;(LDD)&nbsp;Ch14-网络驱动程序(下)(转载)<BR>发信站:&nbsp;饮水思源&nbsp;(2001年12月13日08:57:59&nbsp;星期四),&nbsp;站内信件<BR>&nbsp;<BR>【&nbsp;以下文字转载自&nbsp;<FONT 
      color=#00ff00>UNIXpost&nbsp;</FONT>讨论区&nbsp;】<BR>【&nbsp;原文由<FONT 
      color=#00ff00>&nbsp;altmayer.bbs@bbs.nju.edu.cn,</FONT>&nbsp;所发表&nbsp;】<BR>&nbsp;<BR>【&nbsp;以下文字转载自&nbsp;<FONT 
      color=#00ff00>altmayer&nbsp;</FONT>的信箱&nbsp;】<BR>&nbsp;<BR>&nbsp;<BR>打开和关闭<BR>&nbsp;<BR>我们的驱动程序可以在模块加载和核心引导时探测接口。下一步是给接口赋一个地址,<BR>这样驱动程序就可以通过它交换数据了。打开和关闭一个接口由ifconfig命令完成。<BR>&nbsp;<BR>当使用ifconfig为一个接口赋地址时,它完成两项工作。第一,它通过<BR>ioctl(SIOCSIFADDR)(即Socket&nbsp;I/O&nbsp;Control&nbsp;Set&nbsp;InterFace&nbsp;ADDRess)来赋地址。接着<BR>它通过ioctl(SIOCSIFFLAGS)(即Socket&nbsp;I/O&nbsp;Control&nbsp;Set&nbsp;InterFace&nbsp;FLAGS)&nbsp;对dev-&gt;fl<BR>ag中的IFF_UP置位来打开接口。<BR>&nbsp;<BR>至于设备,ioctl(SIOCSIFADDR)设置dev-&gt;pa_addr,dev-&gt;family,dev-&gt;pa_mask,dev-<BR><FONT 
      color=#00ffff>&gt;pa_brdaddr,没有驱动程序函数被调用----这个任务是设备无关的,由核心来完成。不</FONT><BR>过,后一个命令ioctl(SIOCSIFFLAGS)为设备调用open方法。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>过,后一个命令ioctl(SIOCSIFFLAGS)为设备调用open方法。<BR>&nbsp;<BR>类似地,当一个接口关闭时,ifconfig使用ioctl(SIOCSIFFLAGS)来清除IFF_UP,并且调<BR>用stop方法。<BR>&nbsp;<BR>两个设备方法在成功时都返回0,发生错误时,通常返回一个负值。<BR>&nbsp;<BR>至于代码,驱动程序必须执行与字符和块设备同样的工作。open请求它所需要的所有的<BR>系统资源,并告诉接口启动;stop则关闭接口,并释放系统资源。<BR>&nbsp;<BR>如果驱动程序不准备使用共享中断(例如,它不打算与旧的核心兼容),还有最后一步<BR>需要做。核心引出一个irq2dev_map阵列,它由IRQ号寻址,持有空指针;驱动程序也许<BR>想用这个数组将中断号映射到指向device结构的指针。这是在不使用接口处理程序的情<BR>况下,在一个驱动程序里支持一个以上接口的唯一方法。<BR>&nbsp;<BR>另外,在接口可以和外界通信以前,硬件地址还必须从板上复制到dev-&gt;dev_addr。硬件<BR>地址可以按驱动程序的意愿在探测时或打开时被赋值。snull软件接口时从open里对其赋<BR>值;它用两个ASCII串伪造一个硬件号码。地址的第一个字节是个空字符(在后面的“地<BR>址解析”中解释)。<BR>&nbsp;<BR>结果得到的open代码如下所示:<BR>&nbsp;<BR>(代码319)<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>(代码319)<BR>&nbsp;<BR>(代码320&nbsp;#1)<BR>&nbsp;<BR>正如你所看到的,device结构中的几个域被修改了。start表明接口已准备好,&nbsp;tbusy断<BR>言发送者不忙(也就是说,核心可以发出一个包)。<BR>&nbsp;<BR>stop方法是open的操作的反转。由于这个原因,实现&nbsp;stop的函数通常调用&nbsp;close。<BR>&nbsp;<BR>(代码320&nbsp;#2)<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>包发送<BR>&nbsp;<BR>网络接口执行的最重要的工作是数据发送和接收。我准备从发送开始,因为它相对比较<BR>简单。<BR>&nbsp;<BR>当核心需要发送一个数据包时,它调用hard_start_transmit方法将数据放到一个输出队<BR>列。核心处理的每个包包含在一个套接字缓冲区结构(struct&nbsp;sk_buff)中,其定义见&lt;<BR>linux/skbuff.h&gt;。这个结构从Unix用来表示一个网络连接的抽象,即套接字得名。即使<BR>接口与套接字无关,每个网络包在较高的网络层中一定属于某个套接字,任何套接字的<BR>输入输出缓冲区都是sk_buff结构的列表。同样的sk_buff结构在整个Linux网络子系统中<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>输入输出缓冲区都是sk_buff结构的列表。同样的sk_buff结构在整个Linux网络子系统中<BR>都被用来承载网络数据,但在考虑接口时,一个套接字缓冲区就是一个包。<BR>&nbsp;<BR>指向sk_buff的指针通常被称做skb,我将在示例和正文中都使用使用这个习惯。<BR>&nbsp;<BR>套接字缓冲区是一个复杂的结构,核心提供一组函数来对其操作。这些函数在后面的“<BR>套接字缓冲区”中描述----目前,知道sk_buff的一些基本事实已足以写出可工作的驱动<BR>程序。另外,我习惯于在扎入另人讨厌的细节之前先弄明白是如何工作的。<BR>&nbsp;<BR>传递给hard_start_xmit的套接字缓冲区含有物理包,它具有传输层的包头。接口不需要<BR>修改被发送的数据。skb-&gt;data指向被发送的包,skb-&gt;len是它的长度,以八元组为单位<BR>。<BR>&nbsp;<BR>snull的包发送代码如下所示;物理发送机制被隔离在另一个函数中,因为每个接口驱动<BR>程序必须按照被驱动的特定硬件来实现它。<BR>&nbsp;<BR>(代码321)<BR>&nbsp;<BR>这样发送函数只进行一些清晰的对包的检查,并通过硬件相关的函数发送数据。在一个<BR>中断表明一个“发送结束”的条件时,dev-&gt;tbusy被清除。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>包接收<BR>&nbsp;<BR>从网络中接收数据比发送要复杂一些,因为必须分配一个sk_buff,并从一个中断处理程<BR>序中将其交递给高层----接收包的最好的办法是通过中断,除非接口是象snull一样是纯<BR>软件的,或是环回接口。尽管有可能写轮询的驱动程序,而且在正式的核心里也的确有<BR>几个,但中断驱动的要好的多,不管是在数据吞吐率还是计算需求上。由于绝大多数网<BR>络接口都是中断驱动的,我不打算谈论轮询实现,它只是利用了核心计时器。<BR>&nbsp;<BR>snull的实现是将硬件细节和设备无关的工作分离开的。这样,在硬件收到一个包后,sn<BR>ull_rx被调用,它已经在计算机的内存中了。snull_rx因此收到一个指向数据的指针和<BR>包的长度。。这个函数唯一的责任就是将包和一些额外信息发送到网络代码的高层。其<BR>代码与数据指针及长度获得的方法无关。<BR>&nbsp;<BR>(代码322)<BR>&nbsp;<BR>这个函数足够通用,可以作为任何网络驱动程序模版,但在你有信心重用这个代码段之<BR>前还需要一些解释。<BR>&nbsp;<BR>注意缓冲区分配函数需要知道数据长度。者避免了在调用kmalloc时浪费内存。dev_allo<BR>c_skb以原子优先级调用分配函数,因此它也可以在中断时安全地使用。核心还提供了套<BR>接字缓冲区分配的其它一些接口,但不值得在这里介绍;套接字缓冲区在本章后面的“<BR>套接字缓冲区”中详细介绍。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>套接字缓冲区”中详细介绍。<BR>&nbsp;<BR>一旦有了一个有效的skb指针,就可以通过调用memcpy将包数据复制到这个缓冲区。skb_<BR>put更新缓冲区中数据尾的指针,并返回一个指向新生成空间的指针。<BR>&nbsp;<BR>不幸的是,包头中没有足够的信息来正确处理网络层----在缓冲区向上层传递之前,dev<BR>和protocol域必须被赋值。接着我们需要指定如何执行校验和(snull不进行任何校验和<BR>)。skb-&gt;ip_summed可能的策略为:<BR>&nbsp;<BR>CHECKSUM_HW<BR>&nbsp;<BR>板子用硬件执行校验和。一个硬件校验和的离子是Sparc&nbsp;HME接口。<BR>&nbsp;<BR>CHECKSUM_NONE<BR>&nbsp;<BR>校验和完全有软件完成。对新分配的缓冲区,这是缺省的策略。<BR>&nbsp;<BR>CHECKSUM_UNNECESSARY<BR>&nbsp;<BR>不做任何校验和。这是snull和环回接口的策略。<BR>&nbsp;<BR>在1.2核心版本中没有校验和选项和ip_summed。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>最后,驱动程序更新它的统计计数器记录一个新包被收到了。统计结构有几个域组成,<BR>最重要的是rx_packets和tx_packets,它们包含收到的和发送的包的个数。所有的域在<BR>后面的“统计信息”中给出一个彻底的描述。<BR>&nbsp;<BR>包接收的最后一步由netif_rx完成,它将套接字缓冲区递交到上一层。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>中断驱动的操作<BR>&nbsp;<BR>大多数硬件接口以中断处理程序的方式控制。接口中断处理器表明两种事件中的一种:<BR>一个新包到达了或一个包发送完成了。这种一般化并不是总适用,但它基本上揭示了与<BR>异步包传送相关的问题。PLIP和PPP是不适用这种一般化的例子。它们处理同样的事件,<BR>但低级中断处理略有不同。<BR>&nbsp;<BR>一般的中断例程可以通过检查在硬件设备上的一个状态寄存器来分辨新包到达中断与完<BR>成发送的通知。snull接口工作方式类似,但其状态字在dev-&gt;priv中。网络接口的中断<BR>处理程序看起来如下:<BR>&nbsp;<BR>(代码324)<BR>&nbsp;<BR>处理程序的第一个任务是接收一个指向正确的device结构的指针。你可以用irq2dev_map<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>处理程序的第一个任务是接收一个指向正确的device结构的指针。你可以用irq2dev_map<BR>[](假如你在打开是给它赋了一个值)或者接收到的dev_id指针作为一个参数。如果你<BR>希望驱动程序可以与新于1.3.70的核心工作,你必须使用irq2dev_map[],因为早期版本<BR>中没有dev_id。<BR>&nbsp;<BR>这个处理程序中有趣的部分是处理“发送完成”的部分。接口通过清除dev-&gt;tbusy并标<BR>志网络下半部例程来相应发送完成。如果net_bh的确运行了,它会试图发送所有等待的<BR>包。<BR>&nbsp;<BR>另一方面,包接收并不需要任何特殊的中断处理。所有需要做的就是调用snull_rx。<BR>&nbsp;<BR>实际上,当netif_rx被接收函数调用时,它所进行的实际操作只有标志net_bh。换句话<BR>说,核心在一个下半部处理程序中完成了所有网络相关的工作。因此,网络驱动程序应<BR>该总是宣称它的中断处理程序太慢,因为下半部将会更早地执行(见第九章中“下半部<BR>设计”)。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>套接字缓冲区<BR>&nbsp;<BR>我们已经讨论了于网络接口相关的多数内容。下面几节我们将更细地讨论sk_buff时如何<BR>设计的。这几节既介绍这个结构的主要域,也介绍在套接字缓冲区上操作的函数。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>尽管并没有理解sk_buff内部的严格需要,但是如果能理解它的内容将会有助于你解决问<BR>题和优化代码。例如,如果你看了loopback.c,你会发现一个基于sk_buff内部知识的优<BR>化。<BR>&nbsp;<BR>我不打算在这里描述整个结构,而只是那些可能被驱动程序用到的域。如果你想知道更<BR>多,你可以看&lt;linux/skbuff.h&gt;,结构的定义和函数的原形都在那里定义。至于这些域<BR>和函数如何使用的细节可以通过浏览核心源码得到。<BR>&nbsp;<BR>重要的域<BR>&nbsp;<BR>出于我们的目的,结构里重要的域是那些驱动程序的作者可能要用到的域。它们如下所<BR>示,无特别顺序。<BR>&nbsp;<BR>struct&nbsp;device&nbsp;*dev;<BR>&nbsp;<BR>设备接收或者发送这个缓冲区。<BR>&nbsp;<BR>__u32&nbsp;saddr;<BR>&nbsp;<BR>__u32&nbsp;daddr;<BR>&nbsp;<BR>__u32&nbsp;raddr;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>__u32&nbsp;raddr;<BR>&nbsp;<BR>源地址,目的地址,和路由器地址,由IP协议使用。raddr是包要到达其目的地的第一步<BR>。这些域在包被发送前被设置,收到之后就不必赋值了。到达hard_start_xmit方法的外<BR>出包已经有了一个合适的硬件包头设置反映了“第一步”信息。<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;*head;<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;*data;<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;*tail;<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;*end;<BR>&nbsp;<BR>这些指针用来访问包中的数据。head指向分配空间的开始,data是有效八元组的开始(<BR>通常比head略大),tail是有效八元组的结束,&nbsp;end指向tail可以到达的最大地址。观<BR>察它们的另一个方法是:可用缓冲区空间为skb-&gt;end-skb-&gt;head,当前使用的数据空间<BR>为skb-&gt;tail-skb-&gt;data。这种处理内存区域的清晰方法在1.3开发时才实现。这是snull<BR>没有被移植在Linux1.2上编译的主要原因。<BR>&nbsp;<BR>unsigned&nbsp;long&nbsp;len;<BR>&nbsp;<BR>数据本身的长度(skb-&gt;tail-skb-&gt;head)。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>数据本身的长度(skb-&gt;tail-skb-&gt;head)。<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;ip_summed;<BR>&nbsp;<BR>这个域有驱动程序对进来包设置,由TCP/UDP校验和使用。它在前面的“包接收”中介绍<BR>过。<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;pkt_type;<BR>&nbsp;<BR>这个域被内部用来发送进来包。驱动程序负责将其设置为PACKET_HOST(这个包是我的)<BR>,&nbsp;PACKET_BROADCAST,PACKET_MULTICAST,或是PACKET_OTHERHOST(不,这个包不是我<BR>的)&nbsp;。以太网驱动程序并不显式地修改pkt_type,因为eth_type_trans会为它做这件事<BR>。<BR>&nbsp;<BR>union&nbsp;{&nbsp;unsigned&nbsp;char&nbsp;*raw;&nbsp;[…]}&nbsp;mac;<BR>&nbsp;<BR>与pkt_type类似,这个域被用来处理进来包,必须在包接收时设置。函数eth_type_tran<BR>s为以太网驱动程序负责这件事。非以太网驱动程序应设置skb-&gt;mac.raw指针,后面“非<BR>以太网包头”中将会提到。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>结构中其余的域并无特别兴趣。它们被用来维护缓冲区列表,解释占有缓冲区的套接字<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>结构中其余的域并无特别兴趣。它们被用来维护缓冲区列表,解释占有缓冲区的套接字<BR>的内存,等等。<BR>&nbsp;<BR>在套接字缓冲区上操作的函数<BR>&nbsp;<BR>使用sock_buff的网络设备通过正式的接口函数在这个结构上操作。有很多在套接字缓冲<BR>区上操作的函数,下面是最有趣的一些:<BR>&nbsp;<BR>struct&nbsp;sk_buff&nbsp;*alloc_skb(unsigned&nbsp;int&nbsp;len,&nbsp;int&nbsp;priority);<BR>&nbsp;<BR>struct&nbsp;sk_buff&nbsp;*dev_alloc_skb(unsigned&nbsp;int&nbsp;len);<BR>&nbsp;<BR>分配一个缓冲区。alloc_skb分配一个缓冲区并初始化skb-&gt;data和skb-&gt;tail到skb-&gt;hea<BR>d。&nbsp;dev_alloc_skb函数(在Linux1.2中没有)一个快捷方式,它用GFP_ATOMIC优先级调<BR>用alloc_skb,并反转skb-&gt;head和skb-&gt;data之间的16个字节。这个数据空间可以用来“<BR>推”硬件包头。<BR>&nbsp;<BR>void&nbsp;kfree_skb(struct&nbsp;sk_buff&nbsp;*skb,&nbsp;int&nbsp;rw);<BR>&nbsp;<BR>void&nbsp;dev_kfree)skb(struct&nbsp;sk_buff&nbsp;*skb,&nbsp;int&nbsp;rw);<BR>&nbsp;<BR>释放一个缓冲区。kfree_skb被核心内部使用。驱动程序应该使用dev_kfree_skb,在拥<BR>有缓冲区的套接字需要再次使用它的情况下,它可以正确地处理缓冲区加锁。两个函数<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>有缓冲区的套接字需要再次使用它的情况下,它可以正确地处理缓冲区加锁。两个函数<BR>的rw参数是FREE_READ或FREE_WRITE。这个值用来跟踪套接字的内存。外出缓冲区应用<BR>FREE_WRITE来释放,而进来的则使用FREE_READ。<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;*skb_put(struct&nbsp;sk_buff&nbsp;*skb,&nbsp;int&nbsp;len);<BR>&nbsp;<BR>这个线入函数更新结构sk_buff的tail和len域,它被用来在缓冲区尾加入数据。其返回<BR>值是skb-&gt;tail以前的值(或者说,它指向刚生成的数据空间)。有些驱动程序通过调用<BR>ins(ioaddr,skb_put(…))或memcpy(skb_put(…),&nbsp;data,len)来使用这个返回值。这个<BR>函数及下面的一些在为Linux1.2构造模块是不存在。<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;*skb_push(struct&nbsp;sk_buff&nbsp;*skb,&nbsp;int&nbsp;len);<BR>&nbsp;<BR>这个函数减小skb-&gt;data,增加skb-&gt;len。类似于skb_put,除了数据是加在包开始而不<BR>是结尾。返回值指向刚生成的空间。<BR>&nbsp;<BR>int&nbsp;skb_tailroom(struct&nbsp;sk_buff&nbsp;*skb);<BR>&nbsp;<BR>这个函数返回为在缓冲区中放置数据的可用空间量。如果驱动程序在缓冲区中放了多于<BR>它能承载的数据,系统可能回崩溃。你也许会反对并认为,用printk指出这个错误已经<BR>足够了,而内存崩溃对系统太有害了,开发者肯定要采取一些措施。但实际上,如果缓<BR>冲区被正确分配了,你根本不必检查可用空间。因为驱动程序通常在分配缓冲区之前获<BR>得包大小,只有有严重缺陷的驱动程序才可能在缓冲区内放太多的数据,崩溃可以认为<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>得包大小,只有有严重缺陷的驱动程序才可能在缓冲区内放太多的数据,崩溃可以认为<BR>是应得的惩罚。<BR>&nbsp;<BR>int&nbsp;skb_headroom(struct&nbsp;sk_buff&nbsp;*skb);<BR>&nbsp;<BR>返回数据前面得可用空间量,也就是可以向缓冲区中“推”多少八元组。<BR>&nbsp;<BR>void&nbsp;skb_reserve(struct&nbsp;sk_buff&nbsp;*skb,&nbsp;int&nbsp;len);<BR>&nbsp;<BR>这个函数增加data和tail。它可以用来在填充缓冲区前预留空间。大多数以太网接口在<BR>包前预留两个字节;这样IP头可以在一个4字节以太网头之后,在16字节边界对齐。snul<BR>l完成得很好,尽管在“包接收”中并未提到这一点,那主要是为了避免彼时引入过多得<BR>概念。<BR>&nbsp;<BR>unsigned&nbsp;char&nbsp;*skb)pull(struct&nbsp;sk_buff&nbsp;*skb,&nbsp;int&nbsp;len);<BR>&nbsp;<BR>从包头中删除数据。驱动程序并不用这个函数,但为了完整性也包含在这里。它减少skb<BR>-&gt;len,增加skb-&gt;data;这是从进来包的开始剥出以太网包头的方法。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>核心还定义了几个在套接字缓冲区上操作得别的函数,但它们主要应用于网络代码得高<BR>层,驱动程序并不需要它们。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>层,驱动程序并不需要它们。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>地址解析<BR>&nbsp;<BR>以太网通信最急迫得问题之一是硬件地址(接口得唯一标志符)与IP号码之间的关联。<BR>大多数协议都有类似问题,但我只向重点讨论一下以太网类得情况。我力图给出一个全<BR>面得描述,因此我将显示三种情况:ARP,没有ARP的以太网头(象plip),以及非以太网<BR>包头。<BR>&nbsp;<BR>在以太网上使用ARP<BR>&nbsp;<BR>地址解析得一般方法是ARP,即地址解析协议。幸运的是,ARP由核心管理,以太网接口<BR>不必为支持ARP做任何特殊工作。只要在打开时正确地设置了dev-&gt;addr和dev-&gt;addr_len<BR>,驱动程序不需担心任何从IP号码到物理地址的转换;ether_setup将正确的设备方法赋<BR>给dev-&gt;hard_header和dev-&gt;rebuild_header。<BR>&nbsp;<BR>当一个包被构造时,以太网包头由dev-&gt;hard_header来布局,并由dev-&gt;rebuild_header<BR>在后来填充,它使用ARP协议将未知的IP号码映射到地址上。驱动程序作者不必知道这个<BR>过程的细节去写一个可工作得驱动程序。<BR>&nbsp;<BR>越过ARP<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>越过ARP<BR>&nbsp;<BR>简单得点到点网络接口如plip可以从以太网包头获益,但却要避免来回发送ARP包得开销<BR>。snull中得示例代码就属于这一类网络设备。snull不能使用ARP,因为驱动程序修改被<BR>发送得包得IP地址,而ARP包也交换IP地址。<BR>&nbsp;<BR>如果你的设备想用一般的硬件包头,却不想运行ARP,你需要越过缺省的dev-&gt;rebuild_h<BR>eader方法。这就是snull实现的方法,这个简单的函数有三条语句:<BR>&nbsp;<BR>(代码329)<BR>&nbsp;<BR>事实上,并没有指定eth-&gt;h_source和eth-&gt;h_dest内容的实际需要,因为这些值只被用<BR>来进行包得物理传送,而一个点到点得连接保证能将包发送到它的目的地,而与硬件地<BR>址无关。snull重构包头的原因是向你演示,当eth_rebuild_header不可用时,一个真实<BR>的网络接口的重构函数是如何实现的,<BR>&nbsp;<BR>当接口收到一个包时,硬件包头只被eth_type_trans使用。我们在snull_rx中已经见过<BR>这个调用:<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;skb-&gt;protocol=eth_type_trans(skb,dev);<BR>&nbsp;<BR>这个函数从以太网包头中抽取协议标志符(在这里是ETH_P_IP);它还要赋值skb-&gt;mac.<BR>raw,从包数据中删去硬件包头,并设置skb-&gt;pkt_type。最后一项在skb分配时缺省为PA<BR></P></FONT><FONT 
      color=#ffffff size=3>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -