Linux内核:为什么'子类'结构将基类信息放在最后? [英] Linux kernel: why do 'subclass' structs put base class info at end?

查看:198
本文介绍了Linux内核:为什么'子类'结构将基类信息放在最后?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读有关Linux内核的美丽代码的章节,作者讨论Linux内核如何实现C语言(包括其他主题)中的继承。简而言之,定义了一个基本结构,为了继承它,子类结构在子类结构定义的末尾中放置了一个基础的副本。然后,作者然后花费几页解释一个聪明而复杂的宏来确定要从对象的基本部分转换到对象的子类部分的多少字节。

I was reading the chapter in Beautiful Code on the Linux kernel and the author discusses how Linux kernel implements inheritance in the C language (amongst other topics). In a nutshell, a 'base' struct is defined and in order to inherit from it the 'subclass' struct places a copy of the base at the end of the subclass struct definition. The author then spends a couple pages explaining a clever and complicated macro to figure out how many bytes to back in order to convert from the base part of the object to the subclass part of the object.

我的问题:在子类结构中,为什么不将结构声明为结构体中的第一个事物,而不是最后的的东西?

My question: Within the subclass struct, why not declare the base struct as the first thing in the struct, instead of the last thing?

将基础结构的第一个主要优点在于从基础到子类的转换,您根本不需要移动指针 - 实际上,做这个演员只是告诉编译器让你的代码使用子类结构在基础定义的东西之后放置的'extra'字段。

The main advantage of putting the base struct stuff first is when casting from the base to the subclass you wouldn't need to move the pointer at all - essentially, doing the cast just means telling the compiler to let your code use the 'extra' fields that the subclass struct has placed after the stuff that the base defines.

澄清我的问题有点让我抛出一些代码:

Just to clarify my question a little bit let me throw some code out:

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?
}

如果碰巧有一个指向dev字段的指针usb_device对象然后为了将其转发回该usb_device对象,需要从该指针中减去8。但是如果dev是usb_device中的第一件事,那么这个指针就不需要移动指针了。

If one happens to have a pointer to the "dev" field inside of a usb_device object then in order to cast it back to that usb_device object one needs to subtract 8 from that pointer. But if "dev" was the first thing in a usb_device casting the pointer wouldn't need to move the pointer at all.

对此的任何帮助将不胜感激。甚至不知道在哪里找到答案的建议将不胜感激 - 我不太确定Google如何为这样的决定背后的架构原因。 StackOverflow最接近的是:
为什么要使用这些奇怪的嵌套结构

Any help on this would be greatly appreciated. Even advice on where to find an answer would be appreciated - I'm not really sure how to Google for the architectural reason behind a decision like this. The closest I could find here on StackOverflow is: why to use these weird nesting structure

而且,为了清楚,我明白很多明亮的人已经在Linux内核上工作了很长时间,所以很清楚一个很好的理由这样做,我只是想不出它是什么。

And, just to be clear - I understand that a lot of bright people have worked on the Linux kernel for a long time so clearly there's a good reason for doing it this way, I just can't figure out what it is.

推荐答案

Amiga操作系统使用这个常见的头技巧在很多地方,它看起来像是一个好主意当时:通过简单地转换指针类型进行子类化。但是有缺点。

The Amiga OS uses this "common header" trick in a lot of places and it looked like a good idea at the time: Subclassing by simply casting the pointer type. But there are drawbacks.

Pro:


  • 您可以扩展现有的数据结构

  • 您可以在预期基类型的所有地方使用相同的指针,不需要指针算术,节省宝贵的周期

  • 感觉自然

Con:


  • 不同的编译器往往数据结构不一致。如果基本结构以 char a; 结束,那么在子类的下一个字段开始之前,您可以再次使用0,1或3个填充字节。这导致了相当糟糕的错误,特别是当你必须保持向后兼容性(即由于某种原因,你必须有一定的填充,因为一个古老的编译器版本有一个bug,现在,有很多代码,期望的错误填充) 。

  • 当你传递错误的结构时,你不会很快注意到。使用您的问题中的代码,如果指针算术错误,则字段很快被丢弃。这是一件好事,因为它提高了更早发现错误的机会。

  • 它导致一种态度,我的编译器将为我解决(有时不会)所有的演员都导致我比编译器更好的态度。后者会使您在理解错误消息之前自动插入演员,这将导致各种奇怪的问题。

  • Different compilers tend to align data structures differently. If the base structure ended with char a;, then you could have 0, 1 or 3 pad bytes afterwards before the next field of the subclass starts. This led to quite nasty bugs, especially when you had to maintain backwards compatibility (i.e. for some reason, you have to have a certain padding because an ancient compiler version had a bug and now, there is lots of code which expects the buggy padding).
  • You don't notice quickly when you pass the wrong structure around. With the code in your question, fields get trashed very quickly if the pointer arithmetic is wrong. That is a good thing since it raises chances that a bug is discovered more early.
  • It leads to an attitude "my compiler will fix it for me" (which it sometimes won't) and all the casts lead to a "I know better than the compiler" attitude. The latter one would make you automatically insert casts before understanding the error message, which would lead to all kinds of odd problems.

Linux内核正在把共同的结构放在别处;它可以但不一定是在结束。

The Linux kernel is putting the common structure elsewhere; it can be but doesn't have to be at the end.

Pro:


  • 错误将显示早期

  • 您将不得不为每个结构做一些指针算术,所以你习惯了

  • 你不需要演员

Con:


  • 不明显

  • 代码更复杂

这篇关于Linux内核:为什么'子类'结构将基类信息放在最后?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆