本系列是南京大学蒋炎岩老师的操作系统课程学习笔记
课程主页:老师的wiki
课程视频:B站合集
第一个MiniLab是实现一个简易版的pstree,谨记老师的两条教导:
- 计算机的世界没有玄学,一切都建立在确定的机制上
- 不要慌,相信自己
因此,在实验指导书的帮助下,伴随着RTFM和STFW,我也算是勉强完成了这个实验 虽然看起来代码量不大,但花费的时间还是不少的,好在有所收获吧
获取命令行参数
这里主要是用getopt_long进行处理,用long是因为要同时处理长选项和短选项(-p, --show-pids
)
struct option long_options[] = {{"show-pids", no_argument, &pflag, 1},
{"numeric-sort", no_argument, &nflag, 1},
{"version", no_argument, &vflag, 1},
{0, 0, 0, 0}};
while ((c = getopt_long(argc, argv, "npV", long_options, NULL)) != -1) {
switch (c) {
case 'n':
nflag = 1;
break;
case 'p':
pflag = 1;
break;
case 'V':
vflag = 1;
break;
case 0:
break;
case '?':
printf("Unknown option: %s\n", argv[optind - 1]);
return 1;
default:
printf("Unexpected option: %c\n", c);
return 1;
}
}
这里需要注意使用option结构体时的一个坑,手册里写了一句话The last element of the array has to be filled with zeros.
这还是我自测时不小心输入了--show-p
这种部分匹配的选项时触发了段错误才发现的,不过具体为什么会崩溃没有深入的研究
获取全部进程号
Linux下我们可以在/proc目录看到很多以数字命名的目录,每个目录对应一个进程,也是该进程的进程号,因此只需要遍历该目录找到所有数字命名的目录即可。由于对C语言不熟所以在网上找了很多示例代码,最终用opendir和readdir解决
char *base_path = "/proc";
struct dirent *dent;
DIR *srcdir = opendir(base_path);
if (srcdir == NULL) {
perror("Open fail");
return -1;
}
// Proc array.
proc *procs;
procs = malloc(sizeof(proc) * (pid_max + 1));
pid_t *ppids;
ppids = malloc(sizeof(pid_t) * pid_max);
proc *p = procs;
// Read proc directory.
while ((dent = readdir(srcdir)) != NULL) {
if (dent->d_name[0] < '0' || dent->d_name[0] > '9') {
continue;
}
// Save the pids.
p->pid = atoi(dent->d_name);
// Read the stat to get name and ppid.
char path[13 + strlen(dent->d_name) + 1], buf[1024];
memset(path, 0, sizeof(path));
strcat(path, base_path);
strcat(path, "/");
strcat(path, dent->d_name);
strcat(path, "/status");
FILE *f = fopen(path, "r");
while ((fscanf(f, "%s", buf) != EOF)) {
if (strcmp(buf, "Name:") == 0) {
fscanf(f, "%s", p->name);
}
if (strcmp(buf, "PPid:") == 0) {
fscanf(f, "%d", &ppids[p - procs]);
}
}
fclose(f);
++p;
}
获取每个进程的父进程
这个信息其实也在/proc
目录中,每个进程的目录里有一个status
文件,包含了进程的许多信息。里面一项就是PPid
。所以在第二步的时候可以一起读出来
$ cat /proc/$$/status
Name: bash
Umask: 0022
State: S (sleeping)
Tgid: 17248
Ngid: 0
Pid: 17248
PPid: 17200
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 100 100 100 100
FDSize: 256
Groups: 16 33 100
NStgid: 17248
NSpid: 17248
NSpgid: 17248
NSsid: 17200
VmPeak: 131168 kB
VmSize: 131168 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 13484 kB
VmRSS: 13484 kB
RssAnon: 10264 kB
RssFile: 3220 kB
RssShmem: 0 kB
VmData: 10332 kB
VmStk: 136 kB
VmExe: 992 kB
VmLib: 2104 kB
VmPTE: 76 kB
VmPMD: 12 kB
VmSwap: 0 kB
HugetlbPages: 0 kB # 4.4
CoreDumping: 0 # 4.15
Threads: 1
SigQ: 0/3067
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000010000
SigIgn: 0000000000384004
SigCgt: 000000004b813efb
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: ffffffffffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Speculation_Store_Bypass: vulnerable
Cpus_allowed: 00000001
Cpus_allowed_list: 0
Mems_allowed: 1
Mems_allowed_list: 0
voluntary_ctxt_switches: 150
nonvoluntary_ctxt_switches: 545
打印树
按老师的思路,使用递归函数实现,为此需要定义一个结构体,每个进程能找到自己所有的子进程从而递归打印
typedef struct {
char name[1024];
pid_t pid;
pid_t *children;
int child_num;
} proc;
void PrintTree(proc *p, proc *procs, int depth, int pflag) {
if (depth > 0) {
printf("\n%*s |\n", (depth - 1) * 4, "");
printf("%*s", (depth - 1) * 4, "");
printf(" +--");
}
printf("%s", p->name);
if (pflag) {
printf("(%d)", p->pid);
}
for (int i = 0; i < p->child_num; ++i) {
proc *tmp = procs;
while (tmp != NULL) {
if (tmp->pid == p->children[i]) {
break;
}
++tmp;
}
PrintTree(tmp, procs, depth + 1, pflag);
}
}
完整代码放在Github仓库