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

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

我的问题:在子类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? } 

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

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

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

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

优点:

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

缺点:

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

Linux内核将公共结构放在别处; 它可以但不必在最后。

优点:

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

缺点:

  • 不明显
  • 代码更复杂

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

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

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

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

这是多重inheritance。 struct dev不是唯一可以应用于linux内核中的结构的接口,如果你有多个,只是将子类强制转换为基类是行不通的。 例如:

 struct device { int a; int b; // etc... }; struct asdf { int asdf_a; }; struct usb_device { int usb_a; int usb_b; struct device dev; struct asdf asdf; };