10

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?

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
Hassan Jalil
  • 1,114
  • 4
  • 14
  • 34
  • You need to implement DFS or BFS. Googling around will lead you to the algorithm. There is no support for DFS or BFS in the kernel if you looking for some API. – Vivek S Oct 08 '13 at 06:11

4 Answers4

10

This is a bit old, but I came across it as it seems to be one of the programming projects found in chapter 3 of Operating System Concepts 9th Edition, so others may yet come looking.

The code you started with is straight from the book, but it is a good starting point. You just need to implement the DFS. Here is the code that will accomplish it, it should be pretty self explanatory:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>

/**
 * Performs a DFS on a given task's children.
 *
 * @void
 */
void DFS(struct task_struct *task)
{   
    struct task_struct *child;
    struct list_head *list;

    printk("name: %s, pid: [%d], state: %li\n", task->comm, task->pid, task->state);
    list_for_each(list, &task->children) {
        child = list_entry(list, struct task_struct, sibling);
        DFS(child);
    }
}

/**
 * This function is called when the module is loaded. 
 *
 * @return 0  upon success
 */ 
int task_lister_init(void)
{
    printk(KERN_INFO "Loading Task Lister Module...\n");
    DFS(&init_task);

    return 0;
}

/**
 * This function is called when the module is removed.
 *
 * @void
 */
void task_lister_exit(void)
{
    printk(KERN_INFO "Removing Task Lister Module...\n");
}

// Macros for registering module entry and exit points.
module_init(task_lister_init);
module_exit(task_lister_exit);
Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
tam5
  • 3,197
  • 5
  • 24
  • 45
  • I was under the impression that `list_entry(list, struct task_struct, sibling);` should be `list_entry(list, struct task_struct, children);` but it doesent work. Can you please explain to me what am missng? – Vivek Maran Jul 24 '18 at 05:11
1

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

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

joe
  • 1,136
  • 9
  • 17
0

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).

Amar
  • 5
  • 1
0

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.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128