Process Control & IPC#
Booting#
Bootstrapping#
- 開機用 program 在 BIOS 裡 (硬體檢查硬體)
- 並非 Unix kernel 本人
- 把 Unix kernel 的幾隻 process 叫起來 (OS 檢查硬體)
- Unix kernel 將整個 system 叫起來到 single (super) user mode 做一些事情
e.g. check host name, timezone, disk data consistency
- 開啟 multiuser mode
Special process
PID 0: swapper (scheduler)
- kernel process 做一些 prior 的事情,躲在 partition
PID 1: init 所有 process 的共同祖先
- 把系統的 service 叫起來
inetd: 管理 remote login 會 fork surface process 出來像是 telnetdaemon
PID 2: pagedaemon

Process Control Block (PCB)#
- Process 的 metadate
- Process states:
- New
- Running
- Waiting
- Ready
- Terminated

- Program Counter (IP: Instruction Pointer)
- CPU registers
- context switch 回來要 restore
- CPU scheduling information
- Memory-management information
- Accounting information
- I/O status information

Process table#
- 每個 entry 都是 PCB,記錄當前所有 process
- run 一次看看誰的
ppid 是自己就知道自己有多少 child 了
Context switch#

Process Content & Metadata#
- Content: Virtual memory

- text: instruction
- un-initialized data, initialized data: global variable
- stack: local variable
- heap: 只漲不縮
Process Creation & Termination#

fork() 開始,會出現很多狀況: parent 先死, 直接 exec()
- 兩個 process 的關聯性只有
ppid
Virtual Memory 的運作
- 把整個 process copy 是 high-cost 的
- 把 virtual memory map 到 physical memory 上
- 對應在
p_ldt 上的 table
- Virtual Memory 用不完的,所以會有洞,但這樣浪費空間

- Copy-on-write

- 當發現出現 share memory 要更改的時候再 copy
- 第一次開始寫入才真正複製
Normal cases in fork#
- 通常 parent 會等到 child 結束後 call
wait() return 然後繼續運行
- 但也可以 parent 繼續做事 (跟 child 並行) 但 eventually 還是得呼叫
wait()
- 有
CHILD_MAX
Inherited properties
- Effective UID, time, umask … etc
Differences on properties
- PID/PPID
- PCB 裡面的 time 是 process time 不是 program time
- lock
- pending SIGNAL

Deadlock#
- 三個 process 互相 lock 著對方需要的 condition
- Lock 必須互斥,並且系統不可干預
Process Termination#
- Normal termination

- main 內部 call
exit() 直接回到 kernel 直接消失
- 你不 exit(),C start-up routine 會幫你
exit(main(argc, argv));
_exit() 就直接死,不 clean up
- Abnormal termination
int atexit(void (*func)(void));
- 可以註冊一連串 function先註冊再執行
- 在要死前執行(main return 後)
- 反向往回叫 stack
pid_t wait(int *statloc)
- Blocking mode,任何一個 child 死掉就死了
- return 值會是死掉 child 的 PID
statloc 會是死亡結束碼
pid_t waitpid(pid_t pid, int *statloc, int op)
- 可以選擇 non-blocking 直接 return
- 可以指定哪個 child
- wait 到不是自己的 child 會 return error,沒 child 也一樣
Zombie & Orphan#
Zombie#
- Child 死了 但 Parent 還沒呼叫
wait() 就會出現 Zombie process,雖然沒有 virtual memory 但會有 PCB.
- Keep minimal information aka PCB
- 一定要
kill -9
Orphan#
- Process 死的時候,child 還沒有死掉,
init 會把所有沒有 ppid 的 process 轉成自己
init 會定期掃描整個 PCB 執行 wait()
Double fork
- 不想呼叫
wait() 但也不希望有 zombie process

- Grandchild 才是真正我們想要的
pid_t waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
- 可以用
idtype 指定要等
P_PID: wait for a particular process
P_PGID: wait for a group of process
P_ALL: wait for any child process
- 可以選擇 blocking or non-blocking
WNOWAIT 是一個 option 可以把 PCB 留下
pid_t wait3(int *statloc, int op, struct rusage *rusage);,
pid_t wait4(pid_t pid, int *statloc, int op, struct rusage *rusage);
Race Condition#
- 兩個 Process 的執行先後順序不一定是誰,要看 CPU Schedule,是個 Nondeterministic 的過程
- 可能因為順序不同,造成不想要的結果
int execl(const char *pathname, const char *arg0, … /* (char *) 0 */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, … /* (char *) 0, char *const envp[] */);
int execve(const char *pathname, char *const argv[], char *const envp[]);
- 程式執行一定要在一個 process 上,但執行並非從 exec 開始算,而是 fork 開始
spawn() 會同時 fork(), exec()
exec 是把既有的 process 換成另一隻 process,instruction, data (virtual memory) 全部替換,但是不改 open file descriptor table
l, v argument 用 list 還是 vector 表示 e 是 environment
int execlp(const char *filename, const char *arg0, … /* (char *) 0 */);
int execvp(const char *filename, , char *const argv[]);
- p 家族放
filename
- 用
PATH=/bin:/usr/bin:. 的順序找執行檔
- p 會把東西 binary file 當 shell script 執行,先呼叫
/bin/sh 讓他去執行
Close on exec

- 要不要再執行後把 element 關掉
- child 的 open fd table 就是 parent 的
- 預設是 close,只有 open 的時候才會長得不一樣
IPC (Inter-process Communication)#
Pipe#
int pipe( int filedes[2] );
- 一端寫一端讀,不一定多少人

fork() 時如何送資訊給別人
- 都是單向讀寫,一個 process 只會有一個 fd 開著
- 一端寫了另一端還沒讀就 suspend 因為這是 fd blocking mode
- Multiplexing 就
poll 就好了,因為也是 file
- pipe 完畢再 fork 就可以繼承 pipe


$ ls | more
- 就是
$ shell 先呼叫 pipe 然後 fork 出兩個 process
FIFO#
- 為了讓
pipe 不必要用 fork() 形成繼承關係
- 另外創建一個獨立的 file,當作 pipe 的 fd
- 無法 random access
- 把 data 讀掉就沒了,不會像傳統檔案留東西在裡面
- 沒人去 reference 那個 pipe 裡面的 data 會被 remove
int mkfifo(const char *pathname, mode_t mode);
- 跟傳統
open 相同,權限都跟傳統檔案一樣
O_NONBLOCK
- NO: 會讓沒人 write 的時候 suspend,寫端也一樣
- YES: 直接 return
- Write for no-reader FIFO: 會收到
SIGPIPE
PIPE_BUF 指定最多讀多少,避免資料混淆
Back to the content
NTU PJ System Programming
2025 Fall
← Back to the content