进程管理之进程创建和删除(二)

在上篇文章中我们主要分析了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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值