最详细的Linux TCP/IP 协议栈源码分析( 四 )

2.4 路由初始化(包括neighbour表、FIB表、和路由缓存表的初始化工作)
2.4.1 rtcache表 ip_rt_init()函数 在net/ipv4/ip_output中调用 , net/ipv4/route.c中定义
2.4.2 FIB初始化 在ip_rt_init()中调用 在net/ipv4/fib_front.c中定义
2.4.3 neigbour表初始化 arp_init()函数中定义
2.5 网络接口设备初始化
在系统中网络接口都是由一个dev_base链表进行管理的 。 通过内核的启动方式也是通过这个链表进行操作的 。 在系统启动之初 , 将所有内核能够支持的网络接口都初始化成这个链表中的一个节点 , 并且每个节点都需要初始化出init函数指针 , 用来检测网络接口设备 。 然后 , 系统遍历整个dev_base链表 , 对每个节点分别调用init函数指针 , 如果成功 , 证明网络接口设备可用 , 那么这个节点就可以进一步初始化 , 如果返回失败 , 那么证明该网络设备不存在或是不可用 , 只能将该节点删除 。 启动结束之后 , 在dev_base中剩下的都是可以用的网络接口设备 。
2.5.1 do_initcalls---->net_dev_init()(net/core/dev.c)------>ethif_probe()(drivers/net/Space.c,在netdevice{}结构的init中调用 , 这边ethif_probe是以太网卡针对的调用)
3.网络设备驱动程序(略)
4.网络连接
4.1 连接的建立和关闭
tcp连接建立的代码如下:
server=gethostbyname(SERVER_NAME);sockfd=socket(AF_INET,SOCK_STREAM,0);address.sin_family=AF_INET;address.sin_port=htons(PORT_NUM);memcpy(connect(sockfd, 连接的初始化与建立期间主要发生的事情如下:
1)sys_socket调用:调用socket_creat(),创建出一个满足传入参数family、type、和protocol的socket,调用sock_map_fd()获取一个未被使用的文件描述符 , 并且申请并初始化对应的file{}结构 。
2)sock_creat():创建socket结构 , 针对每种不同的family的socket结构的初始化 , 就需要调用不同的create函数来完成 。 对应于inet类型的地址来说 , 在网络协议初始化时调用sock_register()函数中完成注册的定义如下:
struct net_proto_family inet_family_ops={PF_INET;inet_create};所以inet协议最后会调用inet_create函数 。
3)inet_create: 初始化sock的状态设置为SS_UNCONNECTED,申请一个新的sock结构 , 并且初始化socket的成员ops初始化为inet_stream_ops,而sock的成员prot初始化为tcp_prot 。 然后调用sock_init_data,将该socket结构的变量sock和sock类型的变量关联起来 。
4)在系统初始化完毕后便是进行connect的工作 , 系统调用connect将一个和socket结构关联的文件描述符和一个sockaddr{}结构的地址对应的远程机器相关联 , 并且调用各个协议自己对应的connect连接函数 。 对应于tcp类型 , 则sock->ops->connect便为inet_stream_connect 。
5)inet_stream_connect: 得到sk,sk=sock->sk,锁定sk , 对自动获取sk的端口号存放在sk->num中 , 并且用htons()函数转换存放在sk->sport中 。 然后调用sk->prot->connect()函数指针 , 对tcp协议来说就是tcp_v4_connect()函数 。 然后将sock->state状态字设置为SS_CONNECTING,等待后面一系列的处理完成之后 , 就将状态改成SS_CONNECTTED 。
6) tcp_v4_connect():调用函数ip_route_connect() , 寻找合适的路由存放在rt中 。 ip_route_connect找两次 , 第一次找到下一跳的ip地址 , 在路由缓存或fib中找到 , 然后第二次找到下一跳的具体邻居 , 到neigh_table中找到 。 然后申请出tcp头的空间存放在buff中 。 将sk中相关地址数据做一些针对路由的变动 , 并且初始化一个tcp连接的序列号 , 调用函数tcp_connect() , 初始化tcp头 , 并设置tcp处理需要的定时器 。 一次connect()建立的过程就结束了 。