【问题标题】:C: Linux built in linked list in kernel data structures usageC:Linux内置链表在内核数据结构中的使用
【发布时间】:2014-11-07 18:01:58
【问题描述】:

我正在尝试向我的 linux 内核添加系统调用,并且在我修改 task_struct(通过添加链表)时使用内置链表将是有利的,并且 task_struct 已经有很多struct list_head 在那里用于其他目的。为了统一起见,我想坚持使用这种数据结构。

我的问题是我真的不完全了解如何使用这种结构。例如,我看到他们有“struct list_head children”。但是,这种结构的实现很简单,一个“*next”和“*last”。

我在网上查看了一些例子,每个例子都说,好吧

struct node{
    int data;
    struct list_head list;
}; 

但这是否表明我应该在 task_struct 中包含的数据结构应该是

struct node list;

?

如果我使用 list_head,我不太明白如何初始化结构以包含我希望它包含的数据。

本质上,我想添加一个系统调用的链表,并以 char* 链表(可读格式)的形式将它们附加到进程中。

目前,获取系统调用信息的语义并不重要......我只需要弄清楚如何让链表与task_struct一起工作。

编辑:例如我正在尝试做的事情:

我获得了一个函数执行的系统调用列表。我将它们保存在单独的 char* 变量中。我想在进程 task_struct 中添加一个列表,以跟踪所有这些系统调用。

例如

处理“abc”调用 printf ~> 等同于“WriteScreen” getchar ~> 等同于“ReadKey”

我现在有一段包含这两个字符串的用户态代码。我调用了一个系统调用,我将编写(每个标记一次)以使用这些系统调用“标记”进程。

两次调用后,'abc'的task_struct有一个列表

abc->task_struct->tag_list

此列表包含“WriteScreen”和“ReadKey”。

稍后我将使用这些标签来打印已调用 WriteScreen、ReadKey 等的进程的列表。这些将在我了解如何使用该列表来适当地存储附加到进程的字符串之后进行。

【问题讨论】:

    标签: c linux-kernel kernel


    【解决方案1】:

    因此,您要尝试完成的是每个进程的列表,而不是创建进程列表 (task_struct)。

    这意味着每个进程都有自己的列表,即自己的列表头。

    除了 next/prev 指针之外,这个列表还将存储一条数据,实际上是一个字符串(可以是字符串本身,也可以是指向其他地方的字符串的指针)。

    因此,列表节点将是:

    struct my_node {
        struct list_head list;
        char data[100]; // arbitrarily set to 100; could be also char*
    }
    

    task_struct 应该增加一个新的列表头:

    struct task_struct {
      // many members that contains info about a process
      ...
    
      struct list_head my_list;
    }
    

    是的。您会注意到两种情况(当进程属于列表时,以及当列表属于进程时)成员是相同的;只是用途不同。

    现在,当创建进程时,您应该初始化列表头(因为每个进程都会有一个新列表):

    struct task_struct *new_process;
    INIT_LIST_HEAD(&new_process->my_list);
    

    插入一个新节点(假设你已经创建了它,也就是分配了内存并初始化了它的数据):

    struct my_node *node; 
    struct task_struct *a_process;
    
    [... my_node initialized ...]
    [... a_proccess obtained somehow ...]
    
    list_add_tail(&node->list, &a_process->my_list);
    

    遍历元素:

    struct my_node *p;
    struct task_struct *a_process
    
    // list is the member name (yes, the member name) of your list inside my_node
    list_for_each_entry(p, &a_process->my_list, list) {
      // do whatever you want with p
    }
    

    编辑:

    但是请注意,您还有其他方法可以做您想做的事情,而无需求助于复杂的链表。

    例如,您可以分配一个 char 数组并通过用一些 char(逗号、句点等)分隔字符串列表来对字符串列表进行编码。这样:

    "WriteScreen,ReadKey\0"
    

    在这种情况下,您应该注意缓冲区限制,以免溢出。另一方面,您不必负责分配和释放列表的节点。

    【讨论】:

      【解决方案2】:

      关于Linux Kernel链表的大参考是http://www.makelinux.net/ldd3/chp-11-sect-5

      你像这样初始化结构:

      struct node node_var = {
          .data = 0,
          .list = LIST_HEAD_INIT(node_var.list)
      }
      

      你像这样遍历列表:

      struct list_head *phead;
      list_for_each(phead, node_var.list)
      {
         struct node * pnode = list_entry(phead, struct node node_var, list);
         // Do what you may with the pnode.
      }
      

      确实,它看起来确实很奇怪。我们通过使用指向该结构的字段struct list_head 的指针来获得指向struct node 类型的指针。这个魔法是由在list_entry 内部调用的container_of 宏完成的。

      希望我能帮上忙。

      【讨论】:

        【解决方案3】:

        编辑:这个答案是在 OP 对他/她想要做什么提供更清晰的解释之前提供的。

        您应该只使用新成员修改 task_struct:

        struct task_struct {
          // many members that contains info about a process
          ...
          // then come the lists that a process may participate
          ...
          // then you amend with your new list
          struct list_head my_list;
        }
        

        这种扩充本身不会做任何事情,因为没有人会更改或以其他方式访问此成员。

        您应该在其他地方声明您的列表头(例如全局),然后您可以开始将进程(task_struct)添加到您的列表中。

        LIST_HEAD(my_list_head); // this will declare, define and initialize a new variable:
                                 // an empty list.
        

        Linux 内核链表宏将为您处理一切。

        task_struct *a_given_process; // assigned elsewhere, maybe passed as parameter to current function
        list_add_tail(&a_given_process->my_list, my_list_head); // an example
        

        编辑:

        迭代项目:

        struct task_struct *p;
        
        // my_list_head is the head of your list (declared with LIST_HEAD)
        // my_list is the member name (yes, the member name) of your list inside task_struct
        list_for_each_entry(p, my_list_head, my_list) {
          // do whatever you want with p
        }
        

        【讨论】:

        • 谢谢,这很有帮助。但问题是:我如何知道头部是否已经为某个进程初始化?如果它不为空,我不能简单地 LIST_HEAD(my_list_head) ,在这种情况下 list_empty 会让我知道。但是如果还没有初始化,list_empty 就不会返回true。
        • 另外,听起来有点像这将包含进程本身的列表,而不是每个进程的 char* 列表?我这样读错了吗?
        • @Aserian 你应该在进程创建时将指针归零(这样你就会知道它们何时被初始化,因为它们会指向非 NULL 的东西)。
        • @Aserian 是的,该列表包含进程本身。
        • 你可能习惯于列出指向数据的实现; Linux 内核链表非常聪明,因为数据(此处为 task_struct)指向列表(每个 struct list_head 成员)。
        猜你喜欢
        • 2015-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-08
        • 1970-01-01
        • 2019-05-02
        相关资源
        最近更新 更多