《TCP-IP详解 卷2:实现》学习笔记—mbuf的深入解析

时间:2022-02-10 22:36:57

1、下面将要经常会的遇到的四种不同类型的mbuf,它们依据在成员mh_flags中填写的不同标志M_PKTHDRM_EXT而不同。

mbuf的固定大小是128字节

1  第一类mbufmh_flags等于0mbuf只包含数据,在mbuf中有108字节的数据空间,指针mh_data指向这108字节缓存中的某个位置。

2  第二类mbufmh_flags值是M_PKTHDR,它指示这是一个分组首部,描述一个分组数据的第一个mbuf。数据仍然保存在这个mbuf中,但是由于分组首部占用了8字节,只有100字节的数据可存储在这个mbuf中。

3  当分组数据超过208字节的数据时,如果采用前面提到的1/2mbuf,需要3个或更多的mbuf,这时我们就要使用一种称之为簇的mbuf,就是我们下面讲到的mbuf。第3m b u f不包含分组首部(没有设置M_PKTHDR),但包含超过208字节的数据,这时用到一个叫的外部缓存(设置M_EXT)。在此mbuf中仍然为分组首部结构分配了空间,但没有用。在这个mbuf中,指针mh_data指向这个簇中的某个位置。

4  第四类mbuf包含一个分组首部,包含超过208字节的数据,同时设置了标志M_PKTHDRM_EXT

2、Mbstat是一个全局变量

下面是全局结构mbstat中维护的各种统计

struct mbstat {
	u_long	m_mbufs;	/* mbufs obtained from page pool */ 从页池(未用)中获得mbuf数
	u_long	m_clusters;	/* clusters obtained from page pool */从页池中获得簇
	u_long	m_spare;	/* spare field */剩余空间(未用)
	u_long	m_clfree;	/* free clusters */*簇
	u_long	m_drops;	/* times failed to find space */寻找空间(未用)失败的次数
	u_long	m_wait;		/* times waited for space */等待空间(未用)的次数
	u_long	m_drain;	/* times drained protocols for space */调用协议的drain函数来回收空
                                                               /间的次数
	u_short	m_mtypes[256];	/* type specific mbuf allocations */当前mbuf的分配数:
                                                               /MT_XXX索引
};


 

3、获取一个mbuf

MGET宏。例如调用MGET来分配系统sendto系统调用的目标地址的mbuf如下所示:

MGET(m, M_WAIT, MT_SONAME);
If ( m == NULL)
	Return (ENOBUFS);



MGET函数的原型如下:MBUFLOCK来保护函数和宏不被中断 

#define	MGET(m, how, type) { 
	MALLOC((m), struct mbuf *, MSIZE, mbtypes[type], (how)); 
	if (m) { 
		(m)->m_type = (type); 
		MBUFLOCK(mbstat.m_mtypes[type]++;) 
		(m)->m_next = (struct mbuf *)NULL; 
		(m)->m_nextpkt = (struct mbuf *)NULL; 
		(m)->m_data = (m)->m_dat; 
		(m)->m_flags = 0; 
	} else 
		(m) = m_retry((how), (type)); 
}


MBUFLOCK用于跟踪统计每种mbuf类型的内核结构加1mbstat)。当执行这句时,宏MBUFLOCK把它作为参数来改变处理器优先级,然后把优先级恢复为原值。这防止在执行语句mbstat.m_mtypes[type]++时被网络设备中断,因为mbuf可能在内核中的各层中被分配。考虑这样一个系统,它用三步来实现一个c中的++运算:(1)把当前值装入到一个寄存器;(2)寄存器加1;(3)把寄存器值存入到存储器。假设计数器值为77并且MGET在插口层执行。假设执行了步骤12(寄存器值为78),并且一个设备中断发生。若设备驱动也执行MGET来获得同种类型的mbuf,在存储器中取值(77),加178),并存回在存储器。当被中断执行的MGET的步骤3继续执行时,它将寄存器的值(78)存入存储器。但是计数器应为79,而不是78,这样计数器就被破坏了。

4、分配一个mbuf

struct mbuf *
m_get(nowait, type)
	int nowait, type;
{
	register struct mbuf *m;

	MGET(m, nowait, type);
	return (m);
}


这个调用表明参数nowait的值为M_WAIT或M_DONTWAIT它取决于在存储器不可用时是否需要等待。例如,当插口层请求分配一个mbuf来存储sendto系统调用的目标地址时,它指定M_WAIT,因为在此阻塞是没有问题的。但是当以太网设备驱动程序请求分配一个mbuf来存储一个接收的帧时,它指定M_DONTWAIT,因为它是作为一个设备中断处理来执行的,不能进入睡眠状态来等待一个mbuf。在这种情况下,若存储器不可用,设备驱动程序丢弃这个帧比较好。