# Linux内核的进程调度函数

## fork系统调用

![Linux-kernel-process-scheduling-function\_fork](https://3359249029-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M-fD12ZXZGV4t_VVWP0%2Fsync%2F31922561fcbfef785bb2416d4f3690fe6bda054a.png?generation=1591694845650759\&alt=media)

Fork及其变种在类Unix系统中通常是**创建新进程**的唯一方式。\
如果进程需要启动另一个程序的可执行文件，它需要先Fork来创建一个自身的**副本**。在fork之后，两个进程还运行着**相同的程序**，都像是调用了该系统调用一般恢复执行。然后它们可以**检查调用的返回值确定其状态**：是父进程还是子进程，以及据此行事。

fork的函数原型如下：

```c
#include <sys/types.h>        // 提供类型 pid_t 的定义，之后示例中对其的引用也是同样的作用
#include <unistd.h>

pid_t fork(void);
```

> **返回值**：成功调用fork时，父进程中的`fork`返回子进程的`pid`，子进程中则返回`0`，如果创建子进程失败返回`-1`。

## wait系统调用

wait调用的一个意义在于获取子进程状态变更的相关信息，对于已经`terminated`的进程，调用`wait`可以使系统能够释放和进程相关的资源，否则进程会一直处于`zombie`状态。

### `wait()`

`wait()`的原型如下：

```c
#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
```

> **返回值**：调用成功返回进程ID，失败返回`-1`。

在调用`wait`时，会暂停目前进程的执行，并开始分析当前进程中是否有子进程已经退出（进入僵死状态），如果有则会收集子进程的信息并将其销毁后返回，否则一直等待。\
`status`参数是为了保存退出进程的状态，是指向`int`类型的指针。如果并不需要这一状态，可以传递NULL作为参数。

`tait(&state);`调用等效于`waitpid(-1, &status, 0);`。

### `waitpid()`

`waitpid`在原本的`wait`基础上新增了两个参数，其原型如下：

```c
#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid,int *status,int options);
```

> **参数**

| 参数       | 使用                                                                                                                                                                                                                                                             |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pid`    | <p>等待的子进程的id。<br><code>pid &#x3C; -1</code>时等待绝对值相等的进程组中的所有进程。<br><code>pid = -1</code>等待任何子进程，相当于<code>wait()</code>，事实上<code>wait</code>就是这么实现的。<br><code>pid = 0</code>时，等待同一进程组中的所有子进程（进程组ID相同）。<br><code>pid > 0</code>等待任何子进程id为<code>pid</code>的进程。</p> |
| `option` | <p>以一些额外的选项来控制<code>waitpid</code>，不使用则设为<code>0</code>。<br><code>WNOHANG</code>如果没有任何已经结束的进程，则马上返回。<br><code>WUNTRACED</code>如果子进程进入暂停执行的状态，则马上返回。</p>                                                                                                        |
| `status` | 接收状态信息，储存在一个`int`类型的值中，不需要则设为`NULL`。                                                                                                                                                                                                                           |

> **返回值**

| 状态                      | 返回值                       |
| ----------------------- | ------------------------- |
| 正常返回                    | `waitpid()`收集到的子进程的`pid`。 |
| 设为`WNOHANG`并发现没有已退出的子进程 | 返回`0`。                    |
| 调用出错                    | 返回负值。                     |

### `wait3()`和`wait4()`

函数原型如下：

```c
#include <sys/tpyes.h>
#include <sys/wait.h>

pid_t wait3(int *status,int options,struct rusage *rusage); 
pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);
```

`wait3()`和`wait4()`除了可以活得子进程的状态信息外，还可以获得子进程的资源使用信息，通过参数`rusage`得到。其中`wait3`等待所有进程，`wait4`等待指定`pid`的进程。\
据Linux man page，两个函数非标准因而不推荐使用。

## 信号量

C语言的信号量实现包含在`semaphore.h`中。\
信号量使用类型为`sem_t`的变量，在使用之前需要对信号量进行初始化，并设置初始值。

**初始化**：使用`sem_init();`进行初始化，函数的定义如下：

```c
int sem_init(sem_t *sem, int pshared, unsigned int value);
```

> 参数：\
> `sem`：指定要初始化的信号量。\
> `pshared`：`0`表示应用于多线程，`非0`标识用于多线程。\
> `value`：指定信号量的初始值。

**wait等待**：使用`sem_wait()`时，信号量减一，当信号量为0是，函数阻塞。

```c
int sem_wait(sem_t *sem);
```

> 参数即传入一个信号量`sem`。

**trywait等待**：使用`sem_trywait()`相比于`sem_wait()`在信号量为0的时候不会阻塞。

```c
int sem_trywait(sem_t *sem);
```

> 如果无法完成自减操作，返回错误（被设置为`EAGAIN`）。

**post增加**：使用`sem_post()`时，信号量数值增加。

```c
int sem_post(sem_t *sem);
```
