您的位置:首页 > 运维架构 > 网站架构

嵌入式学习-驱动开发-lesson7.1-网卡驱动架构分析驱动及CS8900流程分析

2016-08-10 21:43 295 查看
PART1 网卡驱动架构分析

一、网络子系统

在内核相关一课中,已经介绍过,linux内核一共有7个子系统(模块):

1.SCI

2.Pm

3.MM

4.Arch

5.Vfs

6.Network stack

7.DD

并且在前面已经讲过内存管理子系统和进程管子系统,今天主要讲解linux网络子系统。

首先看一下网络子系统的主要组成部分,如下图:



如上图所示,处于内核空间的linux网络子系统主要由5个部分组成:

1.系统调用接口

面向应用程序,提供一些 socket send等网络系统调用接口

2.协议无关接口

系统调用接口要访问网络协议栈,不同的接口有不同的协议,如IP,tcp,udp等,太繁杂,利用协议无关接口将下层的所有协议统一起来,提供给系统调用接口一个统一的接口

3.网络协议栈

实现一些具体的网络协议 TCP UDP ip协议等

4.设备无关接口

利用此接口将驱动统一起来,不管驱动程序如何,或者说是如何写的,函数接口都是统一的

5.设备驱动程序:网卡驱动程序位于这一部分,例如 cs8900 dm9000驱动

二、重要数据结构

1) 网卡描述结构

在Linux内核中,每个网卡都由一个net_device结构来描述,其中的一些重要成员有:

char name[IFNAMSIZ]

设备名,如:eth%d

unsigned long base_addr

I/O 基地址

const struct net_device_ops *netdev_ops;

网卡操作函数集

2) 网卡操作集合

类似于字符设备驱动中的file_operations结构,net_device_ops结构记录了网卡所支持的操作。

下面是dm9000的操作函数集:

static const struct net_device_ops dm9000_netdev_ops =

{

.ndo_open = dm9000_open,

.ndo_stop = dm9000_stop,

.ndo_start_xmit = dm9000_start_xmit,

.ndo_do_ioctl = dm9000_ioctl,

.ndo_validate_addr = eth_validate_addr,

.ndo_set_mac_address = eth_mac_addr,

};

3) 网络数据包

Linux内核中的每个网络数据包(由网络协议栈产生)都由一个套接字缓冲区结构 struct sk_buff 描述,即一个sk_buff结构就是一个网络包,指向sk_buff的指针通常被称做skb。通常为:struct sk_buff skb;

下面为skb的组成部分:



head:包最开始的起点

data:有效数据的开始

tail:有效数据的结尾

end:包的终点

三、CS8900分析

1).初始化

1分配net_device结构

在cs8900.c中,在其入口函数中,使用函数alloc_etherdev为其分配结构



2初始化net_device结构

在接下来的过程中,主要工作是对net_device的结构进行了初始化,主要包括以下几个方面:

中断号irq和基地址base_addr的初始化



MAC地址



netdev_ops的初始化



3初始化硬件

这里暂且不分析硬件相关的

4注册网卡驱动

使用register_netdev函数注册网卡驱动



2)网卡发送函数

在net_device_ops函数中,有网卡发送函数ndo_start_xmit,进入到这个函数,分析其数据发送流程



1告诉上层协议,在发送数据,暂停接收其它数据

netif_stop_queue(dev);



2将skb中的数据写入网卡寄存器,发送走



3释放skb结构

dev_kfree_skb (skb);



4 当发送完数据后,网卡会产生一个中断,因此在中断处理函数net_interrupt中,通知上层协议,可以接收数据

netif_wake_queue



3)数据接收函数

对于网卡的数据接收,不像网卡的数据发送那样,在操作函数集中有一个发送函数,而是会产生一个接收中断,在接收中断中则进行接收数据的处理



1读取接收状态

status = readword(ioaddr, RX_FRAME_PORT);

2读取接收的数据长度

length = readword(ioaddr, RX_FRAME_PORT);



3构造一个skb结构,存放接收到的数据

skb = dev_alloc_skb(length + 2);



4从网卡寄存器中读出数据,存入skb



5将收到的数据包skb交给协议栈处理

netif_rx(skb);



流程图如下:



PART2 网络子系统深度剖析

本次需要解决两个问题:

1.用户程序怎样把数据经过linux内核交给网卡去发送

2.网卡收到的数据怎样经过linux内核交给用户态程序

一、网络发包模型



如上图所示,一个安转了Linux系统的PC1,有两个网卡W1 W2,W1的IP为192.168.1.10,他接到了一个路由器Y1上,这个路由器Y1的网关为192.168.1.1,这个路由器又接到了另外一个网络,这个网络中有一个PC2,这个PC2的IP为168.1.1.10,第二个网卡W2,IP地址10.0.0.30,接到了路由器Y2上面,IP地址为10.0.0.1,路由器Y2又接到了另外的网络上,并且有一个PC3.

现在PC1上有一个数据包,要发送到PC2上面去,现在问题来了?这个数据包如何发送出去,是经过W1?W2?PC1如何决定发送路径?

Linux系统会根据系统中的一个东西—路由表来决定,在这个路由表中,有一些路由信息,例如,会存储PC2 168.1.1.10的数据由路由器网关192.168.1.1转发,那Linux会根据这条路由信息选择相应的路径

因此上面的主要流程为:

1.选择路由,即选择好路径,由哪一个路由器发送

2.选择邻居子系统,建立邻居信息

Linux将数据发给PC2,即将数据发给路由器,然后路由器再将数据发送给PC2,路由器在发送数据的过程中,充当一个发送路径上的邻居的角色。

注意:数据给路由器,那么就需要知道路由器的Mac地址,若不知道Mac地址,则需要通过arp获取Mac地址

二、UDP数据发送流程分析

在前面已经介绍过,Linux网络子系统主要由5个部分组成,现在假设有一个UDP的包,要发送,观察这个包在这5个部分的主要流程是如何?

1).SCI和协议无关层

对于要发送udp包,首先要创建一个socket系统调用,然后使用write发送数据,既然要使用socket,那么我们就需要首先知道socket对应的fileoperation,然后通过fileoperation找到在Linux系统中的入口点



如上图,socket_file_ops便是socket对应的fileoperation,

在fileoperation中 sock_aio_write 函数便是整个发包函数,在Linux内核中的入口,



sock_aio_write又调用do_sock_write函数,



do_sock_write函数又调用__sock_sendmsg函数,

上面这三个函数便是sci和协议无关层,同时这两层也没有涉及到具体的协议,即不管发送的是UDP、TCP或者IP,所调用的函数都是一致的,而在下一层网络协议栈,则根据不同的协议构造不同的数据包。

2).网络协议栈

因为我们是发送UDP,所以要找到udp相关的数据发送函数。



udp_sendmsg便是网络协议栈的入口,在这个函数中做了很多事情,其中。重要的有:



调用ip_route_output_flow这个函数,这个函数的重要作用是选择路由



又调用udp_push_pending_frames函数,udp_push_pending_frames又调用



有点小疑问,现在是UDP数据的发送流程,为何出现了与IP有关的函数?

一个数据进来之后,先经过udp协议的处理,然后交给IP协议处理,其处理入口就是Ip_push_pending_frames

先上一张高清无码的大图



接下来分析ip协议是如何处理的?

ip_push_pending经过一系列的调用的过程中,在ip_finish_output2函数中完成了建立邻居信息这个任务

在函数ip_finish_output2函数中,建立邻居信息



判断路由是否有邻居的信息,如果有,则直接发送,如果没有,则调用邻居子系统,建立邻居信息。



调用neigh_resolve_output函数



3)设备无关接口











首先将网卡的的操作函数集赋值给ops,然后ops调用操作函数集里面的指针,ndo_start_xmit,实现数据的发送,这一点就涉及到驱动层了,可以联系上面的cs8900的ndo_start_xmit进行分析。

总结:要发送一个数据包,需要调用write系统调用,依次经过 SCI、协议无关接口、协议栈、设备无关接口、驱动。

在此过程中,有两个重要的地方:

1.选路由

在协议栈中,UDP的协议实现部分,

2.邻居子系统

在协议栈中,IP部分,建立邻居信息

将上面的流程绘制如下:



2.网卡收到包之后,如何交给用户处理

在中断处理程序中会判断是否是接收中断,如果是则从硬件中读取数据,并且放入到skb中去,然后函数netif_rx会把包丢给上层去处理,netif_rx会触发软中断,等Linux内核方便的时候,再处理这个中断,那么谁会处理这个软中断?net_rx_action



下图是具体的流程。



新手一枚,如有错误,多多指教。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐