主机和网络之间的尾端转换
超过一个字节以上的数据结构可以采用两种不同的格式存储在内存中,小尾端 little endian 和大尾端 big endian, 第一种格式把最低字节存储在最低内存地址,而第二种格式恰好相反,像linux这类操作系统所用的格式依赖于所用的处理器,例如Intel处理器遵循的是小尾端模式,而Motorola 处理器使用的是大尾端模式。
假设我们的linux机器从一台远程主机接收到一个IP封包,由于LInux不知道远程主机初始化协议报头试所用的格式是大端还是小端,那么linux将无法读取报头。出于这个原因, 每个协议族都必须定义其所用的尾端,例如tcp/ipc采用大端模式。
但是,这样仍然会给内核开发人员留下一个问题,写的代码必须能在众多支持不同尾端模式处理器上工作,有些处理器上可能与入口封包是同一尾端,但是,如果尾端不同,就必须转换成处理器所用的尾端模式。
因此,每次内核需要读取,保存/或者比较超过一个字节的IP报头字段时,首先就必须将网络尾端转换成主机尾端,或者相反,TCP/IP协议栈的其他协议也是如此,当协议的本地主机都是大尾端时,转换函数就是空操作noop, 因为没必要做任何转换,但是,转换函数还是会始终出现在代码中,为的是让代码可移植性,只有转换函数本身与平台相关,表1-2 列出了用于两个字节的和资格字节的尾端转换的主要的宏定义。
htons 主机 -》网络尾端 short
htonl 主机 -》 网络尾端 long
ntohs 网络 -》主机尾端 short
htonhl 网络 ->主机尾端 long
这些宏定义在头文件 include/linux/byteorder/generic.h 中,以下是每种体系如何根据其尾端裁剪这些宏的定义。
就每种体系而言,每一体系目录 include/asm-include/linux/byteorder/little-endian.h
little-endian .h和big-endian.h都会包含通用的include/linux/byteorder/generic. 文件,表1-2中定义的宏是基于其他由little-endian.h和big_endian.h定义的不同的宏,而这也就是体系的尾端影响表1-2宏定义的方式。
对于表1-2中的每个宏xxxx, 都会有一个姊妹宏__constant_xxx与之对应,当输入字段是常数值时,如美剧列表中的一个元素一节,就会使用这个姊妹宏,输入值为常数。
本节前面已经谈到了,当一个数据字段的范围超过一个字节时,尾端是很重要的,当一个字段所占的字节为一个或者一个以上,而且又定义为位字段集时,尾端实际上更为重要。
捕获bug
有一些函数假定在特定条件下呗调用,或者假定在特定条件下不被调用,内核使用BUG_ON和BUG_TRAP宏捕获引起这类条件不满足的地方,当传给BUG_TRAP的输入条件为假时,内核会打印出警告消息warning message, 对于 BUG_ON则打印出错误的消息,然后内核panic。
数据统计
收集特定条件下发生的统计数据的功能是一种良好的习惯,如缓存查询成功和失败次数,内存分配成功和失败次数等等,对于每种收集统计数据的网络功能,本书会列出每个计数器,并给出描述。
测量时间
内核经常需要测量自某一个时刻起经过了多长时间,例如,一个进行CPU密集运算的任务,通常会在特定时间之后释放CPU,当其被重新调度而执行时,就会继续其工作,即使内核支持内核抢占,在内核代码中,这一点也是特别重要的,网络代码中常见的例子就是实现垃圾收集的函数,在本书中会看到很多类似的函数。
内核空间中时间的流逝采用tick 来计算,一个滴答就是两个连续到期的定时器中断之间的时间长度,定时器会负责各种不同的任务,而且每秒都会定期HZ次。HZ是一个变量,由体系依赖代码进行初始化,例如,在i386机器上,它被初始化位每秒1000次,也就是说,当linux在i386系统上运行时,定时器每秒终端1000次,两次连续中断中间就是一毫秒。
每次定时器到期,就会使一个名为jiffies的全局变量递增, 也就是说在任何时刻,
jiffies代表从系统引导后所经过的滴答次数,而通用值n * HZ代表的就是n秒的时间,
如果一个函数所需要做的工作仅仅是测量经过的时间,就可以吧jiffies的值保存在一个局部变量,稍后再拿jiffies 和当前时间戳做对比,得到一段时间间隔,以得知从测量时刻算起经过了多长时间。
下面的例子给出了一个函数,该函数必须某种工作,但又不想让其持有CPU的时间超过一个滴答,当do_something 吧job_done 设置为非零时,表示工作已经完成了,则此函数就可以返回:
unsigned long start_time = jiffies;
int job_done = 0;
do {
do_somehting(&job_done);
if (job_done)
return ;
} while(jiffies - start_time < 1)
内核代码使用jiffies的几个实例参见 积压处理,process_backlog轮询