加入收藏 | 设为首页 | 会员中心 | 我要投稿 财气旺网 - 财气网 (https://www.caiqiwang.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

数据报文的封装与分用封装,内核网络协议栈的分层结构

发布时间:2022-12-12 16:35:28 所属栏目:大数据 来源:转载
导读: 数据报文的封装与分用

封装:当应用程序用 TCP 协议传送数据时,数据首先进入内核网络协议栈中,然后逐一通过 TCP/IP 协议族的每层直到被当作一串比特流送入网络。对于每一层而言,对收到

数据报文的封装与分用

封装:当应用程序用 TCP 协议传送数据时,数据首先进入内核网络协议栈中,然后逐一通过 TCP/IP 协议族的每层直到被当作一串比特流送入网络。对于每一层而言,对收到的数据都会封装相应的协议首部信息(有时还会增加尾部信息)。TCP 协议传给 IP 协议的数据单元称作 TCP 报文段,或简称 TCP 段(TCP segment)。IP 传给数据链路层的数据单元称作 IP 数据报(IP datagram),最后通过以太网传输的比特流称作帧(Frame)。

分用:当目的主机收到一个以太网数据帧时,数据就开始从内核网络协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议都会检查报文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用。

大数据堆栈_堆栈存储器存取数据的方式是( )_大数据技术堆栈

Linux 内核网络协议栈

协议栈的全景图:

协议栈的分层结构:

大数据堆栈_大数据技术堆栈_堆栈存储器存取数据的方式是( )

逻辑抽象层级:

协议栈实现层级:

系统调用接口层(System call interface),实质是一个面向用户空间(User Space)应用程序的接口调用库,向用户空间应用程序提供使用网络服务的接口。

大数据技术堆栈_堆栈存储器存取数据的方式是( )_大数据堆栈

大家如果还想了解更多Linux内核开发相关的更多知识点,请后台私信我【内核】免费领取,里面记录了许多的Linux内核知识点。

内核学习网站:

Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂

协议栈的数据结构:

网络协议栈初始化流程

这需要从内核启动流程说起。当内核完成自解压过程后进入内核启动流程,这一过程先在 arch/mips/kernel/head.S 程序中,这个程序负责数据区(BBS)、中断描述表(IDT)、段描述表(GDT)、页表和寄存器的初始化,程序中定义了内核的入口函数 kernel_entry()、kernel_entry() 函数是体系结构相关的汇编代码,它首先初始化内核堆栈段为创建系统中的第一过程进行准备,接着用一段循环将内核映像的未初始化的数据段清零,最后跳到 start_kernel() 函数中初始化硬件相关的代码,完成 Linux Kernel 环境的建立。

start_kenrel() 定义在 init/main.c 中,真正的内核初始化过程就是从这里才开始。函数 start_kerenl() 将会调用一系列的初始化函数,如:平台初始化,内存初始化,陷阱初始化,中断初始化,进程调度初始化,缓冲区初始化,完成内核本身的各方面设置,目的是最终建立起基本完整的 Linux 内核环境。

start_kernel() 中主要函数及调用关系如下:

start_kernel() 的过程中会执行 socket_init() 来完成协议栈的初始化大数据堆栈,实现如下:

void sock_init(void)//网络栈初始化
{
	int i;
 
	printk("Swansea University Computer Society NET3.019\n");
 
	/*
	 *	Initialize all address (protocol) families. 
	 */
	 
	for (i = 0; i 
	 *	Initialize the protocols module. 
	 */
 
	proto_init();
 
#ifdef CONFIG_NET
	/* 
	 *	Initialize the DEV module. 
	 */
 
	dev_init();
  
	/*
	 *	And the bottom half handler 
	 */
 
	bh_base[NET_BH].routine= net_bh;
	enable_bh(NET_BH);
#endif  
}

sock_init() 包含了内核协议栈的初始化工作:

此函数为协议栈主要的注册函数:

rc = proto_register(&udp_prot, 1);:注册 INET 层 UDP 协议,为其分配快速缓存。

(void)sock_register(&inet_family_ops);:向 static const struct net_proto_family *net_families[NPROTO] 结构体注册 INET 协议族的操作集合(主要是 INET socket 的创建操作)。

inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0;:向 externconst struct net_protocol *inet_protos[MAX_INET_PROTOS] 结构体注册传输层 UDP 的操作集合。

static struct list_head inetsw[SOCK_MAX]; for (r = &inetsw[0]; r < &inetsw[SOCK_MAX];++r) INIT_LIST_HEAD(r);:初始化 SOCKET 类型数组,其中保存了这是个链表数组,每个元素是一个链表,连接使用同种 SOCKET 类型的协议和操作集合。

socket.c 提供的系统调用接口。

大数据技术堆栈_大数据堆栈_堆栈存储器存取数据的方式是( )

堆栈存储器存取数据的方式是( )_大数据技术堆栈_大数据堆栈

协议栈初始化完成后再执行 dev_init(),继续设备的初始化。

Socket 创建流程

协议栈收包流程概述

硬件层与设备无关层:硬件监听物理介质,进行数据的接收,当接收的数据填满了缓冲区,硬件就会产生中断,中断产生后,系统会转向中断服务子程序。在中断服务子程序中,数据会从硬件的缓冲区复制到内核的空间缓冲区,并包装成一个数据结构(sk_buff),然后调用对驱动层的接口函数 netif_rx() 将数据包发送给设备无关层。该函数的实现在 net/inet/dev.c 中,采用了 bootom half 技术,该技术的原理是将中断处理程序人为的分为两部分,上半部分是实时性要求较高的任务,后半部分可以稍后完成,这样就可以节省中断程序的处理时间,整体提高了系统的性能。

NOTE:在整个协议栈实现中 dev.c 文件的作用重大,它衔接了其下的硬件层和其上的网络协议层,可以称它为链路层模块,或者设备无关层的实现。

网络协议层:就以 IP 数据报为例,从设备无关层向网络协议层传递时会调用 ip_rcv()。该函数会根据 IP 首部中使用的传输层协议来调用相应协议的处理函数。UDP 对应 udp_rcv()、TCP 对应 tcp_rcv()、ICMP 对应 icmp_rcv()、IGMP 对应 igmp_rcv()。以 tcp_rcv() 为例,所有使用 TCP 协议的套接字对应的 sock 结构体都被挂入 tcp_prot 全局变量表示的 proto 结构之 sock_array 数组中,采用以本地端口号为索引的插入方式。所以,当 tcp_rcv() 接收到一个数据包,在完成必要的检查和处理后,其将以 TCP 协议首部中目的端口号为索引,在 tcp_prot 对应的 sock 结构体之 sock_array 数组中得到正确的 sock 结构体队列,再辅之以其他条件遍历该队列进行对应 sock 结构体的查询,在得到匹配的 sock 结构体后,将数据包挂入该 sock 结构体中的缓存队列中(由 sock 结构体中的 receive_queue 字段指向),从而完成数据包的最终接收。

NOTE:虽然这里的 ICMP、IGMP 通常被划分为网络层协议,但是实际上他们都封装在 IP 协议里面,作为传输层对待。

协议无关层和系统调用接口层:当用户需要接收数据时,首先根据文件描述符 inode 得到 socket 结构体和 sock 结构体,然后从 sock 结构体中指向的队列 recieve_queue 中读取数据包,将数据包 copy 到用户空间缓冲区。数据就完整的从硬件中传输到用户空间。这样也完成了一次完整的从下到上的传输。

协议栈发包流程概述

1、应用层可以通过系统调用接口层或文件操作来调用内核函数,BSD socket 层的 sock_write() 会调用 INET socket 层的 inet_wirte()。INET socket 层会调用具体传输层协议的 write 函数,该函数是通过调用本层的 inet_send() 来实现的,inet_send() 的 UDP 协议对应的函数为 udp_write()。

2、在传输层 udp_write() 调用本层的 udp_sendto() 完成功能。udp_sendto() 完成 sk_buff 结构体相应的设置和报头的填写后会调用 udp_send() 来发送数据。而在 udp_send() 中,最后会调用 ip_queue_xmit() 将数据包下放的网络层。

3、在网络层,函数 ip_queue_xmit() 的功能是将数据包进行一系列复杂的操作,比如是检查数据包是否需要分片,是否是多播等一系列检查,最后调用 dev_queue_xmit() 发送数据。

4、在链路层中,函数调用会调用具体设备提供的发送函数来发送数据包,e.g. dev->hard_start_xmit(skb, dev);。具体设备的发送函数在协议栈初始化的时候已经设置了。这里以 8390 网卡为例来说明驱动层的工作原理,在 net/drivers/8390.c 中函数 ethdev_init() 的设置如下:

/* Initialize the rest of the 8390 device structure. */  
int ethdev_init(struct device *dev)  
{  
    if (ei_debug > 1)  
        printk(version);  
      
    if (dev->priv == NULL) { //申请私有空间  
        struct ei_device *ei_local; //8390 网卡设备的结构体  
          
        dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL); //申请内核内存空间  
        memset(dev->priv, 0, sizeof(struct ei_device));  
        ei_local = (struct ei_device *)dev->priv;  
#ifndef NO_PINGPONG  
        ei_local->pingpong = 1;  
#endif  
    }  
      
    /* The open call may be overridden by the card-specific code. */  
    if (dev->open == NULL)  
        dev->open = &ei_open; // 设备的打开函数  
    /* We should have a dev->stop entry also. */  
    dev->hard_start_xmit = &ei_start_xmit; // 设备的发送函数,定义在 8390.c 中  
    dev->get_stats   = get_stats;  
#ifdef HAVE_MULTICAST  
    dev->set_multicast_list = &set_multicast_list;  
#endif  
  
    ether_setup(dev);  
          
    return 0;  
}

UDP 的收发包流程总览

内核中断收包流程

UDP 收包流程

UDP 发包流程

(编辑:财气旺网 - 财气网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!