Linux内核:为什么'subclass'结构将基类信息放在最后?

Mik*_*all 20 c pointers linux-device-driver linux-kernel data-structures

我正在阅读Linux内核上的Beautiful Code中的章节,作者讨论了Linux内核如何在C语言中实现继承(以及其他主题).简而言之,定义了一个"基础"结构,为了从中继承,"子类"结构将基类的副本放在子类结构定义的末尾.然后,作者花了几页来解释一个聪明而复杂的宏,以确定要从对象的基本部分转换为对象的子类部分需要多少字节.

我的问题:在子类struct中,为什么不将struct struct声明为struct中的第一个东西,而不是最后一个

首先放置基础结构的主要优点是从基类转换到子类时根本不需要移动指针 - 实际上,执行转换只是告诉编译器让代码使用'额外'子类struct在基类定义的东西之后放置的字段.

只是为了澄清我的问题,让我抛出一些代码:

struct device { // this is the 'base class' struct
     int a;
     int b;
     //etc
}
struct usb_device { // this is the 'subclass' struct
    int usb_a;
    int usb_b;
    struct device dev; // This is what confuses me - 
                       // why put this here, rather than before usb_a?
}
Run Code Online (Sandbox Code Playgroud)

如果碰巧有一个指向usb_device对象内部的"dev"字段的指针,那么为了将其强制转换回该usb_device对象,需要从该指针中减去8.但是如果"dev"是usb_device中的第一个东西,那么指针根本就​​不需要移动指针.

任何有关这方面的帮助将不胜感激.甚至关于在何处找到答案的建议也会受到赞赏 - 我不太确定谷歌如何为这样的决定背后的架构原因.我在StackOverflow上找到的最接近的是: 为什么要使用这些奇怪的嵌套结构

而且,要明确一点 - 我知道很多聪明的人已经在Linux内核上工作了很长时间,所以很明显这样做是有充分理由的,我只是无法弄清楚它是什么.

Aar*_*lla 12

Amiga OS在很多地方使用这种"常见标题"技巧,当时看起来是个好主意:通过简单地转换指针类型进行子类化.但也有缺点.

优点:

  • 您可以扩展现有数据结构
  • 您可以在期望基本类型的所有位置使用相同的指针,不需要指针算法,从而节省宝贵的周期
  • 感觉很自然

缺点:

  • 不同的编译器倾向于不同地对齐数据结构.如果基本结构以...结束char a;,那么在子类的下一个字段开始之前,您可以有0,1或3个填充字节.这导致了非常讨厌的错误,特别是当你必须保持向后兼容性时(即由于某种原因,你必须有一个填充因为一个古老的编译器版本有一个错误,现在,有很多代码需要填充错误) .
  • 当你传递错误的结构时,你不会很快注意到.使用您的问题中的代码,如果指针算术错误,字段会很快被删除.这是一件好事,因为它提出了更早发现错误的机会.
  • 它导致了一种态度"我的编译器将为我修复它"(有时它不会)并且所有演员都会导致"我比编译器更了解"的态度.后者会让你在理解错误信息之前自动插入强制转换,这会导致各种奇怪的问题.

Linux内核将通用结构放在别处; 它可以但不必在最后.

优点:

  • 错误将提前显示
  • 你必须为每个结构做一些指针运算,所以你已经习惯了
  • 你不需要演员阵容

缺点:

  • 不明显
  • 代码更复杂


S.C*_*sen 9

我是Linux内核代码的新手,所以请把我的ramblings带到这里.据我所知,没有要求放置"子类"结构的位置.这正是宏提供的内容:无论布局如何,都可以转换为"子类"结构.这为您的代码提供了强大的功能(结构的布局可以更改,而无需更改您的代码.也许有一种将"基类"结构放在最后的约定,但我不知道它.我已经在驱动程序中看到了很多代码,其中使用了不同的"基类"结构来转换回相同的"子类"结构(当然,来自"子类"中的不同字段).


Art*_*Art 5

我没有Linux内核的新经验,而是来自其他内核.我会说这根本不重要.

你不应该从一个投射到另一个.允许这样的演员阵容只能在非常特殊的情况下进行.在大多数情况下,它会降低代码的健壮性和灵活性,并且被认为非常草率.因此,您正在寻找的最深刻的"建筑理由"可能就是"因为这是某人碰巧写入的顺序".或者,基准测试表明,该代码中某些重要代码路径的性能最佳.或者,编写它的人认为它看起来很漂亮(如果我没有其他约束,我总是在我的变量声明和结构中构建颠倒的金字塔).或者有人碰巧在20年前以这种方式写它,从那以后其他人都在复制它.

这可能背后有一些更深层的设计,但我对此表示怀疑.根本没有理由设计这些东西.如果你想从权威来源找到为什么这样做,只需向linux提交一个补丁,然后看看谁会对你大喊大叫.