分析pid源代码,改写成线程id以及提出的问题

来源:互联网 发布:云南师范大学网络课程 编辑:程序博客网 时间:2024/05/16 14:35

修改后的代码,每一行都有注释。但是在该方法中,提出一个问题。比如当循环到第32767次时,我们回收进程号32766。

所以分配的进程号为32766,再进入下一次循环此时32767进程号已经被分配,然而last_tid等于32766

此时查找最新可分配的Id号,调用find_next_zero_bit(void *addr, int size, int offset)方法,此时offset = 32767 不可分配,offset+1.

不满足条件,跳出循环并返回32768,该id超出界限,返回-1.

而不会再从头搜索,此时如果存在tid = 3 可用,也不能将其分配出去。

如下图所示:


该问题已经解决,修改下面方法,当有空闲ID时,重复循环查找

static int find_next_zero_bit(void *addr, int size, int offset)
{

unsigned long *p;
unsigned long mask;
//如果小于最大空间
while (offset < size && tidmap.nr_free)
{
//偏移地址
p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));
mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));
//该位是否可分配,可分配,则跳出循环
if ((~(*p) & mask))
{
break;
}
//如果不可分配,则判断下一位
++offset;

//如果超过最大数,相&之后可得余数,这样即可重新查找

offset = offset & BITS_PER_PAGE_MASK;
}


原代码如下所示:

#include<iostream>


using namespace std;


/* max tid, equal to 2^15=32768
 最大线程id号,为了兼容16位机器*/
#define TID_MAX_DEFAULT 0x8000

/* page size = 2^12 = 4K
   PAGE_SHIFT :偏移量
   将1UL向左偏移12位,即为结果 */
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)

/*
每页的位数为2^15,每一位对应一个线程号
*/
#define BITS_PER_BYTE 8
#define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_BYTE)
#define BITS_PER_PAGE_MASK (BITS_PER_PAGE - 1)
/*
定义位图的数据结构
nr_free:空闲的位数
page:字符数组
*/
typedef struct tidmap
{
unsigned int nr_free;
unsigned long page[PAGE_SIZE/sizeof(unsigned long)];
} tidmap_t;
/*
初始化这个数据结构,默认每个字符的值为0,有MAX个空闲位置
*/
static tidmap_t tidmap = { TID_MAX_DEFAULT, {0} };
//初始化last_tid:用来表示最新分配出去的线程号
static int last_tid = -1;
//测试需要分配出去的位是否可用,可用则返回0,不可用返回1
static int test_and_set_bit(int offset, void *addr)
{
unsigned long mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));
unsigned long *p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));
unsigned long old = *p;

*p = old | mask;

return (old & mask) != 0;
}

//清空位数
static void clear_bit(int offset, void *addr)
{
//offset获得的是线程id的偏移量,addr则是首地址
//mask:将1UL向左偏移,偏移的位数是offset%32后的余数
//这里以offset = 67做例子,67%32 = 3 ,mask = 8  即00****00 1000,即第3位的值为1,其他为0
unsigned long mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));
//将offset向右偏移,获得的结果是offset/32的结果,即地址偏移量
//地址偏移量+首地址可求出实际地址
//offset = 67 67/32 = 2,地址偏移量为2 加上首地址,获得第64~95位
unsigned long *p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));
//获得实际地址上的值
unsigned long old = *p;
//将该字上的第3位的数置0,即第67位
*p = old & ~mask;
}

//找到下一个为0的位
//addr:首地址
//size:最大空间
//offset:测试开始位
static int find_next_zero_bit(void *addr, int size, int offset)
{

unsigned long *p;
unsigned long mask;
//如果小于最大空间
while (offset < size)
{
//偏移地址
p = ((unsigned long*)addr) + (offset >> (sizeof(unsigned long) + 1));
mask = 1UL << (offset & (sizeof(unsigned long) * BITS_PER_BYTE - 1));
//该位是否可分配,可分配,则跳出循环
if ((~(*p) & mask))
{
break;
}
//如果不可分配,则判断下一位
++offset;
}

return offset;
}
//分配线程ID
static int alloc_tidmap()
{
//获得最新分配出去的线程号+1,即可能分配出去的下一个线程号
int tid = last_tid + 1;

int offset = tid & BITS_PER_PAGE_MASK;
//如果没有空闲ID,返回-1,分配不成功
if (!tidmap.nr_free)
{
return -1;
}
//找到从last_tid开始下一个为0的位
offset = find_next_zero_bit(&tidmap.page, BITS_PER_PAGE, offset);
//如果该位没有超出界限,并且测试该位可分配
if (BITS_PER_PAGE != offset && !test_and_set_bit(offset, tidmap.page))
{
//减小可用量
--tidmap.nr_free;
//更新最新分配出去的tid号,并返回该tid
last_tid = offset;
return offset;
}

return -1;
}
//释放线程ID
static void free_tidmap(int tid)
{
//首先获得线程id,与BITS_PER_PAGE_MASK相与的目的?是为了防止出界么?这个我也不太了解
//结果与tid一样
int offset = tid & BITS_PER_PAGE_MASK;
//将空闲数+1
tidmap.nr_free++;
//清空位数
clear_bit(offset, &tidmap.page);
}

int main()
{
int i;
for (i = 0; i < TID_MAX_DEFAULT ; ++i)
{
cout<<"nr_free:"<<tidmap.nr_free<<",i="<<i<<", tid ="<< alloc_tidmap()<<endl;
}

free_tidmap(32766);
cout<<"free 32766"<<endl;
cout<<"nr_free"<<tidmap.nr_free<<"i=32766,tid="<<alloc_tidmap()<<endl;
free_tidmap(3);
cout<<"free 3"<<endl;
cout<<"nr_free"<<tidmap.nr_free<<"i=32769,tid="<<alloc_tidmap()<<endl;

return 0;


}

原创粉丝点击