文章へ移動
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 先生が実験ガイドの最後に出した問題にはまだ答えられない。引き続き深く学んでいこう。