引言

kernel 启动时通常会看到下面第二行信息的内容,它们代表了当前 kernel 的版本、编译工具版本、编译环境等信息。

Booting Linux on physical CPU 0x0
Linux version 5.4.124+ (funny@funny) (gcc version 6.5.0 (Linaro GCC 6.5-2018.12)) #30 SMP Sat Sep 11 11:10:28 CST 2021
......

要知道,系统启动过程中的任何一条打印信息,都是经过了无数次讨论和验证才呈现在大家的面前。看似无关紧要的一条信息,但背后却隐藏着非常有趣的故事。

为什么要打印version信息

当系统启动之后有很多种方式能够确定内核版本号信息,在嵌入式或安卓 kernel 系统下,查看版本信息:

  • uname

[root@cpu ]# uname -a
Linux cpu 5.4.124+ #30 SMP Sat Sep 11 11:10:28 CST 2021 armv7l GNU/Linux
[root@cpu ]#
  • proc/version

[root@cpu ]# cat /proc/version
Linux version 5.4.124+ (funny@funny) (gcc version 6.5.0 (Linaro GCC 6.5-2018.12)) #30 SMP Sat Sep 11 11:10:28 CST 2021
[root@cpu ]#

在发行版 linux 系统环境下,还可以用下面的命令查看版本信息:

  • hostnamectl

funny@funny:~$ hostnamectlStatic hostname: funnyIcon name: computer-vmChassis: vm...Virtualization: vmwareOperating System: Ubuntu 16.04.7 LTSKernel: Linux 4.15.0-142-genericArchitecture: x86-64
funny@funny:~$
  • lsb_release

No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.7 LTS
Release:        16.04
Codename:       xenial

以上方法都是系统启动正常、加载完文件系统之后使用的。
那么,系统启动过程中是否有必要打印内核版本信息呢?答案是完全有必要。

例如下面列出的几种应用场景:

  1. SoC 芯片的 kernel 适配

  2. 可装载驱动程序调试

  3. 多分支内核版本加载

  4. 内核伪装

kernel version实现原理

kernel version这条打印信息来源于start_kernl()中的linux_banner字符串。

asmlinkage __visible void __init start_kernel(void)
{
...boot_cpu_init();page_address_init();pr_notice("%s", linux_banner);
...

这里的banner好比是ubuntu系统里的ssh登录横幅一样,呈现了系统的一些基本信息。

Welcome to Ubuntu 16.04.7 LTS (GNU/Linux 4.15.0-142-generic x86_64)* Documentation:  https://help.ubuntu.com* Management:     https://landscape.canonical.com* Support:        https://ubuntu.com/advantage* Super-optimized for small spaces - read how we shrank the memoryfootprint of MicroK8s to make it the smallest full K8s around.https://ubuntu.com/blog/microk8s-memory-optimisation
...

banner字符串的定义位于init/version.c中,注意,它是一个只读字符串,不要去修改它。

const char linux_banner[] ="Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";

由以下几部分组成:

  • UTS_RELEASE
    对应"5.4.124+"

  • LINUX_COMPILE_BY
    对应"funny",我的编译机funny

  • LINUX_COMPILE_HOST
    对应"funny",我的编译机Host是funny

  • LINUX_COMPILER
    对应"gcc version 6.5.0 (Linaro GCC 6.5-2018.12"

  • UTS_VERSION
    对应"#30 SMP Sat Sep 11 11:10:28 CST 2021"
    UTS:Unix Time Stamp,从这个名字可以看出linux中的UNIX印记。

接下来对这些字符串逐条进行解析

上面这些宏的第一级定义位于./scripts/mkcompile_h文件中。

{ echo /\* This file is auto generated, version $VERSION \*/if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fiecho \#define UTS_MACHINE \"$ARCH\"echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\"echo \#define LINUX_COMPILE_BY \"`echo $LINUX_COMPILE_BY | $UTS_TRUNCATE`\"echo \#define LINUX_COMPILE_HOST \"`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\"echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | grep ' version ' | sed 's/[[:space:]]*$//'`\"
} > .tmpcompile

UTS_VERSION

UTS_VERSION="#$VERSION"
CONFIG_FLAGS=""
if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi
if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi
if [ -n "$PREEMPT_RT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT_RT"; fi
UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP"

LINUX_COMPILE_BY
LINUX_COMPILE_HOST
LINUX_COMPILER

if test -z "$KBUILD_BUILD_USER"; thenLINUX_COMPILE_BY=$(whoami | sed 's/\\/\\\\/')elseLINUX_COMPILE_BY=$KBUILD_BUILD_USERfiif test -z "$KBUILD_BUILD_HOST"; thenLINUX_COMPILE_HOST=`hostname`elseLINUX_COMPILE_HOST=$KBUILD_BUILD_HOSTfi

UTS_RELEASE --- 重点分析这个宏的来源
这是一个在kernel顶层Makefile中定义的一个宏,如下:

uts_len := 64
define filechk_utsrelease.hif [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2;    \exit 1;                                                         \fi;                                                               \echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"
endef

提高make的打印等级可以看到,上面的脚本内容经过翻译之后如下:

if [ `echo -n "5.4.124+" | wc -c ` -gt 64 ]; then echo '"5.4.124+" exceeds 64 characters' >&2; exit 1; fi; echo \#define UTS_RELEASE \"5.4.124+\"; }

现在可以确定KERNELRELEASE就是从kernel.release文件中获取到的。打开kernel.release确认一下:

其中KERNELRELEASE对应5.4.124+

KERNELRELEASE又是怎么来的呢?
KERNELRELEASE同样也是在Makefile中定义的、自动生成的字符串,它可以在多个地方被修改。在Makefile中查找KERNELRELEASE字符串,看见它是由下面这条命令生成的。

KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)

这条命令里的2>/dev/null的含义是:若cat失败即没有取到文件内容,那么将错误信息输出到黑洞文件。
通过下面命令验证:

funny@funny:~$ cat funny.txt
funny? yeah
funny@funny:~$ B=$(cat funny.txt 2> /dev/null)
funny@funny:~$ echo $B
funny? yeah
funny@funny:~$

一切准备就绪之后,通过下面的代码将UTS_RELEASE更新到utsrelease.h中。

1195 include/generated/utsrelease.h: include/config/kernel.release FORCE
1196         $(call filechk,utsrelease.h)

其中filechk的定义位于scripts/Kbuild.include

define filechk$(Q)set -e;                                             \mkdir -p $(dir $@);                                     \trap "rm -f $(dot-target).tmp" EXIT;                    \{ $(filechk_$(1)); } > $(dot-target).tmp;               \if [ ! -r $@ ] || ! cmp -s $@ $(dot-target).tmp; then   \$(kecho) '  UPD     $@';                        \mv -f $(dot-target).tmp $@;                     \fi
endef

而utsrelease.h中内容如下:

linux$ cat ./obj/include/generated/utsrelease.h
#define UTS_RELEASE "5.4.124+"
linux$

这就是我们内核启动过程中打印出来的kernel version信息。


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

嵌入式Linux

微信扫描二维码,关注我的公众号

你知道kernel version的实现原理和细节吗相关推荐

  1. Docker Desktop requires a newer WSL kernel version.

    问题描述: Docker Desktop requires a newer WSL kernel version. 问题截图: 问题原因: WSL不是最新版. 解决方案: 适用于 Linux 的 Wi ...

  2. Kafka的结构、特点和原理(细节)

    一.Kafka的结构 重点在于Broker的结构.每一个消息归宿于特定的Broker下的特定的Topic下特定的Partion.而这些对应关系则被ZooKeeper记录下来. 二.特点 异步通信 一种 ...

  3. LSM零知识学习六、插桩原理实现细节(4)

    接前一篇文章:LSM零知识学习五.插桩原理实现细节(3) 本文内容参考: LSM(Linux Security Modules)框架原理解析_lsm linux_pwl999的博客-CSDN博客 特此 ...

  4. LSM零知识学习四、插桩原理实现细节(2)

    接前一篇文章:LSM零知识学习三.插桩原理实现细节(1) 本文内容参考: LSM(Linux Security Modules)框架原理解析_lsm linux_pwl999的博客-CSDN博客 特此 ...

  5. Android version and Linux Kernel version

    一.Android版本与Linux内核的关系 英文名 中文名 版本号 API level 发布时间 内核版本     1.1    2008.9    Cucake 纸杯蛋糕 1.5  3 , NDK ...

  6. CF1379F1 Chess Strikes Back (easy version)(鸽笼原理、线段树)

    解析 很神奇的一道题 关键在于把22的正方形看成一个单位的转化 由于每个22最多有一个国王 因此每个2*2都一定有一个国王 这是本题的关键 个人感觉这个思想很像鸽笼原理 至于后面的线段树就水到渠成了 ...

  7. 以太坊BIP39助记词到公钥地址的原理与细节

    以太坊基础-你真的懂吗 以太坊私钥 eg: fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19 由256位:不考虑0x前缀 ...

  8. WaveNet相关原理及细节介绍

    Neural vocoder层出不穷, 但是WaveNet仍然是重中之重.作为后续变种的基础和参考对比目标,还是需要先对WaveNet进行比较深入的了解,才能为后续演变后的vocoder的学习打下基础 ...

  9. 「实战」蘑菇街 PC 端首页,瀑布流布局的实现原理与细节技巧

    作者:蘑菇街前端团队 链接:https://juejin.im/post/5e05acf0f265da33d158a1b1 零.介绍 这篇文章主要是介绍网站页面瀑布流布局的实现,主要包括: 瀑布流是什 ...

最新文章

  1. 如何基于 String 实现同步锁?
  2. 利用Runtime类,来操作电脑关机。。
  3. POJ - 3417 Network(树上差分)
  4. 深度学习(part1)--机器学习及深度学习基础
  5. html dom 修改,HTML DOM - 修改
  6. python调用c++的库传递二级指针
  7. linux内核开源不能仿照_Linux内核开发,开源生产力工具,使用Google应用程序创建自动日历等
  8. pvr转png工具_图片如何转换格式?最好用的3个转换工具都在这了
  9. Android开发笔记(二十一)横幅轮播页Banner
  10. 前端实现街道地图_来自法国的注重保护个人隐私的开源地图
  11. openpythonxl_常用模块之openpyxl (python3入门)
  12. ubuntu 14安装droidcam调用手机摄像头
  13. rose ha 的使用
  14. 计算机接口学平时作业,西电《计算机接口与通信技术》平时作业[教学作业]
  15. WebRTC 报错:Failed to set remote offer sdp: Called with SDP without DTLS fingerprint
  16. 数据安全需要做什么?
  17. 信阳师范学院计算机老师,信阳师范学院计算机与信息技术学院导师教师师资介绍简介-樊建伟...
  18. Go入门系列(十七) go并发之基于共享变量的并发
  19. 帝国竞争算法(Imperialist Competitive Algorithm, ICA)
  20. 可修改性(Mutability)

热门文章

  1. inotify-tools、sersync配置及压力测试
  2. ehcache memcache redis 三大缓存男高音
  3. 微软的云笔记:OneNote+SkyDrive
  4. flex柱状图和折线图的混合图使用
  5. 阿里云安装Quantaxis
  6. 使用phpunit新建项目
  7. 下拉选择_在管理Excel中实现联动下拉选择
  8. Linux5观察doc目录并截屏,linux截屏命令
  9. python 打包exe_python如何封装为exe
  10. 安装云端服务器操作系统,安装云端服务器操作系统