'Kernel module that iterates over the tree of children of a task using depth first search

So I know how to create a kernel and to iterate over the processes linearly by simply including linux/sched.h and using the following code:

struct task_struct *task;

for_each_process(task)
{
   printk("Name: %s PID: [%d]\n", task->comm, task->pid);
}

How can I print these tasks using a depth first search? I want my output to be similar to the one of ps -eLf.

The following patch of code has been given for reference:

struct task_struct *task;
struct list_head *list;
list_for_each(list, &init_task->children) {
    task = list_entry(list, struct task_struct, sibling);
    /* task points to the next child in the list */
}

and I know that task->comm returns the name and task->pid returns the PID for that task.

What fields are used to for the state and parent PID?



Solution 1:[1]

you can get the state with task->state /* -1 unrunnable, 0 runnable, >0 stopped */

get the parent pid with task->parent->pid

Solution 2:[2]

For uid, tgid you can refer cred structure in task_struct. For priority you can refer rt_priority(for real time priority) and prio field. For other fields you may refer proc directory (Because ps will take input from proc).

Solution 3:[3]

I see the accepted answer is using recursion. Even if the code is simpler, it is still a very bad idea to write recursive functions in kernel code. The kernel has a limited stack size available and a recursive functions can easily overflow that and crash everything. You should implement DFS/BFS iteratively instead: the Wikipedia pages (DFS, BFS) have explanations and examples of code.

Here's a simple DFS and BFS implementation using a support struct as a queue (tested on kernel 5.10). Since the only difference between iterative DFS and BFS is pushing new elements on the head or the tail of the queue respectively, I just implemented both and you can switch to BFS by simply adding #define BFS.

// Uncomment to use BFS instead of DFS
// #define BFS

static void dump_children_tree(struct task_struct *task) {
    char comm[TASK_COMM_LEN];
    struct task_struct *child;
    struct list_head *list;
    struct queue *q, *tmp;
#ifdef BFS
    struct queue **tail;
#endif
    pid_t ppid;

    q = kmalloc(sizeof *q, GFP_KERNEL);
    if (!q)
        goto out_nomem;

    q->task = task;
    q->next = NULL;
#ifdef BFS
    tail = &q->next;
#endif

    while (q) {
        task = q->task;
#ifndef BFS
        tmp = q;
        q = q->next;
        kfree(tmp);
#endif

        rcu_read_lock();
        ppid = rcu_dereference(task->real_parent)->pid;
        rcu_read_unlock();

        pr_info("Name: %-20s State: %ld\tPID: %d\tPPID: %d\n",
            get_task_comm(comm, task),
            task->state,
            task->pid,
            ppid
        );

        list_for_each(list, &task->children) {
            child = list_entry(list, struct task_struct, sibling);
            get_task_struct(child);

            tmp = kmalloc(sizeof *tmp, GFP_KERNEL);
            if (!tmp)
                goto out_nomem;

            tmp->task = child;
#ifdef BFS
            tmp->next = NULL;
            *tail = tmp;
            tail = &tmp->next;
#else // DFS
            tmp->next = q;
            q = tmp;
#endif
        }

        put_task_struct(task);
#ifdef BFS
        tmp = q;
        q = q->next;
        kfree(tmp);
#endif
    }

    return;

out_nomem:
    while (q) {
        tmp = q;
        q = q->next;
        kfree(tmp);
    }
}

NOTE: the above function assumes that get_task_struct() was already called on the passed task, and automatically calls put_task_struct() for you on it before returning.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 joe
Solution 2 Amar
Solution 3