浅谈linux内核中的位图
位图的定义
位图法就是bitmap的缩写,所谓bitmap,是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。例如,要判断一千万个人的状态,每个人只有两种状态:男人,女人,可以用0,1表示。那么就可以开一个int数组,一个int有32个位,就可以表示32个人。操作的时候可以使用位操作。
位图也可以用来存储系统在线/离线处理器,来支持CPU热插拔;再比如,位图在Linux内核等初始化过程中存储已分配的中断请求。
位图声明
数据结构和示例
在这个数组里面,可以存储 N * sizeof(int) * 8个数据,但是最大的数只能是N * sizeof(int) * 8 - 1。假如,我们要存储的数据范围为0-15,则我们只需要使得N=1,这样就可以把数据存进去。如下图:
数据为【5,1,7,15,0,4,6,10】,则存入这个结构中的情况为:
位图法在内核中的应用示例
内核既要分配唯一的PID还要对已经分配好的PID进行跟踪。内核是使用了一个大的位图,其中每个PID由一个比特标识。PID的值可通过对应比特在位图中的位置计算而来。因此,分配一个空闲的PID,本质上就等同于寻找位图中第一个值为0的比特,接下来将比特设置为1,反之释放一个PID可通过将对应的比特从1切换为0实现。
这个思想很简单,那么在实现上8个,我们知道可以用一个能表示8种情况的整数来标识。一个字节的空间就有8个比特位。如0x03说明分配了的PID号是0,1号。
一般应用中数值范围是很大的,如果数值是大于8,那该如何呢?首先可以考虑用一个大点的数来表示,如用unsigned long,这是表示四字节,一个unsigned long的整数可以表示的PID号范围是0~31,要是PID号是大于31的又该如何设计呢?
我们可以申请一个连续的地址空间来表示。如:unsigned long bitmap[8]; 这申请了32个字节的连续地址空间,可以表示的范围是0~32*8-1。如果申请的的PID号是在0~31的范围,则只用处于首地址的数来表示即可。同理如果PID号范围是在32~63,则用第二个地址的数来表示。以此类推。那么在具体实现上是怎么样的呢?
PID号与所求的数据的地址存在着数学关系,我们要把这种关系弄清楚。PID号就是对应于偏移号,即从首个比特开始算起,看是偏移了几位,偏移了的数值就是PID号。如第二个比特位,偏移了1位,所以PID号是1。而unsigned long数据类型,是占四个字节空间,即意味着一个地址可以表示32位表比特。所以第一个地址表示的偏移号是0~31,第二个地址表示的偏移号是32~63,第三个地址表示的偏移号是64~127,通过分析我们可以发现这样的数学关系:0~31的任何数除以32都得0,表示是首地址,32~63的任何数除以32结果得1,表示是第二个地址,64~127的任何数除以32都得2,表示是第三个地址的数据。因此我们可以概括出其中的数学关系:偏移号除以32所得结果是相对于首地址的偏移地址量,得到偏移地址量后加上首地址可求出实际的地址,有了地址就可知道该地址上的数了。有了该数就可对它在相应位上置 位。
特定架构的位运算
clear_bit;
set_bit;
两个函数来实现设置位的操作和清除位操作。