# 进程模块初始化

## 创建idle进程

进程初始化的函数定义在文件`kern/process/proc.c`中的`proc_init`。进程模块的初始化主要分为两步，首先创建第0个内核进程，`idle`。

```c
// kern/process/proc.c
// proc_init - set up the first kernel thread idleproc "idle" by itself and 
//           - create the second kernel thread init_main
void
proc_init(void) {
    int i;

    list_init(&proc_list);//进程链表
    for (i = 0; i < HASH_LIST_SIZE; i ++) {
        list_init(hash_list + i);
    }

    if ((idleproc = alloc_proc()) == NULL) { //分配"第0个"进程 idle
        panic("cannot alloc idleproc.\n");
    }

    idleproc->pid = 0;
    idleproc->state = PROC_RUNNABLE;
    idleproc->kstack = (uintptr_t)bootstack;
    idleproc->need_resched = 1;
    set_proc_name(idleproc, "idle");
    nr_process ++;
    //全局变量current保存当前正在执行的进程
    current = idleproc;

    int pid = kernel_thread(init_main, "Hello world!!", 0);
    if (pid <= 0) {
        panic("create init_main failed.\n");
    }

    initproc = find_proc(pid);
    set_proc_name(initproc, "init");

    assert(idleproc != NULL && idleproc->pid == 0);
    assert(initproc != NULL && initproc->pid == 1);
}
```

在进程模块初始化时，首先需要初始化进程链表。进程链表就是把所有进程控制块串联起来的数据结构，可以记录和追踪每一个进程。然后，调用`proc_alloc`函数来为第一个进程分配其进程控制块。当我们的操作系统开始运行的时候，其实它已经可以被视作一个进程了。但是我们还没有为他设计好进程控制块，也就没法进行管理。`proc_alloc`函数会使用`kmalloc`分配一段空间来保存进程控制块，并且设定一些初值告诉我们这个进程目前还在初始化中。

在分配完空间后，我们对于`idle`进程的控制块进行一定的初始化：

```c
idleproc->pid = 0;
idleproc->state = PROC_RUNNABLE;
idleproc->kstack = (uintptr_t)bootstack;
idleproc->need_resched = 1;
set_proc_name(idleproc, "idle");
nr_process ++;
```

从这里开始，`idle`进程具有了合法的进程编号，`0`。我们把`idle`进程的状态设置为`RUNNABLE`，表示其可以执行。因为这是第一个内核进程，所以我们可以直接将ucore的启动栈分配给他。需要注意的是，后面再分配新进程时我们需要为其分配一个栈，而不能再使用启动栈了。我们再把`idle`进程标志为需要调度，这样一旦`idle`进程开始执行，马上就可以让调度器调度另一个进程进行执行。

## 创建内核进程

接下来我们对于第一个真正的内核进程进行初始化（因为`idle`进程仅仅算是“继承了”ucore的运行）。我们的目标是使用新的内核进程进行一下内核初始化的工作，但在这章我们先仅仅让它输出一个`Hello World`，证明我们的内核进程实现的没有问题。下面是创建内核进程的代码：

```c
int
kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) {
    struct trapframe tf;
    memset(&tf, 0, sizeof(struct trapframe));

    tf.gpr.s0 = (uintptr_t)fn;
    tf.gpr.s1 = (uintptr_t)arg;
    tf.status = (read_csr(sstatus) | SSTATUS_SPP | SSTATUS_SPIE) & ~SSTATUS_SIE;
    tf.epc = (uintptr_t)kernel_thread_entry;
    return do_fork(clone_flags | CLONE_VM, 0, &tf);
}
```

我们将寄存器`s0`和`s1`分别设置为需要进程执行的函数和相关参数列表，之后设置了`status`寄存器使得进程切换后处于中断使能的状态。我们还设置了`epc`使其指向`kernel_thread_entry`，这是进程执行的入口函数。最后，调用`do_fork`函数把当前的进程复制一份。

`do_fork`函数内部主要进行了如下操作：

1. 分配并初始化进程控制块（`alloc_proc`函数）
2. 分配并初始化内核栈（`setup_stack`函数）
3. 根据`clone_flags`决定是复制还是共享内存管理系统（`copy_mm`函数）
4. 设置进程的中断帧和上下文（`copy_thread`函数）
5. 把设置好的进程加入链表
6. 将新建的进程设为就绪态
7. 将返回值设为线程id

如果执行失败，则需要调用相应的错误处理函数释放空间。更多的实现细节可以参考代码，在练习中也会有更多的涉及。

在这里我们需要尤其关注`copy_thread`函数：

```c
static void
copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) {
    proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE - sizeof(struct trapframe));
    *(proc->tf) = *tf;

    // Set a0 to 0 so a child process knows it's just forked
    proc->tf->gpr.a0 = 0;
    proc->tf->gpr.sp = (esp == 0) ? (uintptr_t)proc->tf : esp;

    proc->context.ra = (uintptr_t)forkret;
    proc->context.sp = (uintptr_t)(proc->tf);
}
```

在这里我们首先在上面分配的内核栈上分配出一片空间来保存`trapframe`。然后，我们将`trapframe`中的`a0`寄存器（返回值）设置为0，说明这个进程是一个子进程。之后我们将上下文中的`ra`设置为了`forkret`函数的入口，并且把`trapframe`放在上下文的栈顶。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://nankai.gitbook.io/ucore-os-on-risc-v64/lab4/jin-cheng-mo-kuai-chu-shi-hua.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
