【问题标题】:Adding element twice into Linux kernel double linked list将元素两次添加到 Linux 内核双链表中
【发布时间】:2019-10-31 07:04:26
【问题描述】:

我正在尝试在用户空间中使用https://github.com/torvalds/linux/blob/master/include/linux/list.h 中提到的 linux 内核双向链表实现,它的用户空间实现可以在 https://gist.github.com/roychen/1710968 中找到

以下是我最初使用的代码,它运行良好:)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "list.h"

struct Node
{
    int data;
    char name[10];
    struct list_head mylist;
};

int main()
{
    LIST_HEAD(plist);

    struct Node node1 = {.data = 10, .name = "node1", .mylist = LIST_HEAD_INIT(node1.mylist)};
    struct Node node2;

    node2.data = 20;
    strcpy(node2.name, "node2");
    INIT_LIST_HEAD(&node2.mylist);

    list_add_tail(&node1.mylist, &plist);
    list_add_tail(&node2.mylist, &plist);

    struct Node* iter;

    list_for_each_entry(iter, &plist, mylist)
    {
        printf("name = %s, data = %d\n", iter->name, iter->data);
    }

    return 0;
}

上面代码的输出是

name = node1, data = 10
name = node2, data = 20

符合预期。

现在假设我想添加node1 两次

场景一:

    list_add_tail(&node1.mylist, &plist);
    list_add_tail(&node1.mylist, &plist);

输出 1:

name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
name = node1, data = 10
... -> non-stopping loop (to infinity)

场景二:

    list_add_tail(&node1.mylist, &plist);
    list_add_tail(&node2.mylist, &plist);
    list_add_tail(&node1.mylist, &plist);

输出 2:

name = node1, data = 10 (-> just one node is added to the list instead of 3 nodes)

以上输出表明 list.h 实现存在错误,至少在它的一个函数宏中。

不知道在链表中不能两次添加节点的bug在哪里。

有什么想法吗?! :|

***** 编辑 ***** 场景 3:

    list_add_tail(&node1.mylist, &plist);
    list_add_tail(&node2.mylist, &plist);
    list_add_tail(&node1.mylist, &plist);

    struct Node* iter;

    list_for_each_entry_reverse(iter, &plist, mylist)
    {
        printf("name = %s, data = %d\n", iter->name, iter->data);
    }

输出 3:

name = node2, data = 20
name = node1, data = 10
name = node2, data = 20
name = node1, data = 10
name = node2, data = 20
name = node1, data = 10
name = node2, data = 20
name = node1, data = 10
name = node2, data = 20
name = node1, data = 10
... -> non-stopping loop (to infinity)

【问题讨论】:

  • 这不是 Linux 中的错误。这是您的程序中的一个错误,您希望同一块内存可以在列表中出现多次而不会创建循环。
  • 列表中单个元素的next指针只能指向一个位置,但是添加两次时需要同时指向两个不同的位置(不能做)。您看到的是这种内在限制的副作用。
  • 考虑哪种可能性更大——(1) 数百甚至数千甚至数百万的 Linux 黑客使用了双向链表机制,却没有发现你突然遇到的问题,但他们都是错误并且应该注意到你发现了什么,或者 (2) 你正在滥用一个经过良好测试和已知在未滥用时可以工作的系统。总的来说,我认为你犯错的可能性比其他人都大。抱歉,但在与此类似的情况下,其他人都错的可能性微乎其微,甚至可以忽略不计。这几乎(但不完全)总是你正在做的事情。
  • 您可能会在您首先链接到的主实现中观察到list_add_tail() 之前的cmets: —— /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ —— 注意多次出现“新条目”。您的代码没有创建新条目;它正在尝试重用现有条目。
  • 我很佩服你的自信,但请记住,傲慢会导致克星。

标签: c linux linked-list linux-kernel


【解决方案1】:

Linux 链表不支持多次添加节点。您不能将它两次添加到同一个列表中,也不能将它添加到两个不同的列表中。

【讨论】:

  • 有没有宏可以制作副本实例?
  • 我接受这个答案,也感谢@JonathanLeffler
  • @WillyCole 您不需要宏,只需将struct Node 值分配给另一个struct Node,例如struct Node node1copy = node1; list_add_tail(&amp;node1copy.myList, &amp;plist);。在将其添加到列表之前无需执行INIT_LIST_HEAD(&amp;node1copy.myList);,因为node1copy.myList 中的指针无论如何都会被list_add_tail 覆盖。
  • @IanAbbott 这在 C 中是不允许的(该赋值在 C++ 中而不是在 C 中有用)
猜你喜欢
  • 1970-01-01
  • 2017-02-25
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-09
相关资源
最近更新 更多