OSTEP 第五章:Process API 重點整理
目錄
核心問題:如何建立和管理 Process ?
我們有了 Process
這個概念之後,我們可以進一步思考,我們要怎麼建立與管理它們。
Process APIs #
作業系統提供了許多 Process
相關的 system call
,我們現在僅只討論下面幾個。
The fork() system call #
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("hello world (pid:%d)\n", (int) getpid());
int rc = fork();
if (rc < 0) {
// fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) {
// child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
} else {
// parent goes down this path (original process)
printf("hello, I am parent of %d (pid:%d)\n",
rc, (int) getpid());
}
return 0;
}
// Result
prompt> ./p1
hello world (pid:29146)
hello, I am parent of 29147 (pid:29146)
hello, I am child (pid:29147)
prompt>
// OR
prompt> ./p1
hello world (pid:29146)
hello, I am child (pid:29147)
hello, I am parent of 29147 (pid:29146)
prompt>
我們可以注意到 fork() 的幾個特點
新的
Process
幾乎跟Parent
一模一樣。可以通過他們會跑幾乎相同的 code 來知道這點,Child process
會從if
條件式開始執行程式,rc
值為零。當有兩個
Processes
同時運行時,他們的輸出結果是不固定的;所以我們可能得到兩種不同的結果。(這會在後面的Scheduler
章節深度討論原因)
The wait() system call #
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
printf("hello world (pid:%d)\n", (int) getpid());
int rc = fork();
if (rc < 0) {
// fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) {
// child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
sleep(1);
} else {
// parent goes down this path (original process)
int wc = wait(NULL);
printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
rc, wc, (int) getpid());
}
return 0;
}
// Result
prompt> ./p2
hello world (pid:29266)
hello, I am child (pid:29267)
hello, I am parent of 29267 (rc_wait:29267) (pid:29266)
prompt>
如果我們不想將執行的順序交給 Scheduler
來決定的話,我們就會需要有其他方式來達成這個目的,於是我們有了 wait()
。
使用 wait()
可以讓 Parent Process
等待 Child Process
執行結束後再執行。
The exec() system call #
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int rc = fork();
if (rc < 0) {
// fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) {
// child: redirect standard output to a file
close(STDOUT_FILENO);
open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
// now exec "wc"...
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p4.c"); // argument: file to count
myargs[2] = NULL; // marks end of array
execvp(myargs[0], myargs); // runs word count
} else {
// parent goes down this path (original process)
int wc = wait(NULL);
assert(wc >= 0);
}
return 0;
}
// Result
prompt> ./p4
prompt> cat p4.output
32 109 846 p4.c
prompt>
如果 Child Process
只能執行與 Parent Process
相同的 Program
那也就太無聊了。
於是我們有了 exec()
讓 Child Process
有了更多可能。
以上面的例子來說,Child Process
執行了 wc
這個 Program
來計算 p4.c
這個檔案內有多少個字。
為什麼我們需要 fork() 跟 exec() 兩個不同的 system call ? #
我們將 fork()
跟 exec()
分為兩個 system call
一個最大的好處就是:在執行下一個程式前你有機會介入。
最明顯的例子就是我們常使用的 shell
prompt> wc p3.c > newfile.txt
這個例子就是兩個 Processes
合作的結果。
- word count
- take word count result as input to create a new file.
這樣的模式可以組合出無限多的可能性。
如何控制 Process ? #
Process
的控制是透過傳送不同的 signals
給 Process
。
舉例來說我們可以使用 cmd + c (mac) or ctal + c (windows)
去停止 Process
;是因為這個組合鍵會發送一個 SIGINT
的 signal
給 Process
。
Process
就會依據不同的 signal
去做相對應的處理。
一台電腦可能有許多的使用者同時使用,所以作業系統也發展出了使用者帳號的概念,防止使用者對其他人的 Process
下達惡意的指令。
Process 相關的 Unix 小工具 #
ps
: 顯示電腦目前正在運作的 Processestop
: 顯示目前使用最多資源的 Processes 是哪些