Process 程序管理
## 一、什麼是 Process(程序)?
**程序(Process)** 是「正在執行中的程式」。
程式本身通常只是儲存在磁碟上的執行檔;當作業系統將它載入記憶體並開始執行後,就成為程序。
一個程序通常包含:
- **程式碼區段(text/code)**:要執行的指令
- **資料區段(data)**:全域變數、靜態變數
- **Heap**:動態配置的記憶體
- **Stack**:函式呼叫、區域變數
- **CPU 狀態**:程式計數器、暫存器等
- **程序識別碼 PID**
- **開啟的檔案描述元(file descriptors)**
- **程序狀態**:執行中、等待中、停止或結束等
作業系統會透過程序管理機制,讓多個程序共享 CPU、記憶體及其他資源。
---
## 二、C 如何控制程序?
在 Unix/Linux 系統中,C 程式通常透過 **System Call(系統呼叫)** 請求核心執行特權操作。實際上,C 程式多半呼叫的是 C 函式庫提供的包裝函式,例如 `fork()`、`exec()`、`waitpid()` 等。
常見的程序相關 System Call 包括:
| 函式 | 功能 |
|---|---|
| `fork()` | 複製目前程序,建立子程序 |
| `exec()` 系列 | 將目前程序替換成另一個程式 |
| `wait()` / `waitpid()` | 等待子程序結束並取得結果 |
| `exit()` / `_exit()` | 結束程序 |
| `getpid()` | 取得目前程序的 PID |
| `getppid()` | 取得父程序的 PID |
| `kill()` | 傳送訊號給其他程序 |
| `sleep()` | 暫停程序一段時間 |
### `fork()` 的特性
呼叫 `fork()` 後,會產生一個子程序:
- 在父程序中,回傳值是子程序的 PID
- 在子程序中,回傳值是 `0`
- 發生錯誤時,回傳 `-1`
父子程序從 `fork()` 返回後,會繼續執行後面的程式碼。
### `exec()` 的特性
`exec()` 不會建立新程序,而是將**目前程序的程式內容替換成另一個程式**。
因此常見的做法是:
1. 父程序呼叫 `fork()`
2. 子程序呼叫 `exec()`
3. 父程序呼叫 `waitpid()` 等待子程序
---
## 三、簡單範例:建立子程序執行 `ls`
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
}
if (pid == 0) {
// 子程序
printf("Child: PID = %d\n", getpid());
// 將子程序替換成 ls -l
execlp("ls", "ls", "-l", (char *)NULL);
// 只有 exec 失敗時才會執行到這裡
perror("execlp failed");
_exit(1);
} else {
// 父程序
int status;
printf("Parent: child PID = %d\n", pid);
// 等待子程序結束
if (waitpid(pid, &status, 0) == -1) {
perror("waitpid failed");
return 1;
}
if (WIFEXITED(status)) {
printf("Child exited with status %d\n",
WEXITSTATUS(status));
}
}
return 0;
}
```
編譯與執行:
```bash
gcc process_demo.c -o process_demo
./process_demo
```
### 執行流程
1. 原本的程序呼叫 `fork()`。
2. 作業系統建立一個子程序。
3. 子程序呼叫 `execlp()`,執行 `ls -l`。
4. 父程序呼叫 `waitpid()`,等待子程序結束。
5. 子程序結束後,父程序取得其結束狀態。
需要注意的是,父程序與子程序的執行順序可能由作業系統排程決定,因此輸出的先後順序不一定固定。
相關學習地圖、教學課程
C 語言,系統程式