南大OS2023-M1:打印进程树

0. 前言

linux系统中,在 /proc文件系统中,每个运行中的进程都有一个相应的目录,其名称是进程的PID。在这个目录下,有一个名为status的文件,其中包含了有关该进程的各种信息,包括进程号。
在这里插入图片描述
status文件中包含的信息(部分截选):
在这里插入图片描述
本人只完成了基础的打印功能,按进程号排序打印没有实现。

1.主函数

主函数通过读取命令行参数,来判断功能。(本人判断的方法比较low,使用switch)

typedef struct {
  int processID;
  char processName[256];
  int parentID;
  int parentIndex;  //父亲在列表中的下标(因为使用数组维护,所以要有下标)
  struct Process* children[100];	//假设每个进程最多有100个孩子
  int childrenCnt;
} Process;


int main(int argc, char *argv[]) {
  Process processList[500];		//创建进程集合,最多500个进程
  for(int i = 0; i < 500; i++) {
    processList[0].parentIndex = -1;
  }
  int opcode = 0;
  int count = 0;
  for (int i = 0; i < argc; i++) {
    assert(argv[i]);
    printf("argv[%d] = %s\n", i, argv[i]);
    if(strncmp(argv[i], "-p", 2) == 0 || strncmp(argv[i], "--show-pids", 11) == 0) {
      opcode = 1; 
      break;
    }
    if(strncmp(argv[i], "-n", 2) == 0 || strncmp(argv[i], "--numeric-sort", 14) == 0) {
      opcode = 2;
      break;
    }
    if(strncmp(argv[i], "-V", 2) == 0 || strncmp(argv[i], "--version", 9) == 0) {
      opcode = 3;
      break;
    }
  }
  assert(!argv[argc]);
  switch(opcode) {
    case 1: getProcessList(processList, &count);
            //display(processList, &count);
            buildProcessTree(processList, &count);
            displayTree(&processList[0], 0, 0);
            break;
    case 2: break;
    case 3: break;
    default:
      printf("Display a tree of processes.\n");
      printf("  -p, --show-pids\tshow PIDs\n");
      printf("  -n, --numeric-sort\tsort output by PID\n");
  }
  printf("There are %d processes\n", count);
  return 0;
}

2.打印进程数

2.1 获取进程数组

获得所有进程,保存至主函数创建的数组中。
具体为读取status文件的内容,识别并保存进程基本信息。

void getProcessList(Process* processList, int* count) {
  assert(count);
  //*count = 0;
  int cnt = 0;
  assert(processList);
  DIR* procDir = opendir("/proc");  
  assert(procDir);

  struct dirent* entry;
  while((entry = readdir(procDir)) != NULL) {
    if(entry->d_type == DT_DIR && isNumeric(entry->d_name)) {
      char statusPath[512];
      char line[256];
    
      FILE* statusFile;
      snprintf(statusPath, sizeof(statusPath), "/proc/%s/status", entry->d_name);
      statusFile = fopen(statusPath, "r");

      //逐行读取文件内容
      while(fgets(line, sizeof(line), statusFile) != NULL) {
    
        if (strncmp(line, "Name:", 5) == 0) {
          sscanf(line, "Name:\t%s", processList[cnt].processName);
        }
        if(strncmp(line, "Pid:", 4) == 0) {
          sscanf(line, "Pid:\t%d", &processList[cnt].processID);
        }
        if(strncmp(line, "PPid:", 5) == 0) {
          sscanf(line, "PPid:\t%d", &processList[cnt].parentID);
        }       
      }
      cnt++;
      fclose(statusFile);
    }  
    
  }
  *count = cnt;
  closedir(procDir);
}

2.2 创建进程树

遍历数组,获得每个进程的父进程在数组中的下标(因为数组中的内容要通过下标访问),这里因为遍历出来的进程其父进程的ID号是递增的,所以直接使用二分法查找。

int getParentIndex(Process* processList, int i) {
  //查找父亲在列表中的下标,查找范围是0 ~ i-1之间, 二分法查找
  int a = 0, b = i - 1;
  //查找是左闭右开, 所以对于右端点需要单独验证
  while (b >= a + 1) {
    if(processList[b].processID == processList[i].parentID)
      return b;
    int k = (b - a) / 2 + a;
    if(processList[k].processID == processList[i].parentID)
      return k;
    else if(processList[k].processID > processList[i].parentID)
      b = k;
    else if(processList[k].processID < processList[i].processID)
      a = k;
  }
  return -1;
}


void buildProcessTree(Process* processList, int* count) {
  // ProcessNode* root = creatProcessNode(processList[0].processName, processList[0].processID);
  int max = 0;
  for(int i = 0; i < *count; i++) {
    int index = getParentIndex(processList, i);
    if(index == -1 || index == 1) continue;
    //Process proc = processList[index];
    processList[index].children[processList[index].childrenCnt++] = (struct Process*)&processList[i];
    //processList[index].childrenCnt++;
    if(processList[index].childrenCnt > max)
      max = processList[index].childrenCnt;
    assert(processList[index].childrenCnt <= 100);	//孩子树的数量不能超过100
  }
}

2.3 排版显示

这里本人调试了很久,还是没弄出来555555,排版太难了

void displayTree(Process* proc, int tabCnt, bool isFirst) {
  if(isFirst)
    printf("|");
  else {
      for(int i = 0; i < tabCnt; i++) {
      printf("\t");
    }
  }
  printf("%s(%d)(%d)", proc->processName, proc->processID, proc->parentID);
  if(proc->childrenCnt == 0) {
    printf("\n");
    return;
  }
  for(int i = 0; i < proc->childrenCnt; i++) {
    if(i == 0) 
      isFirst = 1;
    else
      isFirst = 0;
    displayTree((Process*)proc->children[i], tabCnt + 2, isFirst);
  }
}

在这里插入图片描述

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是在Linux打印进程的完整代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <dirent.h> #include <sys/types.h> #include <sys/stat.h> #define MAX_PID_LEN 10 #define MAX_PATH_LEN 100 void print_tree(char *pid, int level); int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s <pid>\n", argv[0]); return -1; } print_tree(argv[1], 0); return 0; } void print_tree(char *pid, int level) { DIR *dir; struct dirent *entry; char path[MAX_PATH_LEN]; char new_pid[MAX_PID_LEN]; struct stat statbuf; int i; if (level == 0) { printf("%s\n", pid); } sprintf(path, "/proc/%s/task", pid); if ((dir = opendir(path)) == NULL) { return; } while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } sprintf(path, "/proc/%s/task/%s", pid, entry->d_name); if (lstat(path, &statbuf) < 0) { continue; } if (S_ISDIR(statbuf.st_mode)) { for (i = 0; i < level + 1; i++) { printf("| "); } printf("|-- "); printf("%s\n", entry->d_name); strncpy(new_pid, entry->d_name, MAX_PID_LEN); print_tree(new_pid, level + 1); } } closedir(dir); } ``` 该程序的主要思路是通过读取 `/proc` 目录下的进程信息来构建进程。首先,程序接受一个进程 PID 作为命令行参数,并将其作为根节点打印出来。然后,程序遍历 `/proc/<pid>/task` 目录下的所有子目录,如果子目录是以数字命名的目录,就将其作为一个子节点打印出来,并递归打印其子节点。在打印子节点时,程序会在前面加上一些空格,以形成状结构。 需要注意的是,程序中使用了 `lstat()` 函数来获取文件的元数据。这是因为 `/proc` 目录下的很多文件都是符号链接,如果直接使用 `stat()` 函数获取文件信息,会返回符号链接本身的信息,而不是链接指向的文件的信息。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值