一.linux内核网路栈代码的打算知识
1.linux内核ipv4网路部份分层结构:
BSDsocket层:这一部份处理BSDsocket相关操作,每位socket在内核中以structsocket结构彰显。这一部份的文件主要有:
/net/socket.c
/net/protocols.c
INETsocket层:BSDsocket是个可以用于各类网路合同的插口,而当用于tcp/ip,即构建了AF_INET方式的socket时,还须要保留些额外的参数,于是就有了structsock结构。文件主要有:
/net/ipv4/protocol.c
/net/ipv4/af_inet.c
/net/core/sock.c
TCP/UDP层:处理传输层的操作,传输层用structinet_protocol和structproto两个结构表示。文件主要有:
/net/ipv4/udp.c
/net/ipv4/datagram.c
/net/ipv4/tcp.c
/net/ipv4/tcp_input.c
/net/ipv4/tcp_output.c
/net/ipv4/tcp_minisocks.c
/net/ipv4/tcp_output.c
/net/ipv4/tcp_timer.c
IP层:处理网路层的操作,网路层用structpacket_type结构表示。文件主要有:
/net/ipv4/ip_forward.c
/net/ipv4/ip_fragment.c
/net/ipv4/ip_input.c
/net/ipv4/ip_output.c
数据链路层和驱动程序:每位网路设备以structnet_device表示,通用的处理在dev.c中,驱动程序都在/driver/net目录下。
2.Linux中TCP/IP网路层次结构与实现
Linux通过一组相邻的软件层实现了TCP/IP模型,它由BSDSocket层、INETSocket层、传输层、网络层,和链路层构成。应用程序使用系统调用向内核函数传递参数和数据进而步入内核空间,由内核中注册的内核函数对相应的数据结构进行处理。
Linux通过同时对多种通讯合同的支持来提供通用的底层基础服务。它的第一个网路模型的版本是4.3BSD,亦称为Net/1,明天的Linux早已使用Net/4(Linux2.2),其中大多数代码早已完全和BSD的版本不同,而且它仍然支持UINX平台之间程序的移植。Linux是惟一与IPv4和IPv6合同标准完全保持兼容的操作系统,而Linux2.4的IPv4伸缩性又大有提升。
Linux支持的六种不同通讯合同族:
1)TCP/IP(使用TCP/IP的Internet合同族),本文讨论的重点。
2)UNIX域合同(一种进程间通讯的合同)
3)X25合同
4)AX25合同(业余无线X25)
5)IPX合同(NovellIPX)
6)APPLETALK合同(AppleTalkDDP)
Linux的TCP/IP层次结构和实现方法如图所示。
3.数据结构(msghdr,sk_buff,socket,sock,proto_ops,proto)
bsd套接字层,操作的对象是socket,数据储存在msghdr这样的数据结构:
创建socket须要传递family,type,protocol三个参数,创建socket似乎就是创建一个socket实例,之后创建一个文件描述符结构linux内核中网络协议的设计与实现,而且相互完善一些关联,即构建相互联接的表针,而且初始化那些对文件的写读操作映射到socket的read,write函数上来。
同时初始化socket的操作函数(proto_ops结构),倘若传入的type参数是STREAM类型,这么就初始化为SOCKET->ops为inet_stream_ops,倘若是DGRAM类型,则SOCKET-ops为inet_dgram_ops。对于inet_stream_ops虽然是一个结构体,包含了stream类型的socket操作的一些入口函数,在这种函数里主要做的是对socket进行相关的操作,同时通过调用下边提及的sock中的相关操作完成socket到sock层的传递。诸如在inet_stream_ops里有个inet_release的操作,这个操作不仅释放socket的类型空间操作外,还通过调用socket联接的sock的close操作,对于stream类型来说,即tcp_close来关掉sock释放sock。
创建socket同时还创建sock数据空间,初始化sock,初始化过程主要做的事情是初始化三个队列,receive_queue(接收到的数据包sk_buff数组队列),send_queue(须要发送数据包的sk_buff数组队列),backlog_queue(主要用于tcp中三次握手成功的这些数据包,自己猜的),按照family、type参数,初始化sock的操作,例如对于family为inet类型的,type为stream类型的,sock->proto初始化为tcp_prot.其中包括stream类型的合同sock操作对应的入口函数。
在一端对socket进行write的过程中,首先会把要write的字符串缓冲区整理成msghdr的数据结构方式(参见linux内核2.4版源代码剖析大全),之后调用sock_sendmsg把msghdr的数据传送至inet层,对于msghdr结构中数据区中的每位数据包,创建sk_buff结构,填充数据,挂至发送队列。一层层往下层合同传递。一下每层合同不再对数据进行拷贝。而是对sk_buff结构进行操作。
inet套接字及以上层数据储存在sk_buff这样的数据结构里:
路由:
在linux的路由系统主要保存了三种与路由相关的数据,第一种是在数学上和本机相联接的主机地址信息表qq linux,第二种是保存了在网路访问中判定一个网路地址应当走哪些路由的数据表;第三种是最新使用过的查询路由地址的缓存地址数据表。
1.neighbour结构neighbour_table{}是一个包含和本机所联接的所有邻元素的信息的数据结构。该结构中有个元素是neighbour结构的字段,字段的每一个元素都是一个对应于邻机的neighbour结构,系统中因为合同的不同,会有不同的判定邻居的形式,每种都有neighbour_table{}类型的实例,这种实例是通过neighbour_table{}中的表针next串联上去的。在neighbour结构中,包含有与该邻居相连的网路插口设备net_device的表针,网路插口的硬件地址,邻居的硬件地址,包含有neigh_ops{}表针,这种函数表针是直接拿来联接传输数据的,包含有queue_xmit(struct*sk_buff)函数入口地址,这个函数可能会调用硬件驱动程序的发送函数。
2.FIB结构在FIB中保存的是最重要的路由规则,通过对FIB数据的查找和换算,一定就能获得路由一个地址的技巧。系统西路由通常采取的手段是:先到路由缓存中查找表项linux内核中网络协议的设计与实现,假如才能找到,直接对应的一项作为路由的规则;假如不能找到,这么就到FIB中按照规则换算传算下来红旗linux6.0,但是降低一项新的,在路由缓存上将项目添加进去。
3.route结构(即路由缓存中的结构)
数据链路层:
net_device{}结构,对应于每一个网路插口设备。这个结构中包含好多可以直接获取网卡信息的函数和变量,同时包含好多对于网卡操作的函数,这种直接指向该网卡驱动程序的许多函数入口,包括发送接收数据帧到缓冲区等。当那些完成后,例如数据接收到缓冲区后便由netif_rx(在net/core/dev.c各类设备驱动程序的下层框架程序)把它们组成sk_buff方式挂到系统接收的backlog队列之后交由下层网路合同处理。同样,对于下层合同处理出来的这些sk_buff。便由dev_queue_xmit函数装入网路缓冲区,交给网卡驱动程序的发送程序处理。
在系统中存在一张数组dev_base将系统中所有的net_device{}结构连在一起。对应于内核初始化而言,系统启动时便为每位所有可能支持的网路插口设备申请了一个net_device{}空间并串连上去,之后对每位接点运行检查过程,假若测量成功,则在dev_base数组中保留这个接点,否则删掉。对应于模块加载来说,则是调用register_netdev()注册net_device,在这个函数中运行检查过程,若果成功,则加到dev_base数组。否则就返回测量不到信息。调用unregister_netdev注销net_device