跳至文章
ShemoljyyOS-M1:列印行程樹
操作系统

jyyOS-M1:列印行程樹

🔗 https://jyywiki.cn/OS/2024/labs/M1.md

實作上不一定正確,也有可能隨著不斷的學習來修改實作,寫下這個權當對實驗內容的整理和複習,僅供自己參考。

我先嘗試從 1 號行程輸出一個 pstree,不考慮命令列中的 option.

問題可以被分成幾個部分:

  • 如何得到 pid.
  • 如何得到 ppid.
  • 以什麼樣的方式儲存 pid 和 ppid.
  • 如何輸出樹。
  • 以及最後需要考慮的問題,如何解析命令列參數。

如何儲存 pid 和 ppid

選擇把 pid 、ppid 和行程名稱 name 定義在一個結構體中。

c
#define MAX_NAME_LEN 256

typedef struct Process{
	pid_t pid;
	pid_t ppid;
	char name[MAX_NAME_LEN];
}

The GNU C Library 對 pid_t 的官方文件解釋。

The pid_t data type is a signed integer type which is capable
of representing a process ID. In the GNU C Library, this is an int.

將所有行程存放在陣列中,每個元素都是 Process 結構體。並且定義行程數量。

c
#define MAX_PROC 1024
struct Process processes[MAX_PROC];
int process_count = 0;

得到 pid 和 ppid

如實驗指導所言, Everying is a file(說明相關的 C 中的函式和 API 一定是相當重要的),我們可以透過 /proc 資料夾下以數字命名的目錄來取得行程:

c
//traverse /proc directory, get all process information
void get_process_list() {
  // read each entry in the proc directory
  struct dirent *entry;
  DIR *dp = opendir("/proc"); // open /proc dir
  
  if(dp==NULL){
    perror("opendir: /proc");
    exit(1);
  }

  while((entry = readdir(dp)) != NULL){
    if (isdigit(*entry->d_name)){
      //printf("Process ID:%s\n", entry->d_name);
      pid_t pid = atoi(entry->d_name);
      if (pid > 0) {
        pid_t ppid;
        char name[MAX_NAME_LEN];

        // read the status file of the process
        if (read_status_file(pid, &ppid, name) == 0) {
          processes[process_count].pid = pid;
          processes[process_count].ppid = ppid;
          strncpy(processes[process_count].name, name, MAX_NAME_LEN);
          process_count++;
        }
      }
    }
  }
  closedir(dp);
}

struct dirent 的官方解釋。

我自己直接搜 members of dirent structure 還借助了 stack overflow 上的回答。

DIR data type 的官方解釋。

c
int read_status_file(pid_t pid, pid_t *ppid, char *name) {
  char path[64], buffer[256];
  FILE *file;

  sprintf(path, "/proc/%d/status", pid);
  file = fopen(path, "r");
  if (file == NULL) {
    return -1; //file can't be opened, maybe process already exits
  }

  while (fgets(buffer, sizeof(buffer), file)) {
    if (strncmp(buffer, "Name:", 5) == 0) {
      sscanf(buffer, "Name:\t%s", name);
    }
    else if (strncmp(buffer, "PPid:", 5) == 0) {
      sscanf(buffer, "PPid:\t%d", ppid);
    }
  }

  fclose(file);
  return 0;
}

在 read_status_file 函式中,sprintf、fgets、strncmp 以及 sscanf 的用法對我來說都是比較陌生的,但 ChatGPT 告訴我這些函式在讀取和解析檔案內容中都是很常用的。

輸出樹

最容易想到的方法應該就是遞迴了,將目前行程的 pid 與行程陣列中各元素的 ppid ,進行比對來作為是否可以進入遞迴函式的條件。不再贅述。

解析命令列參數

getopt_long 真的很好用。

C parse short option and long option in the command line.

這是官方的定義和示例,我在寫程式的時候還借助了這個示例。

jyy 老師在實驗指導最後提出的問題自己還不能做出解答,繼續深入往下學習吧。