jyyOS-m1-print-pstree-en
🔗 https://jyywiki.cn/OS/2024/labs/M1.md
The implementation may not be correct and might change as I keep learning; this note is mainly to organize and review the lab for my own reference.
I first try to print a pstree starting from PID 1, without handling command-line options yet.
The problem breaks down into:
- How to obtain
pid. - How to obtain
ppid. - How to store
pidandppid. - How to print the tree.
- Finally, how to parse command-line arguments.
How to store pid and ppid
I define pid, ppid, and the process name name in one struct.
#define MAX_NAME_LEN 256
typedef struct Process{
pid_t pid;
pid_t ppid;
char name[MAX_NAME_LEN];
}
The GNU C Library’s official documentation for pid_t.
The
pid_tdata type is a signed integer type which is capable
of representing a process ID. In the GNU C Library, this is anint.
Store all processes in an array where each element is a Process struct, and define the process count.
#define MAX_PROC 1024
struct Process processes[MAX_PROC];
int process_count = 0;
Obtaining pid and ppid
As the lab guide says, everything is a file (so the relevant C functions and APIs matter a lot) , we can get processes via directories under /proc named with numbers:
//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);
}
Official explanation of struct dirent here.
I also searched “members of dirent structure” and used a Stack Overflow answer.
Official explanation of the DIR data type here.
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;
}
Inside read_status_file, usage of sprintf, fgets, strncmp, and sscanf was unfamiliar to me; ChatGPT said these are very common when reading and parsing file contents.
Printing the tree
The most straightforward approach is recursion: compare the current process’s pid with other entries’ ppid in the array to decide whether to recurse further. I’ll skip the details here.
Parsing command-line arguments
getopt_long is really handy.
C parses short and long options on the command line.
This is the official definition and examples; while coding I also referred to this example.
I still can’t answer the questions jyy raised at the end of the lab guide—I’ll keep digging deeper.