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

时间:2014-11-07 18:01:59

标签: c linux-kernel kernel

我正在尝试向我的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”

我现在拥有一个包含这两个字符串的userland代码片段。我打电话给系统调用,我会写(每个标签一次)用这些系统调用“标记”过程。

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

ABC-> task_struct-> tag_list

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

稍后我将使用这些标记来打印一个名为WriteScreen,ReadKey等的进程列表。在我找到如何使用该列表来正确存储附加到进程的字符串之后,将会实现这些。< / p>

3 个答案:

答案 0 :(得分:4)

因此,您不是要创建一个进程列表(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;
}

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

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

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"

在这种情况下,你应该照顾你的缓冲区限制,永远不要溢出它。另一方面,您不必负责分配和释放列表的节点。

答案 1 :(得分:2)

关于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 node的指针来获取指向struct list_head类型的指针。这种魔法是由container_of内部调用的list_entry宏完成的。

希望我能帮忙。

答案 2 :(得分:2)

编辑:在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;
}

这种扩充本身不起作用,因为没有人会改变或以其他方式访问该成员。

您应该将列表头部声明为其他地方(例如全局),然后您可以将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
}