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