VinSong's Blog

Back

NTU-SP 系統程式設計 Ch6 Process Control & IPCBlur image

Process Control & IPC#

Booting#

Bootstrapping#

  • 開機用 program 在 BIOS 裡 (硬體檢查硬體)
  • 並非 Unix kernel 本人
  1. 把 Unix kernel 的幾隻 process 叫起來 (OS 檢查硬體)
  2. Unix kernel 將整個 system 叫起來到 single (super) user mode 做一些事情 e.g. check host name, timezone, disk data consistency
  3. 開啟 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 截圖 2025-10-16 上午9.32.39

Process Control Block (PCB)#

  • Process 的 metadate
  • Process states:
    • New
    • Running
    • Waiting
    • Ready
    • Terminated
  • 截圖 2025-10-16 上午9.40.01
  • Program Counter (IP: Instruction Pointer)
  • CPU registers
    • context switch 回來要 restore
  • CPU scheduling information
    • priority 檢查
  • Memory-management information
  • Accounting information
    • 紀錄任何時間參數
  • I/O status information 截圖 2025-10-16 上午9.44.27

Process table#

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

Context switch#

截圖 2025-10-16 上午9.53.12

Process Content & Metadata#

  • Content: Virtual memory
  • 截圖 2025-10-16 上午9.55.36
  • text: instruction
  • un-initialized data, initialized data: global variable
  • stack: local variable
  • heap: 只漲不縮

Process Creation & Termination#

截圖 2025-10-16 上午10.00.02

  • fork() 開始,會出現很多狀況: parent 先死, 直接 exec()
  • 兩個 process 的關聯性只有 ppid

Virtual Memory 的運作

  • 把整個 process copy 是 high-cost 的
  • 把 virtual memory map 到 physical memory 上
    • 對應在 p_ldt 上的 table
    • Virtual Memory 用不完的,所以會有洞,但這樣浪費空間
    • 截圖 2025-10-16 上午10.13.23
  • Copy-on-write
    • 截圖 2025-10-16 上午10.13.35
    • 當發現出現 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

截圖 2025-10-16 上午10.51.20

Deadlock#

  • 三個 process 互相 lock 著對方需要的 condition
  • Lock 必須互斥,並且系統不可干預

Process Termination#

  • Normal termination
    • 截圖 2025-10-16 上午11.11.33
    • main 內部 call exit() 直接回到 kernel 直接消失
    • 你不 exit(),C start-up routine 會幫你 exit(main(argc, argv));
    • _exit() 就直接死,不 clean up
  • Abnormal termination
    • Signal 傳出

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
    • opWNOHANG 可以製造這種效果
  • 可以指定哪個 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 截圖 2025-10-26 下午4.06.58
  • 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);

  • 檢查 CPU time 跟 Mem usage

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 截圖 2025-10-26 晚上7.53.58

  • 要不要再執行後把 element 關掉
  • child 的 open fd table 就是 parent 的
    • 預設是 close,只有 open 的時候才會長得不一樣

IPC (Inter-process Communication)#

Pipe#

int pipe( int filedes[2] );

  • 一端寫一端讀,不一定多少人
  • 截圖 2025-10-16 上午11.55.07
  • fork() 時如何送資訊給別人
    • 都是單向讀寫,一個 process 只會有一個 fd 開著
    • 一端寫了另一端還沒讀就 suspend 因為這是 fd blocking mode
    • Multiplexing 就 poll 就好了,因為也是 file
    • pipe 完畢再 fork 就可以繼承 pipe
  • 截圖 2025-10-16 上午11.55.50
  • 截圖 2025-10-16 上午11.57.24
  • $ 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


NTU-SP 系統程式設計 Ch6 Process Control & IPC
https://vinsong.csie.org/blog/ntu-sp-ch06-process
Author VinSong
Published at 2025年11月30日