Aller au texte
ShemoljyyOS-M1 : afficher l'arbre des processus
操作系统

jyyOS-M1 : afficher l'arbre des processus

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

L'implémentation n'est pas forcément correcte, et je la modifierai peut-être au fil de l'apprentissage. J'écris ça comme un récap et une révision du lab — juste pour moi.

Je commence par essayer d'afficher un pstree à partir du processus 1, sans m'occuper des options en ligne de commande.

Le problème se découpe en quelques parties :

  • Comment obtenir le pid.
  • Comment obtenir le ppid.
  • Comment stocker pid et ppid.
  • Comment afficher l'arbre.
  • Et enfin, comment parser les arguments de la ligne de commande.

Comment stocker pid et ppid

J'ai choisi de mettre pid, ppid et le nom du processus name dans une structure.

c
#define MAX_NAME_LEN 256

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

La doc officielle de The GNU C Library sur 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.

Tous les processus sont rangés dans un tableau, chaque élément est une struct Process. On définit aussi le nombre de processus.

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

Obtenir pid et ppid

Comme le dit le guide du lab, Everying is a file (ce qui veut dire que les fonctions C et les API associées comptent vraiment), on peut récupérer les processus via les répertoires nommés par des chiffres sous /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);
}

Doc officielle de struct dirent.

J'ai cherché moi-même « members of dirent structure » et j'ai aussi utilisé une réponse Stack Overflow.

Doc officielle du 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;
}

Dans read_status_file, sprintf, fgets, strncmp et sscanf m'étaient encore assez étrangers, mais ChatGPT m'a dit que ces fonctions sont très courantes pour lire et parser le contenu d'un fichier.

Afficher l'arbre

La méthode la plus évidente, c'est la récursion : comparer le pid du processus courant avec le ppid des éléments du tableau de processus, et s'en servir comme condition pour entrer dans la fonction récursive. Je ne m'étends pas.

Parser les arguments de la ligne de commande

getopt_long est vraiment pratique.

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

Voici la définition et l'exemple officiels. En écrivant le code, j'ai aussi utilisé cet exemple.

Je n'arrive pas encore à répondre aux questions que jyy pose à la fin du guide. On continue d'approfondir.