Linux内核代码笔记6----网络模型

来源:互联网 发布:提词器软件下载 编辑:程序博客网 时间:2024/06/14 10:40
网络协议
TCP/IP模型
  • 应用层 (TELNET、FTP、DNS)
  • 传输层(TCP、UDP)
  • Internet(IP)
  • 网络接口物理层(PPP/SLIP、LAN)
Internet协议
    IP定义了一个协议,而不是一个连接。IP主要负责数据报在计算机之间的寻址问题,并管理这些数据报的分段过程。传输具有“不可靠性”,验证和流量控制交给其他层完成。IP是无连接的
    面向连接:通信双方在通信时,事先建立一条通信线路。过程有建立连接、使用连接、释放连接等;
    无连接:   通信双方事先不需要建立通信线路,而是把带有目的的地址的包送到线路上,由系统选定路线。

TCP协议
    TCP可以向上层提供面向连接的协议,使上层启动应用程序。作用是提供可靠通信的有效报文协议。

套接字
    Linux支持多种套接字种类,不同的套接字种类称为“地址族”。Linux将上述套接字地址族抽象为统一的BSD套接字接口,比如stream、datagram、raw等。

网络设备接口
    网络设备结构:


    网络设备驱动程序功能:
  • 发送数据
        驱动将来自协议层的网络缓冲区通过hard_start_xmit()发送到物理介质,并接收硬件产生的应答信号。
        dev->hard_start_xmit():将网络缓冲区,也就是sk_buff 发送到硬件设备。如果设备不能接受缓冲区,它就会返回1。如果协议层决定释放被设备抛弃的缓冲区,那么缓冲区就不会再被送回设备;如果设备知道缓冲区短时间内不被能传送,例如设备严重堵塞,那么它就调用  dev_kfree_skb()函数丢掉缓冲区,该函数返回零值标明缓冲区已经被处理完毕。

        当缓冲区被传送到硬件以后,硬件应答信号标识传输已经完毕,驱动程序必须调用dev_kfree_skb(skb, FREE_WRITE)函数释放缓冲区,一旦该调用结束,缓冲区就会很自然地消失,这样,驱动程序就不能再涉及缓冲区了。sk_buff是被锁住的(locked),确保其他程序不会存取它。

        数据帧在传送之前先要排成对列,在加入队列之前,还要在每个数据帧的开始添加硬件帧头,这项工作对于数据传送非常必要。网络设备驱动程序提供了一个dev->hard_header()例程,来完成添加硬件帧头的工作。协议层在发送数据之前会在缓冲区的开始留下至少 dev->hard_header_len 长度字节的空闲空间。这样dev->hard_header()程序只要调用 skb_push(),然后正确填入硬件帧头就可以了。

  • 接收数据
        驱动接收来自网络上的数据帧,将其转换为能被网络协议识别的网络缓冲区,然后传递给netif_rx()函数,将数据帧传递到网络协议层做进一步处理。
        网络设备数据结构net_device,是网络驱动最重要的部分,位置在include/linux/netdevice.h。所有的网络设备的信息和操作都保存在设备数据结构中,每注册一个网络设备,都需要提供数据结构中各个域的数据。dev_queue_xmit()和netif_rx()使用了sk_buff结构(意为套接字缓冲区),定义在include/linux/skbuff.h中,用于Linux网络子系统各层间传输数据,是一个双向链表。
        网络设备驱动程序没有关于接收的处理。当它收到数据后都会产生一个中断,中断处理程序调用 dev_alloc_skb(),申请一个大小合适的缓冲区(sk_buff),把从硬件传来的数据放入缓冲区。接着,设备驱动程序分析数据包的类型,把 skb->dev设置为接收数据的设备类型 ,把 skb->protocol 设置为数据帧描述的协议类型,这样,数据帧就可以被发送到正确的协议层。硬件帧头指针保存在 skb->mac.raw中,并且硬件帧头通过调用skb_pull()被去掉,因此网络协议就不涉及硬件的信息。最后还要设置skb->pkt_type,标明链路层数据类型,设备驱动程序必须按以下类型设置skb->pkt_type

PACKET_BROADCAST     链接层广播地址  

PACKET_MULTICAST      链接层多路地址   

PACKET_SELF                  发给自己的数据帧   

PACKET_OTHERHOST    发向另一个主机的数据帧 (监听模式时会有到) 

        最后,设备驱动程序调用netif_rx()把缓冲区向上传递给协议层。netif_rx()将数据放入处理队列后返回,真正的处理是在中断返回后,可以减少中断时间。一旦netif_rx( )被调用,缓冲区就不在属设备驱动程序所有,它不能被修改,而且设备驱动程序也不能再涉及它了。

        在协议层,接收数据包的流程控制分两个层次: 首先,netif_rx()函数限制了从物理层到协议层的数据帧的数量。第二,每一个套接字都有一个队列,限制从协议层到套接字层的数据帧的数量。在传输方面,驱动程序的dev->tx_queue_len 参数用来限制队列的长度。队列的长度通常是100帧,在进行大量数据传输的高速连接中,它足以容纳下所有等待传输的缓冲区,不会出现大量缓冲区阻塞的情况。在低速连接中,例如Slip 连接,队列的长度长设为10帧左右,因为传输10帧的数据就要花费数秒的时间排列数据。


相关函数
    初始化
int (*init)(struct net_device *dev);
        设备初始化和注册时调用,执行的是底层的确认和检查工作,对硬件资源的配置。
    打开设备
int (*open)(struct net_device *dev);
        网络设备激活的时候调用(设备状态down->up)。主要功能:资源申请、硬件激活等。若驱动作为模块装入,open()里调用MOD_INC_USE_COUNT,将引用计数器加1。
        在用户态执行ifconfig eth0 up命令时,要执行两个任务。首先,它通过ioctl(SIOCSIFADDR)(Socket I/O Control Set Interface Address)赋予地址,然后通过ioctl(SIOCSIFFLAGS)(Socket I/O Control Set Interface Flags)设置dev->flag中的IFF_UP标志以打开接口。这个调用会使得设备的open方法得到调用。类似的,在接口关闭时,ifconfig使用ioctl(SIOCSIFFLAGS)来清理IFF_UP标志,然后调用stop函数。
    关闭设备
int (*stop)(struct net_device *dev);
        关闭时调用(设备状态up->down)。若驱动作为模块装入,stop()中需调用MOD_DEC_USE_COUNT。
    分配缓冲区

struct sk_buff *alloc_skb(unsigned int len, int priority);   

struct sk_buff *dev_alloc_skb(unsigned int len);  

        分配一个缓冲区。alloc_skb函数分配一个缓冲区并初始化skb->data和skb->tail为skb->head。参数len为数据缓冲区的空间大小,通常以L1_CACHE_BYTES字节(对ARM为32)对齐,参数priority为内存分配的优先级。dev_alloc_skb()函数以GFP_ATOMIC优先级进行skb的分配。

    释放缓冲区

void kfree_skb(struct sk_buff *skb);   
void dev_kfree_skb(struct sk_buff *skb);
        sk_buff结构中几个数据指针指向如图:

        对sk_buff操作的几个函数:
unsigned char *skb_put(struct sk_buff *skb, int len);   
unsigned char *skb_push(struct sk_buff *skb, int len);   
unsigned char *skb_pull(struct sk_buff *skb, int len);   
void skb_reserve(struct sk_buff ×skb, int len);  
    操作函数
int (*hard_header)(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);  
        根据先前检索到的源和目的硬件地址建立硬件头
int (*rebuild_header)(struct sk_buff *skb); 
        以太网的mac地址是固定的,为了高效,第一个包去询问mac地址,得到对应的mac地址后就会作为cache把mac地址保存起来。以后每次发包不用询问了,直接把包的地址拷贝出来。
void (*tx_timeout)(struct net_device *dev);  
        数据包发送超时失败时调用。
struct net_device_stats *(*get_stats)(struct net_device *dev);
        统计设备信息。
int (*do_ioctl)(struct net_device *dev, struct ifmap *map);
        事先自定义的ioctl命令。

reference
1、深入分析Linux内核源码
2、Linux驱动程序开发实例
3、Linux驱动修炼之道
0 0