Linux进程描述符中的union用法

3

在阅读“理解Linux内核”时,我发现union被用于进程描述符数据结构。

union thread_union {
   struct thread_info thread_info;
   unsigned long stack[2048]; /* 1024 for 4KB stacks */
};

为什么在这里使用union,对于同时使用两个数据结构的union thread_union


这里使用Union可能是因为thread_infostack不需要同时存在。 - Lee Duhem
1
数据大小需要是某种页面大小的倍数,而结构体位于堆栈顶部。我想这是一种填充技巧。 - tangrs
@leeduhem,我也在猜测,但我们对我们怀疑的“需求”不确定! - neeru
1个回答

5
首先,它是
union thread_union {
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};

在内核中定义(参见include/linux/sched.h)。这很重要,因为宏THREAD_SIZE在许多地方使用(总体上在内核源代码中使用了几百次),并且在不同的架构之间有所不同。

楼主想知道为什么不使用结构体:

struct thread_struct {
    struct thread_info thread_info;
    unsigned long stack[(THREAD_SIZE - sizeof (struct thread_info))/sizeof (long)];
};

(我假设相关的宏,init_thread_infoinit_stack已经相应地进行了调整,即两者都引用init_thread_union的开头,以便实际内存布局不会改变。)
简单的原因是联合体的两个成员旨在驻留在同一内存区域中,因此使用联合体更为适当。
完整的推理更加复杂。主要观点是,所有架构都在init/init_task.c中定义了一个这种联合类型的init_thread_union变量,用于引导时的初始内核线程,并且预处理器宏。
#define init_thread_info    (init_thread_union.thread_info)
#define init_stack          (init_thread_union.stack)

在特定于架构的头文件中(例如,在x86上的{{link1:arch/x86/include/asm/thread_info.h}}中),这些宏分别引用初始线程(启动内核的线程)及其堆栈。

据我所知,union thread_union类型除了初始堆栈和线程信息外没有其他用途。此外,init_thread_info部分仅在引导过程中需要,后续不再需要。

这意味着,如果使用结构体代替联合体,则struct thread_info部分将在内存中保持未使用状态,只要内核正在运行即可。当然,这并不是很多字节... 但是,使用联合体 - 请记住,在Linux中,堆栈向下增长 - 初始线程信息位于初始堆栈区域的末尾,如果在内核代码内存在足够深的调用链,需要使用所有可用的内核堆栈位,那么初始thread_info将被堆栈数据覆盖。这没关系,因为它不再需要。

(如果您非常敏锐,您会意识到使用该结构将具有相同的实际效果:运行init_stack时会溢出到init_thread_info成员,覆盖它。假设,如我在括号中所注明的那样,宏被调整为指向联合体的开头。如果未调整宏,则初始线程信息将保留在内存中,未使用,直到重新启动或关闭。)
因此,总之,联合体更加适用,因为内核开发人员仅使用联合类型来处理初始线程信息和初始堆栈(用于引导内核的线程),并且明确希望它们占据相同的内存区域。虽然使用结构可以实现完全相同的实际效果,但这将使init_thread_infoinit_stack宏变得不必要地复杂,浪费其他/未来开发人员的时间,尝试解释原始意图。

最后,请记住,内核开发人员对实际结果比理论或标准更感兴趣。例如,C编译器的编写者可能会指出,根据C标准,访问联合中与上次分配给该联合使用的成员不同的成员将产生未定义的结果。但这并不重要:内核依赖于实际的、真实世界的行为,而不是任何标准的文本。这也意味着阅读代码、注释以及与内核相关的LKML或其他邮件列表上的讨论总是比依靠一般的C知识更加有启发性和可靠。


在您的意见中,一个初学者应该如何开始接触内核源代码?我认为从这本书开始是不错的选择。 - neeru
1
@neeru: 我认为kernelnewbies.org,特别是Kernel Hacking部分,是一个很好的起点。就我个人而言,我所学习的关于内核的一切都是因为我有一些具体的东西需要修复、修改、扩展或理解。我强烈推荐从你特别感兴趣的某个驱动程序或功能开始。如果没有你感兴趣的驱动程序或子系统,请查看Kernel Janitors项目。深入其中,并尽力做到有用。每个人都有机会。 - Nominal Animal
我不确定你所声称的 union thread_union 仅在引导期间用于初始内核线程是正确的。据我所知,每个进程都会有一个 union thread_union - lord.garbage

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接