192.168.1.1-路由器设置 > 192.168.1.1 > 192.168.1.2 >

Linux串口上网的简单实现(2)

文章摘要

从设备中读取数据(用户空间调用read()系统调用)的时候,需要从内核空间把数据拷贝到用户空间,copy_to_user()可完成此功能,它和memcpy()此类函数有本质的区别,memcpy()不能完成不同用户空间数据的交换。如果需请看如下

 

  

  从设备中读取数据(用户空间调用read()系统调用)的时候,需要从内核空间把数据拷贝到用户空间,copy_to_user()可完成此功能,它和memcpy()此类函数有本质的区别,memcpy()不能完成不同用户空间数据的交换。如果需要数据临界区的,使用spin_lock()内核API负责加锁,spin_unlock()负责解锁,防止数据污染。由于串口守候进程server需要不断轮询设备,以查询是否有数据可读,如果用户进程不处于休眠状态,在用户空间查看进程使用资源情况,发现server占用了很多CPU资源。所以我们改进device_read(),使之在内核中轮询,当发现当前设备没有数据可读取,那么就阻塞用户进程,使用内核APIadd_wait_queue()可完成此功能,这时候用户进程并没有占用很多CPU资源,而是处于休眠状态。当内核发现有数据可读的时候,调用remove_wait_queue()即可等待进程,这段

  代码如下:

  

  我们看到我们的伪网络接口没有Interrupt和Baseaddress,这是因为这个伪网络接口不和硬件打交道,也没有分配中断号和IO基址。否则,如果你看一个实实在在的网络接口(如下面的eth1),可以看到它的Interrupt号是11和IOBaseaddress是0xa000。

  ether_setup()填充一些以太网的缺省设置。dev->hard_header_cache=NULL表示不缓存向本网络接口回复的ARP网络数据包。IFF_NOARP的标志设置表明本网络接口不使用ARP。ARP的主要功能是获得通信对方的网络接口的硬件地址,本文的伪网络接口的物理地址是程序中设定的伪物理地址,所以我们不需要ARP协议。SET_MODULE_OWNER(dev)这个宏是设置dev结构中owner域(定义为structmoduleowner;),使得它指向本模块本身。与字符设备一样,本网络设备也需要定义在其上的操作例程。下面就对ednet_init()中用户定义的设备操作函数做进一步说明。整个伪网络设备操作调用结构如图6所示。

  图6

  

  由图6我们看到,ednet_rx()并不是网络设备的一个操作,而是模块中的一个函数。在实际的网卡驱动程序中,当网卡确实接收到数据的时候,由网络中断等待接收数据的用户进程,也就是说,ednet_rx()应该由那个网络中断处理例程调用。我们这里并没有中断,所以字符设备的device_write()可以看成是一个中断例程,也就是说,用户空间往字符写操作的时候,也就调用了网络设备的数据接收内核例程ednet_rx()了。然后ednet_rx()会把原始的数据包发送到TCP/IP上层进行处理,这一切均依赖于内核API函数netif_rx()。ednet_rx()就需要sk_buff数据结构(<linux/skbuff.h>中定义),用来存放从网络接口接收到的原始网络数据,分配后的sk_buff结构将在TCP/IP协议栈上被掉。

  下面介绍一下网络设备的主要操作例程ednet_open()、ednet_release()、ednet_tx()、ednet_stats()、ednet_change_mtu()、ednet_header()。网络设备文件操作结构structnet_device(<linux/netdevice.h>中有定义)中定义了指向以上函数的函数指针的原形:

  操作intednet_open(structnet_devicedev)的作用是打开伪网络接口设备,获得其需要的I/O端口、IRQ等,但是本网络接口不需要和实际硬件打交道,所以不需要自动获得或者赋予I/O端口值,也不需要IRQ中断号,唯一需要程序指定的是其伪硬件地址(这个硬件地址是0ED000,ifconfig可以看到其硬件地址是00:45:44:30:30:30,structnet_device中的dev_addr域存放网络接口的物理地址。操作ednet_open()必须调用netif_start_queue()内核API网络接口接收和发送数据队列。

  当接口关闭的时候,intednet_release(structnet_devicedev)例程被系统调用,在ednet_release()中调用netif_stop_queque()将停止接收和发送队列的工作。

  伪网络设备驱动的传送例程intednet_tx(structsk_buffskb,structnet_devicedev)将把要发送的网络数据包写入字符设备ed[ED_TX_DEVICE]。在发送完毕数据包的时候,dev_kfree_skb()KernelAPI由上层协议栈分配的sk_buff数据块。伪网络接口在进行硬件传输的时候,需要为网络数据包打上时间戳。如果传送数据包的时候超时,将调用超时处理例程ednet_tx_timeout()超时处理例程。例程ednet_tx()调用真正的硬件传送例程ednet_hw_tx()在实际的网卡驱动程序中,就是真正向特定的网络硬件设备写数据的程序。我们看到,我们的硬件就是本文前面描述的字符设备,字符设备的操作例程.kernel_write()在ednet_hw_tx()将被调用。

  如果我们希望使用ifconfig看到伪网络接口的统计信息,那么系统就调用structnet_device_statsednet_stats(structnet_devicedev)。我们看到,网络接口的统计信息被放到设备的私有数据指针指向的内存。网络数据信息的统计结构被放在内核结构structnet_device_stats中。

  在TCP会话中,也许要协商MTU的大小,intednet_change_mtu(structnet_devicedev,intnew_mtu)可以随时改变MTU的大小。比如在使用FTP协议的时候,在传送数据库的时候,MTU可能被协商为最大,以提高网络传送吞吐量。由于改变了MTU,存放网络数据的字符设备初始化分配的缓存区就要重新被分配,并把已经存放数据的旧的缓存区的内容拷贝到新的缓存区中,所以,当MTU改变大小的时候,那么就要使用kmalloc(new_mtu,GFP_KERNEL)重新分配缓存区。读者可以根据自己的需要定义新的缓存区大小。kfree()是内核API,负责内核空间的内存,它的使用方法和用户空间的free()系统调用一致,这里就不列举ed_realloc()函数的源程序了。

  IP数据包在被网络接口发送前,需要构建其以太网头信息intednet_header(structsk_buffskb,structnet_devicedev,unsignedshorttype,voiddaddr,voidsaddr,unsignedintlen)例程完成此功能,我们看到网络数据包的以太源、目的地址,都是从发送这个数据包的网络接口设备数据结构structnet_device中得到的。源地址和目的地址信息是从网络设备结构得到的。在编译本程序的时候,如果发现htons()这个函数没有定义,可以这样定义htons()为:#definehtons(x)((x>>8)(x<<8))。

  因为伪网络接口没有使用ARP获得硬件地址,所以我们可以把我们自己定义的伪硬件地址复制到数据包的以太网包头。Linux2.4.x使用设备方法hard_header()代替设备

  • 共3页:
  • 上一页
  • 1
  • 2
  • 3
  • 下一页
  • 分享到:

    tags:192.168.5.1

    最近更新-关于我们 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明
    CopyRight2009-2011 All Rights Reserved 192.168.1.1 路由器设置jmqy.com