该分配进程pid的函数被定义在:kernel/pid.c
数据定义:
typedef struct pidmap {
atomic_t nr_free;//当前空闲的pid的个数
void *page;//用数组代表位图,每一项为一个字长,用位来表示是否该pid被分配.
} pidmap_t;
BITS_PE_PAGE:一个页面中可以表示的数的个数,32位的为2^15(页面大小4KB)
BITS_PE_PAGE_MASK:BITS_PE_PAGE-1
代码解析
int alloc_pidmap(void)
{
int i, offset, max_scan, pid, last = last_pid;
pidmap_t *map;
pid = last + 1;//last为全局变量,表示上次分配pid时分出去的
if (pid >= pid_max)
pid = RESERVED_PIDS;//300,前300个pid是固定的,不可以分配的
offset = pid & BITS_PER_PAGE_MASK;//找出pid在某一个页面中的偏移量
map = &pidmap_array[pid/BITS_PER_PAGE];//&pidmap_array[i]表示的是第i个描述pid使用状况的页面的地址.
//pid/BITS_PER_PAGE代表的是该pid在第几个描述pid的页面
max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset;//获得pid号在内存中表示占用的页面个数,32位的为1
//seem the page number
for (i = 0; i <= max_scan; ++i) {//对每个页面进行扫描
if (unlikely(!map->page)) {//如果map所指的页的物理地址为空
unsigned long page = get_zeroed_page(GFP_KERNEL);//在内存中分配一个物理页面
//GFP_KERNEL wait|io|fs
/*
* Free the page if someone raced with us
* installing it:
*/
spin_lock(&pidmap_lock);
if (map->page)//因为可能是多处理器,或者进程的交叉执行,所以需要再次判断map所指的物理页面是否为空
free_page(page);//为空的话就把刚刚申请的页面释放
else
map->page = (void *)page;
spin_unlock(&pidmap_lock);
if (unlikely(!map->page))//如果页面指针还是为空,分配失败,跳出循环,返回-1
break;// cannot alloc pid
}
if (likely(atomic_read(&map->nr_free))) {//如果该页面的空闲pid的个数不为0的话
do {
if (!test_and_set_bit(offset, map->page)) {//判断该页面中offset偏移量表示的pid是否为空
atomic_dec(&map->nr_free);//为空在空闲的pid个数--
last_pid = pid;//全局变量更新
return pid;
}
offset = find_next_offset(map, offset);//失败则寻找下一个pid
pid = mk_pid(map, offset);//根据map和offset的值获得pid
//new pid
/*
* find_next_offset() found a bit, the pid from it
* is in-bounds, and if we fell back to the last
* bitmap block and the final block was the same
* as the starting point, pid is before last_pid.
*/
} while (offset < BITS_PER_PAGE && pid < pid_max &&//判断offset是否超过一个页面可以表示的最大的pid数或者最大的pid
(i != max_scan || pid < last ||//如果i==max_scan的话,如果pid>=last因为之前已经判断过,就没必要了
!((last+1) & BITS_PER_PAGE_MASK)));
//对于max_scan等于1,也需要对其扫描两编,第一次是last之后的,第二次是last之前的.
}
if (map < &pidmap_array[(pid_max-1)/BITS_PER_PAGE]) {
++map;//如果map没有超过最大的pid所占的页面的地址
offset = 0;
} else {
map = &pidmap_array[0];//对于分配地一个pid的页面,前300是固定的,所以offset需要赋值为非0
offset = RESERVED_PIDS;
if (unlikely(last == offset))
break;
}
pid = mk_pid(map, offset);//重新计算pid的值
}
return -1;
}