在上篇文章中我们主要分析了do_init()创建进程函数,在里面我们有个函数非常重要,我们先来讲述第一个alloc_pidmap()函数,这个函数的功能就是分配新的空闲PID号,一下是它的代码:
int alloc_pidmap(void)
{
int i, offset, max_scan, pid, last = last_pid;
pidmap_t *map;
pid = last + 1;// last = last_pid,这里的last_pid是一个全局变量,它表示上一次分配的pid号。所以本次申请的pid号就是 last+1啦。如果该pid号被占用了,我们接着往后查找空闲pid号,如果超过最大的pid号,我们就从0开始从新查找空闲pid号。
if (pid >= pid_max)//如果发现一上来就超过了最大PID号。
pid = RESERVED_PIDS;//还好,这时系统还有一个最后的方案,系统会保留0x300PID好作为空闲,希望还是可以为这个进程分配空闲PID号。
offset = pid & BITS_PER_PAGE_MASK;//这个是求pid号在一页内存页中的偏移量,其实就是在pidmap_array[]某个位码表中的偏移量。
map = &pidmap_array[pid/BITS_PER_PAGE];//有pid号,我们肯定可以求出这个pid所在哪张位码表里。
max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset;//这个很有意思,如果offset=0,说明分配的pid号就是表中的第一位。对于我们32位系统,我们只有一张表的情况下,我们要求max_scan=0,这样你会发现下面的for循环只是执行了一遍。现在大家是不是有点明白了,如果我们直接发现这次的offset所在位是0的话,我们就直接返回,但是如果发现不是的话,我们就往后一直寻找,直到最后位置。但是如果我们发现0ffset!=0的时候,我们的max_scan=1,这样我们的for循环就要做2遍了,这样大家就有疑问了,我们才一张表,我们为什么要扫描两遍呢?对于这个问题,我会在后面具体讲解的。
for (i = 0; i <= max_scan; ++i) {
if (unlikely(!map->page)) {//如果发现位码表对应的物理内存页的指针为空的话
unsigned long page = get_zeroed_page(GFP_KERNEL);//我们就在低端物理内存中申请一页物理页内存空间。同时还把这个物理页空间清0.返回这个页的指针赋给page.
spin_lock(&pidmap_lock);//获得位码表的锁
if (map->page)//这里再次判断这个位码表对应的物理页是否还是为空。
free_page(page);//不为空,我们刚才上面申请的物理页就多余了,我们这里把它释放掉。
else//如果还是为空的话
map->page = (void *)page;//我们就让这个位码表对应的物理页指向刚才申请到的物理页。
spin_unlock(&pidmap_lock);//解除锁
if (unlikely(!map->page))
break;//如果还是发现位null的话,我们直接跳出for循环,反回-1.
}
if (likely(atomic_read(&map->nr_free))) {//我们这里判断这个位码表中是否还存在空闲的pid号,如果没有的话,直接跳到下面紧接的 if (map < &pidmap_array[(pid_max-1)/BITS_PER_PAGE]) 进行判断,如果发现我们还是有第二张页表的话,我们就通过++map访问下一张表,同时我们从这张表的第一位开始查找。然后再回到上面的for循环,再到这里看看有没有空闲页。我们现在考虑是有的,我们接着往下看。
do {
if (!test_and_set_bit(offset, map->page)) {//这里判断我们刚才last+1对应的位码表中的位是否为1,如果不是的话,我们就直接做这里面的语句,同时还要把这位置1.
atomic_dec(&map->nr_free);//减少空闲pid号的数量。
last_pid = pid;//把pid赋给last_pid。
return pid;//返回我们找到的空闲pid号。
}
offset = find_next_offset(map, offset);//我们会调用find_next_offset()函数,在这个位码表中找寻下一个pid号(这个不一定空闲,我们只是找到它的位置。),对于32位系统,offset=pid
pid = mk_pid(map, offset);//这里直接就是offset=pid,如果是多页的话,(((map) - pidmap_array)*BITS_PER_PAGE + (off))前面的*是有效的。
} while (offset < BITS_PER_PAGE && pid < pid_max &&
(i != max_scan || pid < last ||
!((last+1) & BITS_PER_PAGE_MASK)));//这里我们还是要判断这个pid有没有超过max_pid,同时也要也要判断offset有没有超过1页表的范围,对于最后一个条件是最有说道的。如果i一直不等于max_scan时,我们还是可以一直往下做下去,但是如果到最后的一次循环的话,我们i=max_scan,这样我们第一个条件就失效了,现在第二个或是第三个条件就有效了,其实你可以看出,最后也只是扫描到上次分配的pid号处就不往下扫描了,因为pid后面的我们在前面已经扫描了一遍了。这里,大家就可以明白我上面说为什么当offset!=0时,我们的max_scan要扫描两遍了吧,第一遍就是扫描last后面的,第二遍就是扫描last前面的。对于!((last+1) & BITS_PER_PAGE_MASK))这个条件,32位系统永远都是用不到的,我们发现他一直都是假。
}
if (map < &pidmap_array[(pid_max-1)/BITS_PER_PAGE]) {//这里就是说如果发现系统在上一页中都没有发现有空闲的pid号的话,同时系统里面还是不知一张表的话,我们就寻找下面一张表。
++map;
offset = 0;
} else {//如果发现这是最后一张表了
map = &pidmap_array[0];//我们会返回到最初的那张表。
offset = RESERVED_PIDS;//我们这里会发现系统会有一个小动作,直接给offset附上保留pid号,我们会发现我们不会重0直接扫描,我们直接看保留的pid号是否为空闲。
if (unlikely(last == offset))
break;//如果发现上次就被使用过后,我们就没有必要再继续查了,我们直接跳出for循环,返回-1
}
pid = mk_pid(map, offset);
}
return -1;
}