C:Linux内核数据结构中内置的链表用法

C:Linux内核数据结构中内置的链表用法,c,linux-kernel,kernel,C,Linux Kernel,Kernel,我正在尝试向linux内核添加一个系统调用,当我修改task_结构(通过添加一个链表)时,使用内置链表会很有好处,task_结构中已经有很多用于其他目的的struct list头。为了一致性,我想坚持使用这种数据结构 我的问题是我真的不完全理解如何使用这个结构。例如,我看到他们有“struct list_head children”。然而,这种结构的实现是简单的“*下一个”和“*最后一个” 我在网上查阅了一些例子,每个人都说:好吧,让我们来做一个 struct node{ int dat

我正在尝试向linux内核添加一个系统调用,当我修改task_结构(通过添加一个链表)时,使用内置链表会很有好处,task_结构中已经有很多用于其他目的的struct list头。为了一致性,我想坚持使用这种数据结构

我的问题是我真的不完全理解如何使用这个结构。例如,我看到他们有“struct list_head children”。然而,这种结构的实现是简单的“*下一个”和“*最后一个”

我在网上查阅了一些例子,每个人都说:好吧,让我们来做一个

struct node{
    int data;
    struct list_head list;
}; 
但这难道不意味着我应该包含在任务结构中的数据结构应该是

struct node list;
?

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

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

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

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

我已经获得了一个函数执行的系统调用列表。我将它们保存在单独的char*变量中。我想在Processs任务结构中添加一个列表,用于跟踪所有这些系统调用

比如说

处理abc电话 printf~>等同于“WriteScreen” getchar~>等同于“ReadKey”

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

两次调用后,“abc”的任务结构都有一个列表

abc->任务结构->标记列表

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


稍后,我将使用这些标记打印一个名为WriteScreen、ReadKey等的进程列表。这些进程的实现将在我了解如何使用该列表来适当存储附加到进程的字符串之后进行。

关于Linux内核链表的重要参考是

您可以如下方式初始化结构:

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 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\u head
的指针,我们可以获得指向
struct node
类型的指针。这个魔术是由宏的
容器实现的,它在
列表项
中调用


希望我能帮上忙。

编辑:这个答案是在OP更清楚地解释他/她试图做什么之前提供的

您只能使用新成员修改任务结构:

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;
}
此扩充本身不会起任何作用,因为没有人会更改或以其他方式访问此成员

您应该将列表头声明在其他位置(例如,全局),然后可以启动列表的addind进程(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
}

因此,与创建进程列表(task_struct)不同,您试图完成的是每个进程的列表

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

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

因此,列表节点将是:

struct my_node {
    struct list_head list;
    char data[100]; // arbitrarily set to 100; could be also char*
}
任务结构应增加一个新的列表头:

struct task_struct {
  // many members that contains info about a process
  ...

  struct list_head 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
}
编辑:

但是,请注意,您有其他方法来完成您正在尝试执行的操作,而无需求助于复杂的链表

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

"WriteScreen,ReadKey\0"

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

谢谢,这非常有帮助。但问题是:如何知道头部是否已经为某个进程初始化?如果不是空的,我不能简单地列出头(我的头),在这种情况下,LIST-empty会让我知道。但是如果它还没有初始化,list_empty将不会返回true。而且,听起来有点像它将包含进程本身的列表,而不是每个进程的char*列表?我这样读不对吗?@Aserian您应该在进程创建时将指针归零(这样您就可以知道它们何时被初始化,因为它们将指向非空的对象)。@Aserian是的,列表包含进程本身。您可以用来列出指向数据的实现;Linux内核链表非常聪明,因为数据(这里是
task\u struct
)指向列表(每个
struct list\u head
成员)。