为什么Linux内核有15+百万行代码?

Jon*_*han 118 linux-kernel

这个单体代码库的内容是什么?

我了解处理器架构支持、安全性和虚拟化,但我无法想象超过 600,000 行左右。

内核代码库中包含驱动程序的历史和当前原因是什么?

这 15 多万行是否包括每个硬件的每个驱动程序?如果是这样,那就引出了一个问题,为什么驱动程序嵌入在内核中,而不是从硬件 ID 自动检测和安装的单独软件包?

对于存储受限或内存受限的设备,代码库的大小是否是一个问题?

如果所有这些都被嵌入,它似乎会使空间受限的 ARM 设备的内核大小膨胀。预处理器是否剔除了很多行?说我疯了,但我无法想象一台机器需要那么多逻辑来运行我所理解的内核角色。

是否有证据表明,由于其看似不断增长的性质,规模将在 50 多年后成为问题?

包括驱动程序意味着它会随着硬件的制造而增长。

编辑:对于那些认为这是内核本质的人,经过一些研究,我意识到并非总是如此。内核不需要这么大,因为 Carnegie Mellon 的微内核Mach 被列为“通常不到 10,000 行代码”的例子

小智 86

根据针对 3.13 的cloc run,Linux 大约有 1200 万行代码。

  • 700 万名司机 LOC/
  • 拱形中 200 万个 LOC/
  • 内核中只有 139,000 个 LOC/

lsmod | wc 在我的 Debian 笔记本电脑上显示 158 个模块在运行时加载,因此动态加载模块是一种很好的支持硬件的方式。

健壮的配置系统(例如make menuconfig)用于选择要编译的代码(更重要的是,编译哪些代码)。嵌入式系统.config仅使用他们关心的硬件支持来定义自己的文件(包括支持内核内置的硬件或作为可加载模块)。

  • @JonathanLeader 我记得 Linux 刚开始时 - 即使让安装程序工作(如果它甚至有安装程序!)也是一个巨大的痛苦 - 仍然有一些发行版你必须手动选择你的*鼠标驱动程序*。让诸如网络之类的东西,或者上帝保佑,X-window 工作,是一种通过仪式。在我第一次安装 Red Hat 时,我必须编写自己的图形驱动程序,因为只有三个 (!) 驱动程序可用。默认情况下让基础工作是成熟的标志 - 显然,您可以在嵌入式系统上进行更多的调整,其中只有少数硬件组合。 (7认同)
  • 我认为由此我们可以得出结论,Linux 内核之所以庞大,是因为它支持各种设备配置,而不是因为它非常复杂。我们在这里看到 15m 线路中很少有实际使用。虽然,几乎所有事情都是如此,它可能过于复杂,但至少我们可以在知道这是合理的情况下晚上睡觉 (6认同)
  • 计算模块是不够的,很多可能是由配置内置的 (3认同)
  • @JonathanLeaders:是的 - 除了用于奇怪设备的模块外,还有用于模糊文件系统、网络协议等的模块...... (2认同)
  • @JonathanLeaders 正如我认为您已经意识到的那样,源代码中的 LOC 或多或少无关紧要。如果你想知道内核使用了多少内存[有更直接的方法](http://unix.stackexchange.com/questions/97261/how-much-ram-does-the-kernel-use)。 (2认同)

use*_*552 77

对于任何好奇的人,这里是 GitHub 镜像的行数细分:

=============================================
    Item           Lines             %
=============================================
  ./usr                 845        0.0042
  ./init              5,739        0.0283
  ./samples           8,758        0.0432
  ./ipc               8,926        0.0440
  ./virt             10,701        0.0527
  ./block            37,845        0.1865
  ./security         74,844        0.3688
  ./crypto           90,327        0.4451
  ./scripts          91,474        0.4507
  ./lib             109,466        0.5394
  ./mm              110,035        0.5422
  ./firmware        129,084        0.6361
  ./tools           232,123        1.1438
  ./kernel          246,369        1.2140
  ./Documentation   569,944        2.8085
  ./include         715,349        3.5250
  ./sound           886,892        4.3703
  ./net             899,167        4.4307
  ./fs            1,179,220        5.8107
  ./arch          3,398,176       16.7449
  ./drivers      11,488,536       56.6110
=============================================
Run Code Online (Sandbox Code Playgroud)

drivers有助于大量的行数。

  • 那很有意思。更有趣的是代码中潜在的弱点,程序员很恼火:`grep -Pir "\x66\x75\x63\x6b" /usr/src/linux/ | wc -l` (23认同)
  • @jimmij '\x73\x68\x69\x74' 可能更常见 [这项开创性的(如果有点过时)研究](http://andrewvos.com/2011/02/21/amount-of-profanity-in -git-commit-messages-per-programming-language/)。 (5认同)
  • 随机事实:接近 OP 估计的 600 000 LOC 的文件夹是文档。 (3认同)
  • `./documentation` 有超过 500,000 行代码吗?....什么? (2认同)

Pet*_*des 53

驱动程序在内核中维护,因此当内核更改需要对函数的所有用户进行全局搜索和替换(或搜索和手动修改)时,由进行更改的人完成。由进行 API 更改的人更新您的驱动程序是一个非常好的优势,而不是在更新的内核上无法编译时必须自己完成。

另一种选择(对于树外维护的驱动程序会发生这种情况),补丁必须由其维护人员重新同步以跟上任何更改。

快速搜索引发了关于树内驱动程序开发与树外驱动程序开发的争论

Linux 的维护方式主要是将所有内容都保存在主线存储库中。配置选项支持构建小型精简内核以控制#ifdefs。因此,您绝对可以构建微小的精简内核,这些内核仅编译整个 repo 中的一小部分代码。

Linux 在嵌入式系统中的广泛使用导致比多年前 Linux 内核源代码树较小时更好地支持遗漏内容。超小 4.0 内核可能比超小 2.4.0 内核小。

  • @JonathanLeaders:是的,它避免了维护不太活跃的驱动程序的位腐烂。在考虑核心更改时,拥有所有驱动程序代码也可能很有用。搜索某些内部 API 的所有调用者可能会发现驱动程序以您没有想到的方式使用它,这可能会影响您正在考虑的更改。 (9认同)
  • 现在这对我来说很有意义,为什么将所有代码放在一起是合乎逻辑的,它以计算机资源和过度依赖为代价节省了工时。 (5认同)
  • @Junaga:您意识到 linux 非常便携和可扩展,对吗?在 32MB 的嵌入式系统上浪费 1MB 永久使用的内核内存是一件大事。源代码大小并不重要,但编译后的二进制大小仍然很重要。内核内存没有分页,所以即使有交换空间,你也无法取回它。 (4认同)

Oli*_*Oli 47

到目前为止的答案似乎是“是的,有很多代码”并且没有人用最合乎逻辑的答案来解决这个问题:15M+?所以呢?1500万行源代码和鱼的价格有什么关系?是什么让这如此难以想象?

Linux 显然做了很多。比什么都重要......但是你的一些观点表明你不尊重它构建和使用时发生的事情。

  • 并非所有内容都已编译。内核构建系统允许您快速定义选择源代码集的配置。有些是实验性的,有些是旧的,有些不是每个系统都需要的。看看/boot/config-$(uname -r)(在 Ubuntu 上)中make menuconfig,你会看到有多少被排除在外。

    这是一个可变目标桌面发行版。嵌入式系统的配置只会引入它需要的东西。

  • 并非所有内容都是内置的。在我的配置中,大多数内核功能都构建为模块:

    grep -c '=m' /boot/config-`uname -r`  # 4078
    grep -c '=y' /boot/config-`uname -r`  # 1944
    
    Run Code Online (Sandbox Code Playgroud)

    需要明确的是,这些全部内置......正如他们可以打印出来,做成一个巨大的纸三明治。除非您为离散硬件工作进行自定义构建(在这种情况下,您已经限制了这些项目的数量),否则这没有意义。

  • 模块是动态加载的。即使系统有数千个模块可用,系统也将允许您只加载您需要的东西。比较以下输出:

    find /lib/modules/$(uname -r)/ -iname '*.ko' | wc -l  # 4291
    lsmod | wc -l                                         # 99
    
    Run Code Online (Sandbox Code Playgroud)

    几乎没有加载任何东西。

  • 微内核不是一回事。只需 10 秒钟查看链接维基百科页面的主要图像,就会突出显示它们的设计方式完全不同。

    Linux 驱动程序是内部化的(主要是动态加载的模块),而不是用户空间,并且文件系统同样是内部的。为什么这比使用外部驱动程序更糟糕?为什么微型更适合通用计算?


评论再次强调你没有得到它。如果您想在离散硬件(例如航空航天、TiVo、平板电脑等)上部署 Linux,您可以将其配置为仅构建您需要的驱动程序。您可以在桌面上使用make localmodconfig. 您最终会得到一个零灵活性的小型专用内核构建。

对于像 Ubuntu 这样的发行版,一个 40MB 的内核包是可以接受的。不,擦洗,它实际上将 4000 多个浮动模块保留为包的大规模存档和下载方案更可取。它为他们使用更少的磁盘空间,更容易在编译时打包,更容易存储并且对他们的用户(拥有一个可以正常工作的系统)更好。

未来似乎也不是问题。CPU 速度、磁盘密度/定价和带宽改进的速度似乎比内核的增长快得多。10 年后的 200MB 内核包不会是世界末日。

它也不是单向街。如果不维护,代码确实会被踢出。

  • @JonathanLeaders 你永远不会在嵌入式系统上为桌面运行内核 **configured**。我们的嵌入式系统有 *13* 个模块,并删除了我们不需要的所有硬件支持(以及大量其他自定义)。停止将台式机与嵌入式系统进行比较。Linux 运行良好,因为它支持所有内容,并且可以自定义为仅*包含*您关心的内容。这些 4k 模块在台式机系统上真的很棒:当我的最后一台笔记本电脑坏了时,我只是将硬盘驱动器放在一台更新的笔记本电脑中,*一切正常*。 (7认同)
  • 这个原本很好/有价值的答案受到明显愤怒和好斗的语气的影响。-1. (6认同)
  • 关注点主要是嵌入式系统。正如您所展示的,您有 4,000 个模块未在您自己的系统上使用。在一些小型机器人或航空航天应用中,(阅读:不是通用计算)这将是不可接受的浪费。 (2认同)
  • @JonathanLeaders 我认为您可以安全地删除它们。在桌面安装中,如果您突然在 USB 端口插入某些东西或更改某些硬件配置等,它们就在那里。 (2认同)

Ale*_*lex 23

Linux tinyconfig 编译源行数 tinyconfig 气泡图svg(小提琴)

从内核构建创建 json 的shell 脚本,与http://bl.ocks.org/mbostock/4063269 一起使用


编辑:原来unifdef有一些限制(-I被忽略和-include不受支持,后者用于包含生成的配置标头)此时使用cat并没有太大变化:

274692 total # (was 274686)
Run Code Online (Sandbox Code Playgroud)

脚本和程序已更新。


除了驱动程序、架构等,还有很多条件代码编译与否取决于选择的配置,代码不一定在动态加载的模块中,而是内置在核心中。

因此,下载了linux-4.1.6 源,选择了tinyconfig,它不启用模块,老实说,我不知道它启用了什么或用户在运行时可以用它做什么,无论如何,配置内核:

# tinyconfig      - Configure the tiniest possible kernel
make tinyconfig
Run Code Online (Sandbox Code Playgroud)

构建内核

time make V=1 # (should be fast)
#1049168 ./vmlinux (I'm using x86-32 on other arch the size may be different)
Run Code Online (Sandbox Code Playgroud)

内核构建过程留下*.cmd用命令行调用的隐藏文件,也用于构建.o文件,处理这些文件并提取script.sh下面的目标和依赖项副本,并将其与find一起使用:

find -name "*.cmd" -exec sh script.sh "{}" \;
Run Code Online (Sandbox Code Playgroud)

这为.o名为的目标的每个依赖项创建一个副本.o.c

.c 代码

find -name "*.o.c" | grep -v "/scripts/" | xargs wc -l | sort -n
...
   8285 ./kernel/sched/fair.o.c
   8381 ./kernel/sched/core.o.c
   9083 ./kernel/events/core.o.c
 274692 total
Run Code Online (Sandbox Code Playgroud)

.h 标头(已清理)

make headers_install INSTALL_HDR_PATH=/tmp/test-hdr
find /tmp/test-hdr/ -name "*.h" | xargs wc -l
...
  1401 /tmp/test-hdr/include/linux/ethtool.h
  2195 /tmp/test-hdr/include/linux/videodev2.h
  4588 /tmp/test-hdr/include/linux/nl80211.h
112445 total
Run Code Online (Sandbox Code Playgroud)


小智 11

从一开始就在 Tananbaum 和 Torvalds 之间公开辩论单片内核的权衡。如果您不需要为所有事情都进入用户空间,那么内核的接口可以更简单。如果内核是单体的,那么它可以在内部进行更优化(也更凌乱!)。

很长一段时间以来,我们已经将模块作为折衷方案。它正在继续使用 DPDK(将更多网络功能移出内核)之类的东西。添加的内核越多,避免锁定就越重要;所以更多的东西会进入用户空间,内核会缩小。

请注意,单片内核不是唯一的解决方案。在某些架构上,内核/用户空间边界并不比任何其他函数调用更昂贵,这使得微内核具有吸引力。

  • 顺便说一句,这里有更多关于您提到的辩论的资源:https://en.wikipedia.org/wiki/Tanenbaum%E2%80%93Torvalds_debate (3认同)