分配pid
static int alloc_pidmap( struct pid_namespace *pid_ns)
{
int i, offset, max_scan, pid, last = pid_ns->last_pid;
struct pidmap *map;
/* last为上次分配的pid */
pid = last + 1;
/* 如果pid比最大值还大,那么就设置成RESERVED_PIDS 300,在Linux系统中会保留一部分进程号用作它用 */
if (pid >= pid_max)
pid = RESERVED_PIDS;
/* 计算出该pid在它所在的map表中的偏移量 */
offset = pid & BITS_PER_PAGE_MASK;
/* 计算出该pid所在的map,pid/BITS_PER_PAGE就是它所在map对应的pidmap偏移量 */
map = &pid_ns->pidmap[pid/BITS_PER_PAGE];
/* 计算最大遍历次数,pid_max是一个全局变量,默认值PID_MAX_DEFAULT,根据系统是否设置CONFIG_BASE_SMALL,可以为0x1000或0x8000, 在x86体系上,(pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE 始终是1, 当pid是在map的第一个位置的时候,即offset=0的时候,只需循环
一次;当pid不是第一个位置,在中间或者结尾,即offset!=0的时候,max_scan=1,就需要循环两次,从last+1的位置开始,一直找到末尾,还>没找到对应的,那就从头开始,找到last处,总之需要走一遍map */
max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset;
for (i = 0; i <= max_scan; ++i) {
if (unlikely(!map->page)) {
/* 如果page为空,则从新分配一个,同时加锁再次判断是否为空,如果为空则直接设置,否则分配的就多余了
如果其他地方也在使用该page的时候,那么有可能在加锁之前,map->page由空变成了不空,
void *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
/*
* Free the page if someone raced with us
* installing it:
*/
spin_lock_irq(&pidmap_lock);
if (map->page)
kfree(page);
else
map->page = page;
spin_unlock_irq(&pidmap_lock);
if (unlikely(!map->page))
break;
}
/* 如果map存在空闲位置,那么就设置该offset对应的比特位*/
if (likely(atomic_read(&map->nr_free))) {
do {
if (!test_and_set_bit(offset, map->page)) {
/* 减少空闲位置个数 */
atomic_dec(&map->nr_free);
pid_ns->last_pid = pid;
/* 分配成功返回对应pid */
return pid;
}
/* 设置不成功,或许该位已经置位,那么继续寻找下一个比特位为0的位置,同时生成一个pid */
offset = find_next_offset(map, offset);
pid = mk_pid(pid_ns, map, offset);
/*
* 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 &&
(i != max_scan || pid < last ||
!((last+1) & BITS_PER_PAGE_MASK)));
}
/* 如果map中已经没有空闲了,且判断还存在其他map的时候,继续在下一个map中分配
在x86体系上,判断始终不成立 */
if (map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) {
++map;
offset = 0;
} else {
/* 否则就从第一个位置从头开始找 */
map = &pid_ns->pidmap[0];
offset = RESERVED_PIDS;
/* 如果这个时候的位置已经是上次分配的了,那么就已经找过了,没必要再找一次了 */
if (unlikely(last == offset))
break;
}
pid = mk_pid(pid_ns, map, offset);
}
return -1;
}