Linux的ps简单实现

原理:遍历下的/proc/%s/task/%s/status所有文件,两个%s都为pid号。
注:多线程下,只打印一个pid/task下的所有目录,即可收集各个线程对应的信息。

源码

$ cat ps.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>#define MAX_SIZE    (1024)
#define PATH_SIZE   (128)char *print_char(char *str, int length)
{static char buf[MAX_SIZE] = {0};int i = strlen(str);int j = 0;memset(buf, 0, strlen(buf));for (i = 0; i < length; i++) {/* 跳过' ', ‘\t’,'\n', '\t' */if (str[i] == ' ' || str[i] == '\t'|| str[i] == '\n' || str[i] == '\r') {continue;}buf[j++] = str[i];}buf[j] = 0;return buf;
}void open_file_and_print_info(const char *path, const char *process_name)
{char buf[MAX_SIZE] = {0};FILE *fp = NULL;const char *title = NULL;//printf("path: %s\n", path);/* 打开文件 */fp = fopen(path, "r");if (fp == NULL) {perror("Failed to open path");exit(1);}/* 读取每一行 */while (fgets(buf, MAX_SIZE, fp) != NULL) {title = "Name:";if(strncmp(buf, title, strlen(title)) == 0) {if ((process_name) && (NULL == strstr(buf, process_name))) {break;}printf("%-40.40s",print_char(&buf[strlen(title)], strlen(buf) - strlen(title)));continue;}title = "Tgid:";if (strncmp(buf, title, strlen(title)) == 0) {printf("%-10.10s",print_char(&buf[strlen(title)], strlen(buf) - strlen(title)));continue;}title = "PPid:";if (strncmp(buf, title, strlen(title)) == 0) {printf("%-10.10s",print_char(&buf[strlen(title)], strlen(buf) - strlen(title)));continue;}title = "Pid:";if (strncmp(buf, title, strlen(title)) == 0) {printf("%-10.10s",print_char(&buf[strlen(title)], strlen(buf) - strlen(title)));continue;}title =  "Cpus_allowed_list:";if (strncmp(buf, title, strlen(title)) == 0) {printf("%-10.10s",print_char(&buf[strlen(title)], strlen(buf) - strlen(title)));printf("\n");continue;}}/* 关闭stattus文件 */fclose(fp);
}void handle_dir(const char *dir_name, const char *process_name)
{DIR *dir = NULL;struct dirent *entry = NULL;char file[PATH_SIZE] = {0};/* 打开目录 */if ((dir = opendir(dir_name)) == NULL) {perror("fail to open dir");return;}while ((entry = readdir(dir)) != NULL) {/* 跳过当前目录 */if (entry->d_name[0] == '.') {continue;}/* 跳过系统信息目录,所有进程的目录全都是数字,而系统信息目录全都不是数字 */if ((entry->d_name[0] <='0' ) || (entry->d_name[0] >= '9')) {continue;}/* 使用sprintf完成拼接路径,其中%s会由entry->d_name表示的子线程ID替代 */memset(file, 0, strlen(file));snprintf(file, strlen(file) - 1, "%s/%s/status", dir_name, entry->d_name);open_file_and_print_info(file, process_name);}/* 关闭目录 */closedir(dir);
}int main(int argc, char *argv[])
{DIR *dir = NULL;struct dirent *entry = NULL;char path[PATH_SIZE] = {0};const char *process_name = NULL;if (argc > 1) {if (strstr(argv[1], "-h")  || strstr(argv[1], "--help")) {printf("Usage:\n %s [options]\n\t-h: show help; \n\t--help: show help; \n\t<porcess_name | all (default)>\n", argv[0]);return 0;} else if (!strstr(argv[1], "all")) {process_name = argv[1];}}/* 输出表头 */printf("%-40.40s%-10.10s%-10.10s%-10.10s%-10.10s\n", "NAME", "Tgid", "PID", "PPid", "CPU_LIST");/* 打开/proc目录 */if ((dir = opendir("/proc")) == NULL ) {perror("Failed to open dir");return -1;}while ((entry = readdir(dir)) != NULL) {/* 跳过当前目录,proc目录没有父目录 */if (entry->d_name[0] == '.') {continue;}/* 跳过系统信息目录,所有进程的目录全都是数字,而系统信息目录全都不是数字 */if ((entry->d_name[0] <='0' ) || (entry->d_name[0] >= '9')) {continue;}memset(path, 0, strlen(path));
#if 0/* 使用snprintf完成拼接路径,其中两个%s会由entry->d_name表示的进程ID替代 */snprintf(path, strlen(path) - 1, "/proc/%s/task/%s/status", entry->d_name,entry->d_name);open_file_and_print_info(path);
#else/* 使用snprintf完成拼接路径,其中%s会由entry->d_name表示的进程ID替代 */snprintf(path, strlen(path) - 1, "/proc/%s/task", entry->d_name);handle_dir(path, process_name);
#endif}/* 关闭目录 */closedir(dir);return 0;
}

编译:

$ gcc ps.c -o ps

运行:

#查询指定进程
$ ./ps server
NAME                                    Tgid      PID       PPid      CPU_LIST
server                                  5765      5765      4700      0
server                                  5765      5766      4700      1
server                                  5765      5767      4700      2
server                                  5765      5768      4700      3
server                                  5765      5769      4700      4
sftp-server                             29037     29037     29036     0-5
#查询所有进程
$ ./ps all
NAME                                    Tgid      PID       PPid      CPU_LIST
systemd                                 1         1         0         0-5
kthreadd                                2         2         0         0-5
rcu_gp                                  3         3         2         0-5
rcu_par_gp                              4         4         2         0-5
kworker/0:0H                            6         6         2         0
mm_percpu_wq                            8         8         2         0-5
rcu_sched                               10        10        2         0-5
migration/0                             11        11        2         0
idle_inject/0                           12        12        2         0
...

与系统的ps结果对比:

$ ps -aux | grep server | grep -v grep
test+  5765  0.0  0.0  58764  5116 pts/1    Sl+  12月11   0:00 ./server$ ps -eLF | grep 5765 | grep -v grep
test+  5765  4700  5765  0    5 14691  5116   0 12月11 pts/1  00:00:00 ./server
test+  5765  4700  5766  0    5 14691  5116   1 12月11 pts/1  00:00:00 ./server
test+  5765  4700  5767  0    5 14691  5116   2 12月11 pts/1  00:00:00 ./server
test+  5765  4700  5768  0    5 14691  5116   3 12月11 pts/1  00:00:00 ./server
test+  5765  4700  5769  0    5 14691  5116   4 12月11 pts/1  00:00:00 ./server$ pstree -p 5765
server(5765)─┬─{server}(5766)├─{server}(5767)├─{server}(5768)└─{server}(5769)