Linux的ps简单实现

2023-12-15 13:13:13

原理:遍历下的/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)

文章来源:https://blog.csdn.net/daa20/article/details/134937804
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。