C:Linux内置链表在内核数据结构使用中



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

我的问题是我真的不完全了解如何使用这种结构。 例如,我看到他们有"结构list_head孩子"。 但是,此结构的实现很简单,即"*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 等的进程列表。 这些的实现将在我了解如何使用列表来适当地存储附加到进程的字符串之后发生。

因此,

您尝试完成的不是创建进程列表(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 数组,并通过用一些字符(逗号、句点等(分隔字符串来对字符串列表进行编码。这边:

"WriteScreen,ReadKey"

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

关于 Linux 内核链表的主要参考是 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宏完成的。

希望我确实有帮助。

编辑:这个答案是在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
}

最新更新