《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)

在BSD的TCP/IP代码中的一个基本概念是存储器缓存,成为mbuf,在整个实现中用于存储各种信息。(在linux中使用

的是skbuff,UNUX和LINUX在TCP/IP实现上有不同。)

下图是BSD TCP/IP代码的大概组织。

《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)_第1张图片

1.输出处理

1.1.包含socket地址结构的mbuf

下图是一个包含socket地址结构的mbuf。

《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)_第2张图片

mbuf的前20个字节是首部,它包含关于这个mbuf的一些信息。mbuf总长是128个字节。

mbuf使用成员m_next和m_nextpkt链接起来。

成员m_data指向mbuf中的数据,成员m_len指示它的长度,成员m_type指示包含在mbuf中的数数据类型。上图是MT_SONAME。


1.2.包含数据的mbuf

《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)_第3张图片

接口层将sendto调用中指定的数据缓存中的数据复制到一个或多个mbuf中。下图显示了150个字节的数据时如何存储在两个

mbuf中的。

《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)_第4张图片

成员m_next把链表中的所有mbuf都链接在一起。

另一个变化是链表中的第一个mbuf的首部的另外两个成员:m_pkthdr.len和m_pkthdr.rcvif。这两个成员只用于链表的第一个

mbuf中。m_flags的值是M_PKTHDR,只是这个mbuf包含一个分组首部。rcvif包含了一个指向接收分组的接收接收接口结构

指针。


1.3.添加IP和UDP

在插口层将目标插口地址结构复制到一个mbuf中,并把数据复制到mbuf链中后,与此接口描述符对应的协议层被调用。即

UDP输出例程被调用,指向mbuf的指针被作为一个参数传递。这个例程要在数据的前面添加一个IP首部和一个UDP首部,

然后将这些mbuf传递给IP输出例程。

《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)_第5张图片

mbuf链表中添加这些数据的方法是分配另外一个mbuf,把它放在链首,并将分组首部从带有100字节数据的mbuf复制到

这个mbuf。


1.4.IP输出

IP输出例程要填写IP首部中剩余的字段,包括IP校验和。确定数据报应发到哪个输出接口,必要时,对IP报文分片,以及调用
接口输出函数。

1.5.以太网输出

以太网输出函数的第一个功能是把32位IP地址转换成相应的48位以太地址。(使用ARP协议)
然后以太网输出例程把一个14字节的以太网首部添加到链表的第一个mbuf中,紧接在IP首部的前面。以太网首部包括6字节
以太网目标地址,6字节以太网源地址和2字节以太网帧类型。之后此mbuf链表被加到此接口的输出队列队尾。
当接口处理它输出队列中的一个mbuf时,它把数据复制到它的传输缓存中,并且开始输出。主要包含:14字节以太网首部,
20字节IP首部,8字节UDP首部以及用户数据。


2.输入处理

输入处理与输出处理不同,因为输入时异步的。它是通过一个接受完成中断驱动以太网设备驱动程序来接收一个输入分组,而

不是通过进程的系统调用。内核处理这个设备中断,并调度设备驱动程序进入运行状态。


2.1以太网输入

以太网设备驱动程序处理这个中断,假定它表示一个正常的接收已完成,数据从设备读到mbuf链表中。如下图所示。

《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)_第6张图片

这个mbuf是一个分组首部(m_flag被设置成M_PKTHDR),它是一个数据记录的第一个mbuf。分组首部的成员len包含

数据的总长度,成员rcvif包含一个指针,它指向接收数据的接口接口的结构。rcvif用于接收分组而不是输出分组。数据就

存放在这个mbuf中。

设备驱动程序把mbuf传给一个通用以太网输入例程,它通过以太网帧中的类型字段来确定哪个协议层来接收此分组,在

这个例子中,类型字段标识一个IP数据报,从而mbuf被加入到IP输入队列,另外会产生一个软中断来执行IP输入例程。


2.2.IP输入

IP输入是异步的,并且通过一个软中断来执行,当接口层在系统的一个接口上收到一个IP数据报时,它就设置这个软中断,

当IP输入例程执行它时,循环处理在它的输入队列中每一个IP数据报,并在整个队列被处理完后返回。

IP输入例程处理每个接收到的IP数据包。它验证IP首部检验和,处理IP选项,然后进行转发或者调用IP首部中标识的协议的

输入例程。


2.3.UDP输入

UDP输入例程验证UDP首部中的各字段,然后确定是否一个进程应该接收此数据报。

UDP输入例程从一个全局变量开始,查看所有UDP协议控制块链表,寻找一个本地端口号与接收的UDP数据报的目标端口号

匹配的协议控制块。这个PCB是由我们调用socket创建的,它的成员inp_socket指向相应插口结构,并允许接收的数据在此

插口排队。

因为这个UDP数据报要传递给我们的进程,发送方的IP地址和UDP端口号被放置到一个mbuf中,这个mbuf和数据被追加到此

插口的接收队列中。下图是被追加到这个插口的接收队列中的这两个mbuf。

《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇)_第7张图片

上图的第二个mbuf,成员m_len和m_pkthdr.len都减少了28个字节,并且指针m_data也减少了28个字节,这有效第将IP的UDP

首部删去。只保留了26字节数据追加到插口接收队列。

在链表的第一个mbuf中包括一个16字节internet插口地址结构,它带有发送方IP地址和UDP端口号。它的类型是MT_SONAME。

这个mbuf是插口层创建的,将这些信息返回给通过调用系统调用recvform或recvmsg的调用进程。即使这个链表的第二个mbuf中

有空间存储这个插口地址结构,它也必须存放到它自己的mbuf,因为它们的类型不同,一个是MT_SONAME,一个是MT_DATA。


2.4.进程输入

我们的进程调用recvfrom时被阻塞,在内核中处以休眠状态,现在进程被唤醒。UDP层追加到插口接收队列中的26字节的数据被

内核从mbuf复制到我们程序的缓存中。

你可能感兴趣的:(《TCP/IP详解卷2:实现》笔记--mbuf与输入输出(UDP篇))