V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
feng32
V2EX  ›  程序员

一个实际工程中的 C 语言问题

  •  
  •   feng32 · 2019-08-15 16:42:23 +08:00 · 2233 次点击
    这是一个创建于 1687 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有一个 Linux 驱动模块,它会给每个设备分配一个数据结构

    struct A {
        ...
        struct list_head list;
        ...
    }
    

    这个数据结构内含一个 “带头节点的双向循环链表”,这个 struct list_head 是 Linux 内核中的一个标准结构

    struct list_head {
        struct list_head *prev;
        struct list_head *next;
    }
    

    一开始的时候,链表为空,只有一个头节点,所以 prev 指向自己,next 也指向自己

    当系统上有两个设备的时候,就会产生 2 个结构体 a1 和 a2,同时也会产生两条链表

    现在,出于一些原因,我需要让 a1 和 a2 共享一条链表。把 struct A 中的 struct list_head list 改为 struct list_head *list 这个方案看起来很直接,但是一旦这么改,代码中其它数百处引用这个 list 成员的地方,就需要一一修改了,成本很高。

    如果能让 list 变成类似 C++ 中的引用变量,似乎可以解决问题,但是 Linux Kernel 不支持 C++

    请问 C 语言里,是否还存在一些其它的可能性,可以避免更新所有引用 list 变量的代码?

    16 条回复    2019-08-16 07:48:41 +08:00
    wutiantong
        1
    wutiantong  
       2019-08-15 16:55:48 +08:00
    先分析清楚你的业务场景吧,不要一上来就一通乱改。
    feng32
        2
    feng32  
    OP
       2019-08-15 17:13:14 +08:00
    @wutiantong 我已经故意把业务场景隐去了,这里讨论的就是 C 语言的奇技淫巧
    takemeh
        3
    takemeh  
       2019-08-15 17:14:00 +08:00
    每个设备对应的结构体里面的链表头是放什么数据的呢?
    另外膜拜写内核的大佬.
    feng32
        4
    feng32  
    OP
       2019-08-15 17:16:57 +08:00
    @takemeh 链表里装的数据是另一个 struct B,和这个问题没什么关系

    内核里的 list_head 和学校里学的传统链表不太一样,可以参考这篇文章:

    https://www.cnblogs.com/zhuyp1015/archive/2012/06/02/2532240.html
    msg7086
        5
    msg7086  
       2019-08-15 17:21:06 +08:00
    你改成引用变量也要改签名吧,一样的。
    cqcsdzmt
        6
    cqcsdzmt  
       2019-08-15 17:27:00 +08:00
    a1->list->prev=b1->list->prev;
    a1->list->next=b1->list->next;
    ....
    cqcsdzmt
        7
    cqcsdzmt  
       2019-08-15 17:29:33 +08:00
    如上问题,骚 easy 的东西为什么想的这么复杂。
    cqcsdzmt
        8
    cqcsdzmt  
       2019-08-15 17:31:25 +08:00
    更正一下
    a1.list->prev=b1->list->prev;
    a1.list->next=b1->list->next;
    cqcsdzmt
        9
    cqcsdzmt  
       2019-08-15 17:31:42 +08:00   ❤️ 1
    更正一下
    a1.list->prev=b1.list->prev;
    a1.list->next=b1.list->next;
    feng32
        10
    feng32  
    OP
       2019-08-15 17:58:46 +08:00
    @cqcsdzmt 如果没有对 head 取地址的操作,那么它就是对的,但是实际上在 linux kernel 的使用场景下,它是有问题的

    list_head 链表的遍历,就是不断 next 一直到达原先的 head,如果这么赋值,对 a1 的遍历就死循环了
    feng32
        11
    feng32  
    OP
       2019-08-15 18:06:58 +08:00
    @msg7086 如果大多数情况引用的是 struct A,函数签名里就没有 list 的类型了,所以这还是有很大作用的
    reus
        12
    reus  
       2019-08-15 18:10:58 +08:00
    menyakun
        13
    menyakun  
       2019-08-15 18:34:57 +08:00
    好像没办法不改吧,我怎么感觉这个 list 结构应该放在设备的 inode 中,然后在 open 的时候,`file->private_data->list = list`这样
    secondwtq
        14
    secondwtq  
       2019-08-15 18:56:43 +08:00
    ... 正解就是让电脑帮你去改

    用奇技淫巧只会留下更多坑
    boywhp
        15
    boywhp  
       2019-08-15 20:50:44 +08:00
    全局定义一个 struct list_head xxList = {0};
    waruqi
        16
    waruqi  
       2019-08-16 07:48:41 +08:00 via Android
    A 里面还是改成 *list 不过所有操作 list 的 api 你用宏做下替换,比如之前是 insert(&list, x) ,用法保持不变 你写个 insert 宏替换之前的 insert func

    #define insert_org insert
    #define insert(list, x) insert_org(*(list), x)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3246 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 46ms · UTC 12:13 · PVG 20:13 · LAX 05:13 · JFK 08:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.