Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lists

Many resources in the ukoOS kernel need to be in some registry. For example, devices of a certain device class (e.g. UARTs) generally need to be in some list, so that the kernel can iterate through all UARTs. Lists (struct list_head, after the same structure in Linux) allow for this in ukoOS. See src/kernel/include/list.h for the actual list API.

ukoOS lists are doubly-linked circular intrusive linked lists; that is, rather than the list containing the element in some way (whether by having a pointer that points to it or by including it in each link’s memory allocation), elements contain the link.

For example, let’s say we had a list of RGB colors and names. We might write that data structure as:

struct named_color {
    struct list_head list;
    const char *name;
    u8 r, g, b;
};

This might seem strange, but it has one big advantage over a container that owns its data – elements can belong to multiple lists. For example, pretend we had both an all_colors list and a favorite_colors list:

struct named_color {
    struct list_head all_colors;
    struct list_head favorite_colors;
    const char *name;
    u8 r, g, b;
};

This lets the color be in both lists at once.

Lists are intended to have a single owning node / sentinel node. This is not embedded in the structure like other nodes are, but stands alone, often as a global:

struct named_color {
    struct list_head all_colors;
    struct list_head favorite_colors;
    const char *name;
    u8 r, g, b;
};

struct list_head all_colors = LIST_INIT(all_colors);
struct list_head favorite_colors = LIST_INIT(favorite_colors);

Since the lists are circular, we can use this sentinel node to know when we’ve reached the end of the list.

We can also use this to create a tree:

struct tree_node {
    /**
     * The parent of this node.
     */
    struct tree_node *parent;

    /**
     * This node's link in its parent's children.
     */
    struct list_head list;

    /**
     * This node's children.
     */
    struct list_head children;
};

The container_of macro can be used to go from a struct list_head * to a struct tree_node *:

struct tree_node node = { /* ... */ };

struct list_head *ptr1 = &node.list;
struct tree_node *node1 = container_of(ptr1, struct tree_node, list);
assert(node1 == &node);

struct list_head *ptr2 = &node.children;
struct tree_node *node2 = container_of(ptr2, struct tree_node, children);
assert(node2 == &node);