1、下面将要经常会的遇到的四种不同类型的mbuf,它们依据在成员mh_flags中填写的不同标志M_PKTHDR和M_EXT而不同。
mbuf的固定大小是128字节
1) 第一类mbuf的mh_flags等于0,mbuf只包含数据,在mbuf中有108字节的数据空间,指针mh_data指向这108字节缓存中的某个位置。
2) 第二类mbuf的mh_flags值是M_PKTHDR,它指示这是一个分组首部,描述一个分组数据的第一个mbuf。数据仍然保存在这个mbuf中,但是由于分组首部占用了8字节,只有100字节的数据可存储在这个mbuf中。
3) 当分组数据超过208字节的数据时,如果采用前面提到的1/2类mbuf,需要3个或更多的mbuf,这时我们就要使用一种称之为簇的mbuf,就是我们下面讲到的mbuf。第3类m b u f不包含分组首部(没有设置M_PKTHDR),但包含超过208字节的数据,这时用到一个叫“簇”的外部缓存(设置M_EXT)。在此mbuf中仍然为分组首部结构分配了空间,但没有用。在这个mbuf中,指针mh_data指向这个簇中的某个位置。
4) 第四类mbuf包含一个分组首部,包含超过208字节的数据,同时设置了标志M_PKTHDR和M_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类型的内核结构加1(mbstat)。当执行这句时,宏MBUFLOCK把它作为参数来改变处理器优先级,然后把优先级恢复为原值。这防止在执行语句mbstat.m_mtypes[type]++时被网络设备中断,因为mbuf可能在内核中的各层中被分配。考虑这样一个系统,它用三步来实现一个c中的++运算:(1)把当前值装入到一个寄存器;(2)寄存器加1;(3)把寄存器值存入到存储器。假设计数器值为77并且MGET在插口层执行。假设执行了步骤1和2(寄存器值为78),并且一个设备中断发生。若设备驱动也执行MGET来获得同种类型的mbuf,在存储器中取值(77),加1(78),并存回在存储器。当被中断执行的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。在这种情况下,若存储器不可用,设备驱动程序丢弃这个帧比较好。