Loading...
墨滴

CG

2021/07/23  阅读:37  主题:默认主题

怎么实现fork

函数原型

函数原型:int fork()

调用fork一次,产生两个结果。

当结果是0时,是子进程;当结果是父进程。伪代码如下:

int pid = fork()
if(pid == 0){
   // 子进程
}else{
   // 父进程
}

实现方法

流程

  1. 在存储进程表的数组proc_table中预留N个空闲进程表。
  2. 调用fork后,在proc_table中找到一个空闲进程表,把这个空闲进程表记作free_proc
  3. 把父进程的进程表复制到free_proc,修改free_procldt_sel
    1. ldt_sel是指向进程的LDT表的GDT选择子。
      1. 这是什么意思?
        1. 根据ldt_sel在GDT中找到对应的描述符,根据描述符找到对应的内存空间。这个内存空间就是进程的LDT。
    2. 为什么要修改free_procldt_sel
      1. 父进程的进程表的ldt_sel指向的LDT在父进程的进程表中。
      2. 如果不修改,子进程的进程表的ldt_sel指向的LDT仍然在父进程的进程表中。
      3. 子进程是一个独立的进程,有自己独立的内存空间,自然不能使用父进程的内存空间。
    3. 怎么修改free_procldt_sel
      1. 初始化每个进程时,进程表中的ldt_sel已经设置好了。
      2. 只需在把父进程的进程表中的数据复制到子进程的进程表中之前把子进程的ldt_sel用临时变量保存起来,等复制结束后,再把之前的ldt_sel赋值给free_procldt_sel即可。
    4. 怎么把父进程的进程表复制到子进程的进程表?这是重点,请看下一个小结。
  4. free_proc标记成非空闲状态。
  5. free_procparent_pid设置成父进程的pid。
  6. 这个和父进程的进程表高度相似的free_proc会和进程表数组中的其他一起被进程调度程序调度。

复制进程表

怎么实现复制

伪代码如下:

// 空闲进程表
struct proc * free_proc;
for(遍历进程表数组)
   // 找到第一个空闲进程表
   free_proc = 第一个空闲进程表的内存地址
*free_proc = 父进程的进程表中的数据

伪代码并没有准确地表达好复制进程表的过程。复制进程表,使用的指针。

为什么单独讲解这个过程?因为我读于渊老师的代码时,在这里消耗的时间最多。

创建指针标量表示空闲进程表,struct proc * free_proc;

如果父进程的进程表是struct proc parent_proc,把父进程的进程表复制给子进程的语句是下面这句,

free_proc = &parent_proc;

不是下面这句。

*free_proc = parent_proc;

可以用这个例子来理解。

#include <stdio.h>

struct Person{
        int age;
        char name[20];
};

int main(int argc, char **argv)
{
        struct Person people[20];
        struct Person * jim = people;
        struct Person * jim2 = &people[1];
        struct Person kate = {19"Lily"};
        struct Person kate2 = {29"Lucy"};
        jim = &kate;
        printf("kate name:%s, age:%d\n", kate.name, kate.age);
        jim->age = 190;
        printf("kate name:%s, age:%d\n", kate.name, kate.age);

        *jim2 = kate2;
        printf("kate2 name:%s, age:%d\n", kate2.name, kate2.age);
        jim2->age = 180;
        printf("kate2 name:%s, age:%d\n", kate2.name, kate2.age);

        return 0;
}

执行结果是:

kate name:Lily, age:19
kate name:Lily, age:190
kate2 name:Lucy, age:29
kate2 name:Lucy, age:29

从执行结果,可以看出:

  1. 使用jim = &kate;,修改jim后,kate也会被修改。
  2. 使用*jim2 = kate2;,修改jim后,kate不受影响。

我们把父进程的进程表复制到子进程的进程表后,无论修改二个进程表中的哪一个,都不应该影响另外一个。因此,复制进程表应该使用的语句类似*jim2 = kate2;

原理解析

在上面的代码中,jim = &kate;*jim2 = kate2; 有什么不同?

jim的数据类型是struct Person *。显然,这是一个指针变量。指针变量的值应该是一个内存地址。

jim2的数据类型和jim相同。但是,*jim2表示的却是jim2这个指针变量指向的那个内存地址中存储的数据。

用一个图表示jim*jim2

内存地址 0x0 0x1 0x2 0x3 0x4 0x5
变量 jim jim2 kate kate2
变量值 0x4 某个内存地址,例如0x3 kate2 kate kate2

在图中的场景下,*jim2 = kate2的含义是:

  1. *jim2的值是内存地址为0x3的内存空间。
  2. *jim2 = kate2,在内存地址为0x3的内存空间中存放kate2。

显而易见,修改*jim2,其实就是修改内存地址为0x3的内存空间中的数据,完全不会影响内存地址为0x5的内存空间中的数据。

CG

2021/07/23  阅读:37  主题:默认主题

作者介绍

CG