🔗 https://jyywiki.cn/OS/2024/labs/M1.md
The implementation isn't necessarily correct, and I might change it as I keep learning. I'm writing this down as notes and review of the lab — just for myself.
I'll first try to print a pstree starting from process 1, without considering command-line options.
The problem can be split into a few parts:
- How to get the pid.
- How to get the ppid.
- How to store pid and ppid.
- How to print the tree.
- And finally, how to parse command-line arguments.
How to store pid and ppid
I chose to put pid, ppid, and the process name name in a 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 docs on pid_t.
The pid_t data type is a signed integer type which is capableof representing a process ID. In the GNU C Library, this is an int.Store all processes in an array; each element is a Process struct. Also define a process count.
#define MAX_PROC 1024
struct Process processes[MAX_PROC];
int process_count = 0;Getting pid and ppid
As the lab guide says, Everying is a file (which means the related C functions and APIs really matter), we can get processes from the numerically named directories under /proc:
//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 docs on struct dirent.
I searched "members of dirent structure" myself and also used a Stack Overflow answer.
Official docs on the DIR data type.
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;
}In read_status_file, sprintf, fgets, strncmp, and sscanf were all pretty unfamiliar to me, but ChatGPT told me these functions are commonly used when reading and parsing file contents.
Printing the tree
The most obvious approach is recursion: compare the current process's pid with the ppid of entries in the process array, and use that as the condition for entering the recursive function. Won't go into more detail.
Parsing command-line arguments
getopt_long is really nice.
C parse short option and long option in the command line.
Here's the official definition and example. While writing the code I also used this example.
I still can't answer the questions jyy posed at the end of the lab guide. I'll keep going deeper.