快轉到主要內容
  1. 文章/

OSTEP 第五章:Process API 重點整理

·523 字·3 分鐘

Heptabase 筆記

核心問題:如何建立和管理 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() 的幾個特點

  1. 新的 Process 幾乎跟 Parent 一模一樣。可以通過他們會跑幾乎相同的 code 來知道這點,Child process 會從 if 條件式開始執行程式, rc 值為零。

  2. 當有兩個 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 合作的結果。

  1. word count
  2. take word count result as input to create a new file.

這樣的模式可以組合出無限多的可能性。

如何控制 Process ? #

Process 的控制是透過傳送不同的 signalsProcess

舉例來說我們可以使用 cmd + c (mac) or ctal + c (windows) 去停止 Process;是因為這個組合鍵會發送一個 SIGINTsignalProcess

Process 就會依據不同的 signal 去做相對應的處理。

一台電腦可能有許多的使用者同時使用,所以作業系統也發展出了使用者帳號的概念,防止使用者對其他人的 Process 下達惡意的指令。

Process 相關的 Unix 小工具 #

  • ps: 顯示電腦目前正在運作的 Processes
  • top: 顯示目前使用最多資源的 Processes 是哪些