你知道kernel version的实现原理和细节吗

引言

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确认一下:

89939cc258658608f1be7b4b9f8cd8e0.png

其中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」获取学习资料网盘链接。

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

bca30bec3285c4358ad0838c05df94bb.png

嵌入式Linux

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465273.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 为你的应用程序添加快捷方式【优先级高的快捷方式】

有人会说,快捷方式,不是安装完应用程序后,长按应用程序的ICON然后将它拖到桌面上不就行了吗?没错,这样是一种方法,但这种方法有一个缺点,看图吧: 如上图,如果我们长按桌面…

icinga2 php模块,在Ubuntu 18.04系统上安装Icinga2监视工具的方法

本文介绍在Ubuntu 18.04系统上安装Icinga2监视工具的方法,使用Icinga 2可以监控:服务器资源、网络服务、网络设备。简介Icinga 2是一个开源,可扩展和可扩展的监视工具,可检查网络资源的可用性,通知用户中断&#xff0c…

面试官问:malloc(0)时程序会返回什么?

今天跟大家找了篇文章,主要是一个面试中的有趣问题,其实有些问题在开发中没有遇到过会很难回答出来,如果在面试过程中回答正确,皆大欢喜,拿到offer的概率更大;回答不出来也不要信口开河,面试官主…

考研失败了,怎么办?

有读者提到这个问题,顺带回答下。我没有考研过,但是身边有很多研究生和博士,额,还有很多海外留学的博士。前天我们有外部厂商来公司讨论合作,领导让我跟着一起介绍项目,对方的人问了一句:“你们…

晒一波工程师的工位,你喜欢哪种?

程序员的圈子啊那是十分神秘,又令人着迷的。每天的工作就是对着电脑,那他们的工作是如何的呢?我们来品一品(PS:后面奉上各位大佬的桌面,别走开哦)↓↓↓最最常见的普通版:升级版&…

彻底搞懂系统调用

在应用程序开发过程中经常会进行IO设备的操作,比如磁盘的读写,网卡的读写,键盘,鼠标的读入等,大多数应用开发人员使用高级语言进行开发,例如C,C,java,python等&#xff0…

Kubernetes(k8s)集群部署(k8s企业级Docker容器集群管理)系列目录

0、目录 整体架构目录:ASP.NET Core分布式项目实战-目录 k8s架构目录:Kubernetes(k8s)集群部署(k8s企业级Docker容器集群管理)系列目录 一、感谢 在此感谢.net core社区的帮助。感谢。 二、系列部署目录 0、部署环境规划 1、自签T…

每天都用手机,你对麦克风了解吗?

简 介: 通过对于实际驻极体MIC进行拆解,看到其中的结构,对比起工作原理,实在令人难以想象它的工作机制是可行的,尽管现在它已经广泛应用在周围很多电子设备中。关键词: 驻极体,MIC01 驻极体话筒…

好了,我不想回深圳了~

国庆节算长假,一共七天,高速免费。如果一个人,待在家里睡上七天,可能我在第二天就会特别无聊,想找事情做,因为国庆离开深圳的人很多,我曾经有一次放假去球场打球,结果很失落&#xf…

开源微信管家平台——JeeWx 捷微4.0 微服务版本发布,全新架构,全新UI,提供强大的图文编辑器...

JeeWx捷微4.0 微服务版本发布^_^ 换代产品(全新架构,全新UI,提供强大的图文编辑器) JEEWX 从4.0版本开始,技术架构全新换代,采用微服务架构,插件式开发,每个业务模块都是独立的JAR…

手把手教用XNA开发winphone7游戏(三)

XNA Game Studio 游戏循环 在这部分中您将重点两剩余部分的游戏 — — 重写Update 和 Draw 功能。有些大大可能看过相关微软的训练包,我这里主要是帮一些初学者。希望各位大大包含,毕竟文章发出来还是有工作量的。大家觉得有用就好,要是没有耽…

我的代码很好,不需要写注释

作者 | Sheetal 译者 | 弯月 责编 | 王晓曼 有时候,我们会写一些非常有创意的注释,而有些注释确实让人不得不佩服 程序员的想象力。看到下面这些注释,相信每个人都会捧腹大笑。【1】#想了解递归,请参见文件末尾 . .(代…

陈潇冰php,webpack4.x入门到进阶

课程详情(本课程所涉及内容)1. webpack是什么?webpack的作用2. webpack的整体构成3. webpack-cli、package.json4. 开发环境(development)和生产环境(production),npm安装包的方式,-D、-S5. 跑一跑webpack6. webpack.config.js配置总览7. 入口配置形式&…

SpringBoot开发案例之整合Spring-data-jpa

什么是spring-data 为了简化程序与数据库交互的代码,spring提供了一个现成的dao层框架,spring家族提供的spring-data适用于关系型数据库和nosql数据库 什么是jpa JPA全称为Java持久性API(Java Persistence API),JPA是j…

细说路由器

介绍以太网交换机工作在第二层即数据链路层,用于在同一网络内部转发以太网帧。但是,当源和目的IP地址位于不同网络时,以太网帧必须发送给路由器。路由器负责在不同网络间传输报文,通过路由表来决定最佳转发路径。当主机将报文发送…

乔布斯,影响了一个时代的人

2011年10月5日,苹果公司的创始人史蒂夫乔布斯,因患胰腺神经内分泌肿瘤病逝,享年56岁,一代传奇人物,与世长辞乔布斯被认为是计算机业界与娱乐业界的标志性人物,同时人们也把他视作麦金塔计算机、iPod、iPhon…

C++ 版本ORM访问数据库之ODB访问oracle的Demo(三)

ODB的组成部分: 1: 操作系统的ODB编译器 2: odb核心库libodb 3: 各种数据库的相关链接库 使用ODB访问数据需要的库和头文件(不懂, 请看https://www.cnblogs.com/hul201610101100/p/9482311.html): lib库: odb-oracle-d.lib, odb-d.lib (由libodb-oracle-2.4.0编译成功后产生的l…

平均年薪60.8万,Linux开发拿下这个证书有多吃香?

互联网行业竞争一年比一年严峻,随着互联网的发展和进步,很多人都是想要进军到编程行业中去,作为工程师的我们唯有不停地学习,不断的提升自己才能保证自己的核心竞争力,打破内卷。从而拿到更好的薪水,进入心…

Linux新手必须掌握的命令(2)

一、输入输出重定向 输入重定向是指把文件导入到命令中,而输出重定向则是指把原本要输出到屏幕的数据信息写入到指定文件中。 在日常的学习和工作中,相较于输入重定向,我们使用输出重定向的频率更高。 所以又将输出重定向分为了标准输出重定向…

极限编程与敏捷开发(4)

解决方案一: 下面图1是一种最简单的解决方案,Switch对象可以轮询真实开关的状态,并且可以发送相应的turnOn和turnOff消息给Light。 图1解决方案二: 上面这个设计违反了两个设计原则:依赖倒置原则(DIP)和开放封闭原则(O…