diff -u:内核开发的新内容
有时需要在内核内部更改函数语义,然后查找并更新该函数的所有用户以匹配新的语义。此类更改可能会导致巨大的补丁进入源代码树,影响数百个文件。
Al Viro 想要对大量内存处理例程进行类似的更改。他注意到,现有的内存分配工具都返回纯数字,用户在绝大多数情况下都必须将其转换为指针。Al 发布了一个巨大的补丁,使这些函数都返回指针而不是纯数字。
但 Linus Torvalds 不喜欢这样。他说,这些巨大的语义更改补丁的问题之一是,反向移植其他不相关的补丁变得更加困难。Linus 说,对于每个需要反向移植的补丁——安全修复、新驱动程序等等——为了跨越 Al 的更改障碍,都需要对端口进行重大修改。这对开发人员来说将非常耗时,会增加新错误的可能性,而且似乎没有足够的价值来证明它是合理的。
Linus 说,解决这个问题的方法是创建全新的函数,使用新的名称和新的语义,让内核的各个部分根据自己的意愿切换到新的调用。但是,即使那样在他看来也很难证明是合理的。
最终,Al 放弃了他的大型补丁,并发布了一套新的内存分配指南,这将帮助用户解决在何种情况下使用哪些函数的问题。
1) 大多数情况下,kmalloc() 是正确的选择。限制:对齐方式不优于字对齐,在引导初期不可用,分配的内存是物理连续的,因此最好避免大型分配。
2) kmem_cache_alloc() 允许在缓存创建时指定对齐方式。否则,它与 kmalloc() 类似。通常用于我们有很多某种类型的实例并且想要动态分配这些实例的情况。
3) vmalloc() 用于大型分配。它们将是页对齐的,但 *不是* 物理连续的。另一方面,大型物理连续分配通常不是一个好主意。与其他分配器不同,没有可以在中断中使用的变体;可以在那里释放,但不能分配。请注意,非阻塞变体 *确实* 存在 - __vmalloc(size, GFP_ATOMIC, PAGE_KERNEL) 可以在原子上下文中使用;中断上下文是禁止使用的。
4) 如果在引导初期,alloc_bootmem() 及其友元可能是唯一的选择。经验法则:如果已经打印了“Memory: ...../..... available.....”,则不应使用它。分配是物理连续的,并且在此时,大型物理连续分配仍然可以。
5) 如果您需要为 DMA 分配内存,请使用 dma_alloc_coherent() 及其友元。它们将为您提供虚拟地址以供您使用,以及设备使用的引用相同内存的 DMA 地址;*不要* 尝试从前者派生后者;使用 virt_to_bus() 等是一个非常糟糕的主意 (tm)。
6) 如果您需要引用 struct page,请使用 alloc_page/alloc_pages。
7) 在某些情况下(页表,最明显的例子),__get_free_page() 及其友元可能是正确的答案。原则上,它是情况 (6),但它返回 page_address(page) 而不是页面本身。从历史上看,这是引入的第一个 API,因此很多本应使用其他 API 的地方最终都使用了它。不要假设级别较低会使其比 kmalloc() 更快 - 这根本不是真的。
众所周知,系统调用具有不足的错误报告。有些系统调用接受大量输入,如果其中任何一个输入以任何方式错误,或未能通过某些晦涩的边界检查,则该调用将返回“EINVAL”表示无效数据,但不会提供任何其他关于哪个数据片段有问题、值是什么或问题发生在代码中的哪个位置的线索。
Alexander Shishkin 最近尝试实施一个解决方案来解决这个问题。但真正的问题是内核不能简单地更改系统调用处理返回值的方式。内核和用户空间中都有代码依赖于当前的行为。因此,任何解决方案都必须以某种方式提供额外的报告信息,而不会更改现有调用例程接收 syscall 返回值的方式。
Alexander 的技术利用了系统调用通常通过一组宏进行处理,然后再将其返回值发送回调用例程这一事实。通过为实际的系统调用设计一套全新的返回值,Alexander 的代码可以引用一个错误消息保持容器,宏将能够处理该容器,同时仍然将最初预期的错误代码返回给调用例程。
宏会将指向 JSON 格式的详细错误报告的指针放入 task_struct 数据结构中,调用例程可以使用 prctl() 调用来检索它。
然而,Jonathan Corbet 对这种方法表示强烈怀疑。首先,如果调用例程不主动查询和重置新的调试数据,则该数据将只会停留在 task_struct 中,变得陈旧。虽然自动清除调试数据会违背最初将其放在那里的目的。
Johannes Berg 也指出,如果应用程序必须在旧内核上运行并期望新的调试数据可用,那么 Alexander 的更改生效后,应用程序可能会崩溃。
最终,Alexander 的方法没有被采纳,尽管没有出现更好的想法。这是一个棘手且持久的问题。目前尚不清楚是否有任何解决方案能够回答所有异议,但也许有些方案能够比现状回答更多的异议。
Linux 中存在一个 Y2038 漏洞。这是 32 位 UNIX 时间戳回滚为零的那一天。由于 Linux 基本上运行着当今已知的宇宙,因此必须处理该漏洞,可能的方法是将时间戳更新为保存 64 位值。Deepa Dinamani 发布了一些补丁来做到这一点,但问题并没有就此结束。
该解决方案必须考虑各种可能性。例如,每个不同的文件系统(NFS、ext4、FUSE 等)都需要其自己手工制作的 Y2038 漏洞修复程序。不仅内核需要修复。此外,在 2038 年之后,即使所有文件系统都有自己的修复程序,用户将如何挂载没有修复程序的旧文件系统实例?这也需要解决。此外,还有企业利益需要考虑。某些服务合同将要求 Y2038 修复程序到位,可能在漏洞实际发生之前的几十年。
总的来说,这将是一项大量的工作。Arnd Bergmann、Dave Chinner 和 Deepa 就其中的来龙去脉进行了长时间的技术对话,但从讨论中得出的最明确的方向感是,他们应该忽略所有不直接相关的内容,并且他们应该尽可能多地削减较小的块来解决,希望主块可能会变得更容易和更易于管理。