Я также хочу вывести PID нижних потомков дерева, который мой код делает неправильно.
Пусть ваши процессы выводят дерево на языке Dot и используютGraphviz для вывода дерева.
Например, если вы сохраните следующее как, скажем, tree.c :
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int process(const unsigned int level, const unsigned int maxlevel, FILE *dot)
{
int status = EXIT_SUCCESS, childstatus;
unsigned int children, i;
pid_t p, child[2];
if (dot) {
/* Output a node for this child, */
fprintf(dot, " \"%ld\" [ label=\"Process %ld\" ];\n", (long)getpid(), (long)getpid());
/* and if not at the top level (0), an edge from our parent. */
if (level)
fprintf(dot, " \"%ld\" -> \"%ld\";\n", (long)getppid(), (long)getpid());
fflush(dot);
}
/* No more forking? */
if (level >= maxlevel) {
if (level)
exit(status);
else
return status;
}
/* Odd levels create two child processes, even one. */
if (level & 1)
children = 2;
else
children = 1;
/* Fork the child processes, */
for (i = 0; i < children; i++) {
child[i] = fork();
if (child[i] == -1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
} else
if (!child[i]) {
/* have each child run process() and nothing else, */
exit(process(level + 1, maxlevel, dot));
}
/* This line is run in parent only. */
}
/* and wait for them. */
for (i = 0; i < children; i++) {
if (child[i] != -1) {
do {
p = waitpid(child[i], &childstatus, 0);
} while (p == -1 && errno == EINTR);
if (p != child[i])
status = EXIT_FAILURE;
} else
status = EXIT_FAILURE;
}
if (level)
exit(status);
else
return status;
}
int dot_process_tree(const int levels, FILE *out)
{
int retval = EXIT_SUCCESS;
if (out) {
fprintf(out, "digraph {\n");
fflush(out);
}
if (levels > 0)
retval = process(0, levels - 1, out);
if (out) {
fprintf(out, "}\n");
fflush(out);
}
return retval;
}
int main(void)
{
return dot_process_tree(5, stdout);
}
, скомпилируете и запустите его, используя
reset ; gcc -Wall -Wextra -O2 tree.c -o tree && ./tree | dot -Tx11
вы получите красивое графическое дерево процессов.(Используйте dot -Tsvg > out.svg
или dot -Tpng > out.png
, чтобы сохранить его как изображение SVG или PNG.) В моей системе:
Обратите внимание, что нет причин, по которым идентификаторы процессадолжен быть в порядке дерева.Хотя, например, Linux передает их довольно упорядоченным образом, они могут быть в любом порядке, даже совершенно случайными.Поэтому не делайте никаких предположений относительно PID.
Сам язык Dot прост.Вывод этой программы выглядит примерно так:
digraph {
"12375" [ label="Process 12375" ];
"12377" [ label="Process 12377" ];
"12375" -> "12377";
"12378" [ label="Process 12378" ];
"12377" -> "12378";
"12379" [ label="Process 12379" ];
"12377" -> "12379";
"12380" [ label="Process 12380" ];
"12378" -> "12380";
"12381" [ label="Process 12381" ];
"12379" -> "12381";
"12382" [ label="Process 12382" ];
"12380" -> "12382";
"12384" [ label="Process 12384" ];
"12381" -> "12384";
"12383" [ label="Process 12383" ];
"12380" -> "12383";
"12385" [ label="Process 12385" ];
"12381" -> "12385";
}
, что должно быть очевидно;узлы именуются по идентификатору процесса, а [ label="Title" ]
устанавливает текст в узле.Это не тот же прогон, что и на схеме выше, поэтому идентификаторы процессов различаются.
В Dot цифры нужно заключать в кавычки, если они используются в качестве имени, но если имя начинается с буквы, вы нене нужно цитировать это.См. Документация Graphviz для получения более подробной информации.(Страница Node, Edge и Graph Attributes - это та страница, которая вам обычно нужна.)
Если вы хотите отображать уровень в каждом узле, используйте
fprintf(dot, " \"%ld\" [ label=\"Process %ld, level %u\" ];\n", (long)getpid(), (long)getpid(), level + 1);
вprocess()
.(Используется уровень 0 вперед, причем все ненулевые уровни являются дочерними процессами, а уровень 0 - исходным процессом. Вот почему возвращается уровень 0 и все остальные уровни exit()
.)