2022年网络源码结构 .pdf

上传人:Q****o 文档编号:27942023 上传时间:2022-07-26 格式:PDF 页数:8 大小:76.34KB
返回 下载 相关 举报
2022年网络源码结构 .pdf_第1页
第1页 / 共8页
2022年网络源码结构 .pdf_第2页
第2页 / 共8页
点击查看更多>>
资源描述

《2022年网络源码结构 .pdf》由会员分享,可在线阅读,更多相关《2022年网络源码结构 .pdf(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、Linux 的源码里,网络接口的实现部份是非常值得一读的,通过读源码,不仅对网络协议会有更深的了解,也有助于在网络编程的时候,对应用函数有更精确的了解和把握。本文把重点放在网络接口程序的总体结构上,希望能作为读源码时一些指导性的文字。本文以 Linux2.4.16 内核作为讲解的对象, 内核源码可以在http:/www.kernel.org上下载。我读源码时参考的是http:/lxr.linux.no/ 这个交差参考的网站, 我个人认为是一个很好的工具,如果有条件最好上这个网站。二.网络接口程序的结构Linux 的网络接口分为四部份:网络设备接口部份,网络接口核心部份,网络协议族部份,以及网络

2、接口socket 层。网络设备接口部份主要负责从物理介质接收和发送数据。实现的文件在linu/driver/net目录下面。网络接口核心部份是整个网络接口的关键部位,它为网络协议提供统一的发送接口,屏蔽各种各样的物理介质,同时有负责把来自下层的包向合适的协议配送。它是网络接口的中枢部份。它的主要实现文件在linux/net/core 目录下,其中linux/net/core/dev.c 为主要管理文件。网络协议族部份是各种具体协议实现的部份。Linux 支持 TCP/IP,IPX, X.25,AppleTalk等的协议,各种具体协议实现的源码在linux/net/ 目录下相应的名称。在这里主要

3、讨论TCP/IP(IPv4) 协议,实现的源码在linux/net/ipv4, 其中 linux/net/ipv4/af_inet.c是主要的管理文件。网络接口 Socket 层为用户提供的网络服务的编程接口。主要的源码在linux/net/socket.c 三.网络设备接口部份物理层上有许多不同类型的网络接口设备, 在文件 include/linux/if_arp.h的 28 行里定义了 ARP 能处理的各种的物理设备的标志符。网络设备接口要负责具体物理介质的控制,从物理介质接收以及发送数据,并对物理介质进行诸如最大数据包之类的各种设置。这里我们以比较简单的3Com3c501 太网卡的驱动程

4、序为例,大概讲一下这层的工作原理。源码在Linux/drivers/net/3c501.c 。我们从直觉上来考虑,一个网卡当然最主要的是完成数据的接收和发送,在这里我们来看看接收和发送的过程是怎么样的。发送相对来说比较简单,在Linux/drivers/net/3c501.c的行 475 开始的 el_start_xmit() 这个函数就是实际向3Com3c501 以太网卡发送数据的函数,具体的发送工作不外乎是对一些寄存器的读写,源码的注释很清楚,大家可以看看。接收的工作相对来说比较复杂。通常来说,一个新的包到了,或者一个包发送完成了,都会产生一个中断。Linux/drivers/net/3c

5、501.c的 572 开始 el_interrupt() 的函数里面,前半部份处理的是包发送完以后的汇报,后半部份处理的是一个新的包来的,就是说接收到了新的数 据 。 el_interrupt() 函 数 并 没 有 对 新 的 包 进 行 太 多 的 处 理 , 就 交 给 了 接 收 处 理 函 数el_receive()。el_receive()首先检查接收的包是否正确,如果是一个“好”包就会为包分配一个缓冲结构 (dev_alloc_skb() ,这样驱动程序对包的接收工作就完成了,通过调用上层的函数 netif_rx()(net/core/dev.c1214 行 ) ,把包交给上层。

6、现在驱动程序有了发送和接收数据的功能了,驱动程序怎么样和上层建立联系呢?就是说接收到包以后怎么送给上层,以及上层怎么能调用驱动程序的发送函数呢?由下往上的关系, 是通过驱动程序调用上层的netif_rx()(net/core/dev.c 1214 行)函数实现的,驱动程序通过这个函数把接到的数据交给上层,请注意所有的网卡驱动程序都需要调用这个函数的,这是网络接口核心层和网络接口设备联系的桥梁。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 8 页 - - - - - -

7、- - - 由上往下的关系就复杂点。网络接口核心层需要知道有多少网络设备可以用,每个设备的函数的入口地址等都要知道。网络接口核心层会大声喊,“嘿,有多少设备可以帮我发送数据包?能发送的请给我排成一队!” 。这一队就由dev_base 开始,指针structnet_device *dev_base (Linux/include/linux/netdevice.h 436 行 )就是保存了网络接口核心层所知道的所有设 备 。 对 于 网 络 接 口 核 心 层 来 说 , 所 有 的 设 备 都 是 一 个net_device 结 构 , 它 在include/linux/netdevice.h,

8、line 233里被定义, 这是从网络接口核心层的角度看到的一个抽象的设备,我们来看看网络接口核心层的角度看到的网络设备具有的功能:struct net_device ,open() stop() hard_start_xmit() hard_header() rebuild_header() set_mac_address() do_ioctl() set_config() hard_header_cache() header_cache_update() change_mtu() tx_timeout() hard_header_parse() neigh_setup() accept_f

9、astpath() , 如果网络接口核心层需要由下层发送数据的时候,在dev_base 找到设备以后,就直接调 dev-hard_start_xmit() 的这个函数来让下层发数据包。驱动程序要让网络接口核心层知道自己的存在,当然要加入dev_base所指向的指针链,然后把自己的函数以及各种参数和net_device 里的相应的域对应起来。加入 dev_base所指向的指针链是通过函数register_netdev(&dev_3c50)(linux/drivers/net/net_init.c, line 532) 建立的。而把自己的函数以和net_device 里的相应的域及各种参数关系的建

10、立是在el1_probe1()(Linux/drivers/net/3c501.c)里进行的:el1_probe1() ,dev-open = &el_open; dev-hard_start_xmit = &el_start_xmit; dev-tx_timeout = &el_timeout; dev-watchdog_timeo = HZ; dev-stop = &el1_close; dev-get_stats = &el1_get_stats; dev-set_multicast_list = &set_multicast_list; ,ether_setup(dev); 名师资料总

11、结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 8 页 - - - - - - - - - , 进一步的对应工作在ether_setup(dev) (drivers/net/net_init.c, line 405 )里进行。我们注意到 dev-hard_start_xmit =&el_start_xmit ,这样发送函数的关系就建立了,上层只知道调用dev-hard_start_xmit这个来发送数据,上面的语句就把驱动程序实际的发送函数告诉了上层。四.网络接口核心部分刚才谈论了

12、驱动程序怎么和网络接口核心层衔接的。网络接口核心层知道驱动程序以及驱动程序的函数的入口是通过*dev_base 指向的设备链的,而下层是通过调用这一层的函数netif_rx()(net/core/dev.c1214 行) 把数据传递个这一层的。网络接口核心层的上层是具体的网络协议,下层是驱动程序, 我们以及解决了下层的关系,但和上层的关系没有解决。先来讨论一下网络接口核心层和网络协议族部份的关系,这种关系不外乎也是接收和发送的关系。网络协议,例如IP,ARP 等的协议要发送数据包的时候会把数据包传递给这层,那么这 种 传 递 是 通 过什 么 函数 来 发 生的 呢 ? 网 络 接口 核 心

13、层通 过dev_queue_xmit()(net/core/dev.c,line975) 这个函数向上层提供统一的发送接口,也就是说无论是 IP,还是 ARP 协议,通过这个函数把要发送的数据传递给这一层,想发送数据的时候就调用这个函数就可以了。dev_queue_xmit()做的工作最后会落实到dev-hard_start_xmit() ,而dev-hard_start_xmit() 会调用实际的驱动程序来完成发送的任务。例如上面的例子中,调用dev-hard_start_xmit() 实际就是调用了el_start_xmit() 。现在讨论接收的情况。网络接口核心层通过的函数netif_r

14、x()(net/core/dev.c 1214 行)接收了上层发送来的数据,这时候当然要把数据包往上层派送。所有的协议族的下层协议都需要接收数据,TCP/IP 的 IP 协议和 ARP 协议,SPX/IPX 的 IPX 协议,AppleTalk 的 DDP 和 AARP协议等都需要直接从网络接口核心层接收数据,网络接口核心层接收数据是如何把包发给这些协议的呢?这时的情形和于下层的关系很相似,网络接口核心层的下面可能有许多的网卡的驱动程序,为了知道怎么向这些驱动程序发数据,前面以及讲过时,是通过*dev_base 这个 指 针 指 向 的 链 解 决 的 , 现 在 解 决 和 上 层 的 关

15、系 是 通 过static struct packet_ptype_base16( net/core/dev.c line 164) 这个数组解决的。这个数组包含了需要接收数据包的协议,以及它们的接收函数的入口。从上面可以看到, IP 协议接收数据是通过ip_rcv() 函数的, 而 ARP 协议是通过arp_rcv()的,网络接口核心层只要通过这个数组就可以把数据交给上层函数了。如果有协议想把自己添加到这个数组,是通过dev_add_pack()(net/core/dev.c, line233)函数,从数组删除是通过dev_remove_pack()函数的。 Ip 层的注册是在初始化函数进行

16、的void _init ip_init(void) (net/ipv4/ip_output.c, line 1003) ,dev_add_pack(&ip_packet_type); , 重新到回我们关于接收的讨论,网络接口核心层通过的函数netif_rx()(net/core/dev.c 1214 行)接收了上层发送来的数据,看看这个函数做了些什么。由于现在还是在中断的服务里面,所有并不能够处理太多的东西,剩下的东西就通过cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ) 交给软中断处理,从 open_softirq(NET_RX_SOFTIRQ, net

17、_rx_action, NULL) 可以知道名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 8 页 - - - - - - - - - NET_RX_SOFTIRQ软 中 断 的 处 理 函 数 是net_rx_action()(net/core/dev.c, line 1419) ,net_rx_action() 根据数据包的协议类型在数组ptype_base16里找到相应的协议,并从中知道了接收的处理函数,然后把数据包交给处理函数,这样就交给了上层处理,实际调用处理函

18、数是通过net_rx_action() 里的pt_prev-func() 这一句。例如如果数据包是IP 协议的话,ptype_baseETH_P_IP-func()(ip_rcv(),这样就把数据包交给了IP 协议。五.网络协议部分协议层是真正实现是在这一层。在 linux/include/linux/socket.h里面,Linux 的 BSDSocket定义了多至32 支持的协议族,其中PF_INET 就是我们最熟悉的TCP/IP 协议族 (IPv4, 以下没有特别声明都指IPv4)。以这个协议族为例,看看这层是怎么工作的。实现 TCP/IP 协议族的主要文件在inux/net/ipv4/

19、 目录下面, Linux/net/ipv4/af_inet.c为主要的管理文件。在 Linux2.4.16 里面,实现了TCP/IP 协议族里面的的IGMP,TCP,UDP,ICMP,ARP,IP 。我们先讨论一下这些协议之间的关系。IP 和 ARP 协议是需要直接和网络设备接口打交道的协议 , 也 就 是 需 要 从 网 络 核 心 模 块 (core) 接 收 数 据 和 发 送 数 据 的 。 而 其 它 协 议TCP,UDP,IGMP,ICMP 是需要直接利用IP 协议的,需要从IP 协议接收数据,以及利用IP 协议发送数据,同时还要向上层Socket 层提供直接的调用接口。可以看到I

20、P 层是一个核心的协议,向下需要和下层打交道,又要向上层提供所以的传输和接收的服务。先 来 看 看IP协 议 层 。 网 络 核 心 模 块 (core) 如 果 接 收 到IP 层 的 数 据 , 通 过ptype_baseETH_P_IP 数组的 IP 层的项指向的IP 协议的 ip_packet_type-ip_rcv() 函数把数据包传递给IP 层,也就是说IP 层通过这个函数ip_rcv()(linux/net/ipv4/ip_input.c)接收数据的。ip_rcv() 这个函数只对IP 数据保做了一些checksum 的检查工作,如果包是正确的就把包交给了下一个处理函数ip_rc

21、v_finish()( 注意调用是通过NF_HOOK这个宏实现的)。现在,ip_rcv_finish() 这个函数真正要完成一些IP 层的工作了。 IP 层要做的主要工作就是路由,要决定把数据包往那里送。路由的工作是通过函数ip_route_input()(/linux/net/ipv4/route.c,line 1622)实现的。对于进来的包可能的路由有这些:属于本地的数据(即是需要传递给TCP, UDP,IGMP 这些上层协议的) ;需要要转发的数据包(网关或者NAT 服务器之类的 );不可能路由的数据包(地址信息有误);我 们 现 在 关 心 的 是 如 果 数 据 是 本 地 数 据

22、的 时 候 怎 么 处 理 。 ip_route_input() 调 用ip_route_input_slow()(net/ipv4/route.c, line 1312),在ip_route_input_slow() 里面的1559 行rth-u.dst.input= ip_local_deliver ,这就是判断到IP 包是本地的数据包,并把本地数据包处理函数的地址返 回 。 好 了 , 路 由 工 作 完 成 了 , 返 回 到ip_rcv_finish() 。 ip_rcv_finish() 最 后 调 用 拉skb-dst-input(skb) ,从上面可以看到,这其实就是调用了ip

23、_local_deliver() 函数,而ip_local_deliver(), 接着就调用了ip_local_deliver_finish() 。现在真正到了往上层传递数据包的时候了。现在的情形和网络核心模块层(core) 往上层传递数据包的情形非常相似,怎么从多个协议选择合适的协议, 并且往这个协议传递数据呢?网络网络核心模块层(core) 通过一个数组ptype_base16保存了注册了的所有可以接收数据的协议,同样网络协议层也定义了这样一个数组 struct net_protocol*inet_protosMAX_INET_PROTOS(/linux/net/ipv4/protocol

24、.c#L102),它保存了所有需要从IP 协议层接收数据的上层协议(IGMP ,TCP,UDP,ICMP) 的接收处理函数的地址。我们来看看TCP 协议的数据结构是怎么样的:linux/net/ipv4/protocol.c line67 static struct inet_protocol tcp_protocol = handler: tcp_v4_rcv,/ 接收数据的函数名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 8 页 - - - - - - - - -

25、err_handler: tcp_v4_err,/ 出错处理的函数next: IPPROTO_PREVIOUS, protocol: IPPROTO_TCP, name: TCP ; 第一项就是我们最关心的了,IP 层可以通过这个函数把数据包往TCP 层传的。在linux/net/ipv4/protocol.c的上部,我们可以看到其它协议层的处理函数是igmp_rcv(),udp_rcv(), icmp_rcv() 。同样在 linux/net/ipv4/protocol.c ,往数组 inet_protosMAX_INET_PROTOS 里面添加协议是通过函数inet_add_protoco

26、l() 实现的,删除协议是通过inet_del_protocol() 实现的。inet_protosMAX_INET_PROTOS初始化的过程在linux/net/ipv4/af_inet.c inet_init()初始化函数里面。inet_init() ,printk(KERN_INFO IP Protocols: ); for (p = inet_protocol_base; p != NULL;) struct inet_protocol *tmp = (struct inet_protocol *) p-next; inet_add_protocol(p);/ 添加协议printk(%

27、s%s,p-name,tmp?, :n); p = tmp; , 如果你在Linux 启动的时候有留意启动的信息, 或者在 linux 下打命令dmesg就可以看到这一段程序输出的信息:IP Protocols: ICMP, UDP, TCP, IGMP 也就是说现在数组inet_protos 里面有了ICMP ,UDP,TCP,IGMP 四个协议的inet_protocol 数据结构,数据结构包含了它们接收数据的处理函数。Linux 2.4.16 在 linux/include/linux/socket.h里定义了32 种支持的BSDsocket 协议,常见的有 TCP/IP,IPX/SPX

28、,X.25等,而每种协议还提供不同的服务,例如TCP/IP 协议通过TCP协议支持连接服务,而通过UDP 协议支持无连接服务,面对这么多的协议,向用户提供统一的接口是必要的,这种统一是通过socket 来进行的。在 BSD socket 网络编程的模式下,利用一系列的统一的函数来利用通信的服务。例如一个典型的利用TCP 协议通信程序是这样:sock_descriptor = socket(AF_INET,SOCK_STREAM,0); connect(sock_descriptor, 地址, ) ;send(sock_descriptor,”hello world ”); recv(sock_

29、descriptor,buffer,1024,0); 第一个函数指定了协议Inet 协议,即TCP/IP 协议,同时是利用面向连接的服务,这样就对应到 TCP 协议,以后的操作就是利用socket 的标准函数进行的。从上面我们可以看到两个问题,首先socket 层需要根据用户指定的协议族(上面是AF_INET) 从下面 32 种协议中选择一种协议来完成用户的要求,当协议族确定以后,还要把特定的服务映射到协议族下的具体协议,例如当用户指定的是面向连接的服务时,Inet 协议族会映射到TCP 协议。从多个协议中选择用户指定的协议,并把具体的出理交给选中的协议,这和一起网络核心层向上和向下衔接的问题

30、本质上是一样的,所以解决的方法也是一样的,同样还是通过数名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 8 页 - - - - - - - - - 组。在Linux/net/socket.c定义了这个数组staticstruct net_proto_family*net_familiesNPROTO 。 数组的元素已经确定了,net_families2 是 TCP/IP协议, net_families3 是 X.25 协议,具体那一项对应什么协议,在include/lin

31、ux/socket.h 有定义。但是每一项的数据结构net_proto_family的 ops 是空的,也就是具体协议处理函数的地址是不知道的。协议的处理函数和ops 建立联系是通过sock_register()(Linux/net/socket.c) 这个函数建立的,例如 TCP/IP 协议的是这样建立关系的:int _init inet_init(void) (net/ipv4/af_inet.c) (void) sock_register(&inet_family_ops); 只要给出 AF_INET( 在宏里定义是2),就可以找到net_failies2 里面的处理函数了。协议的映射完

32、成了,现在要进行服务的映射了。上层当然不可能知道下层的什么协议能对应特定的服务,所以这种映射自然由协议族自己完成。在TCP/IP 协议族里,这种映射是通过 struct list_head inetswSOCK_MAX( net/ipv4/af_inet.c) 这 个 数 组 进 行 映 射 的 , 在 谈 论 这 个 数 组 之 前 我 们 来 看 另 外 一 个 数 组inetsw_array(net/ipv4/af_inet.c) static struct inet_protosw inetsw_array = type: SOCK_STREAM, protocol: IPPROTO_

33、TCP, prot: &tcp_prot, ops: &inet_stream_ops, capability: -1, no_check: 0, flags: INET_PROTOSW_PERMANENT, , type: SOCK_DGRAM, protocol: IPPROTO_UDP, prot: &udp_prot, ops: &inet_dgram_ops, capability: -1, no_check: UDP_CSUM_DEFAULT, flags: INET_PROTOSW_PERMANENT, , type: SOCK_RAW, protocol: IPPROTO_IP

34、, /* wild card */ prot: &raw_prot, ops: &inet_dgram_ops, capability: CAP_NET_RAW, no_check: UDP_CSUM_DEFAULT, 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 8 页 - - - - - - - - - flags: INET_PROTOSW_REUSE, ; 我们看到, SOCK_STREAM映射到了TCP 协议, SOCK_DGRAM映射到了UDP 协议,SOC

35、K_RAW映 射 到 了IP协 议 。 现 在 只 要 把inetsw_array里 的 三 项 添 加 到 数 组inetswSOCK_MAX就 可 以 了 , 添 加 是 通 过 函 数inet_register_protosw() 实 现 的 。 在inet_init()(net/ipv4/af_inet.c) 里完成了这些工作。还有一个需要映射的就是socket 其它诸如accept,send(),connect(),release(),bind()等的操作函数是怎么映射的呢?我们来看一下上面的数组的 TCP 的项 type: SOCK_STREAM, protocol: IPPROT

36、O_TCP, prot: &tcp_prot, ops: &inet_stream_ops, capability: -1, no_check: 0, flags: INET_PROTOSW_PERMANENT, , 我们看到这种映射是通过ops,和 prot 来映射的,我们再来看看tcp_prot 这一项:struct proto tcp_prot = name: TCP, close: tcp_close, connect: tcp_v4_connect, disconnect: tcp_disconnect, accept: tcp_accept, ioctl: tcp_ioctl, i

37、nit: tcp_v4_init_sock, destroy: tcp_v4_destroy_sock, shutdown: tcp_shutdown, setsockopt: tcp_setsockopt, getsockopt: tcp_getsockopt, sendmsg: tcp_sendmsg, recvmsg: tcp_recvmsg, backlog_rcv: tcp_v4_do_rcv, hash: tcp_v4_hash, unhash: tcp_unhash, get_port: tcp_v4_get_port, ; 所以的映射都已经完成了,用户调用connect()函数

38、,其实就是调用了tcp_v4_connect()函数,按照这幅图,读起源码来就简单了很多了。六 Socket 层上一节把 socket 层大多数要讨论的东西都谈论了,现在只讲讲socket 层和用户的衔接。系统调用socket(),bind(),connect(),accept,send(),release() 等是在 Linux/net/socket.c 里面的名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 8 页 - - - - - - - - - 实现的 ,系统调用

39、实现的函数是相应的函数名加上sys_的前缀。现在看看当用户调用socket()这个函数,到底下面发生了什么。Socket(AF_INET,SOCK_STREAM,0)调 用 了sys_socket(),sys_socket() 接 着 调 用socket_creat(),socket_creat()就要根据用户提供的协议族参数在net_families 里寻找合适的协议族,如果协议族没有被安装就要请求安装该协议族的模块,然后就调用该协议族的create()函数的处理句柄。根据参数AF_INET ,inet_creat()就被调用了,在inet_creat()根据服务类型在 inetswSOCK_MAX 选择合适的协议,并把协议的操作集赋给socket 就是了,根据SOCK_STREAM , TCP协议被选中,inet_creat() answer=inetsw 用户要求服务服务 ;sock-ops = answer-ops; sk-prot = answer-prot 到此为止,上下都打通了,该是大家都源码的时候了。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 8 页 - - - - - - - - -

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 技术总结

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁