0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

冬至子 来源:Linux与SoC 作者:Linux与SoC 2023-06-05 14:59 次阅读

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:~$ hostnamectl
   Static hostname: funny
         Icon name: computer-vm
           Chassis: vm
  ...
    Virtualization: vmware
  Operating System: Ubuntu 16.04.7 LTS
            Kernel: Linux 4.15.0-142-generic
      Architecture: 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 memory
   footprint 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 */"; fi

  echo \\#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"; then
         LINUX_COMPILE_BY=$(whoami | sed 's/\\\\/\\\\\\\\/')
 else
         LINUX_COMPILE_BY=$KBUILD_BUILD_USER
 fi
 if test -z "$KBUILD_BUILD_HOST"; then
         LINUX_COMPILE_HOST=`hostname`
 else
         LINUX_COMPILE_HOST=$KBUILD_BUILD_HOST
 fi

UTS_RELEASE --- 重点分析这个宏的来源

这是一个在kernel顶层Makefile中定义的一个宏,如下:

uts_len := 64
define filechk_utsrelease.h
        if [ `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信息。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式系统
    +关注

    关注

    41

    文章

    3551

    浏览量

    129093
  • Linux系统
    +关注

    关注

    4

    文章

    590

    浏览量

    27307
  • SoC芯片
    +关注

    关注

    1

    文章

    605

    浏览量

    34841
  • 驱动程序
    +关注

    关注

    19

    文章

    817

    浏览量

    47902
  • LSB算法
    +关注

    关注

    0

    文章

    7

    浏览量

    5800
收藏 人收藏

    评论

    相关推荐

    【电磁兼容标准解析分享】汽车电子零部件EMC标准解析---应该了解和知道细节(二)

    【电磁兼容标准解析分享】汽车电子零部件EMC标准解析---应该了解和知道细节(二)
    的头像 发表于 08-08 08:17 3741次阅读
    【电磁兼容标准解析分享】汽车电子零部件EMC标准解析---<b class='flag-5'>你</b>应该了解和<b class='flag-5'>知道</b>的<b class='flag-5'>细节</b>(二)

    编译自己的Linux内核(Kernel

    摘要:马上就会发现,也可以获得(get),配置(configure),编译(compile)和安装(install)属于自己的Linux内核(Kernel)。目录:引言安装内核源
    发表于 11-10 12:16

    flashlayout的kernel实现

    一般嵌入式系统都会通过uboot或者lk将flashlayout的信息传递给kernelkernel拿到这些flashlayout的信息后,将对应的每个partition mount成block device.我们看看kernel
    发表于 05-23 08:59

    为什么要打印version信息?kernel version实现原理是什么

    加载内核伪装kernel version实现原理kernel version这条打印信息来源于start_kernl()中的linux_ba
    发表于 06-21 16:18

    知道LINUX系统内核的实现原理是什么吗

    适配可负载驱动程序调试多分支版本加载内核伪装内核版本实现原理内核版本的标签打印信息 start_kernl(中的linux_kernl)字符串。这里的横幅比是 ubuntu 系统里的 ssh 基本横幅
    发表于 06-30 15:43

    看电机选幕布-告诉知道的产品细节

    看电机选幕布-告诉知道的产品细节 家用投影幕布以固定画框幕和电动式投影幕为主,其中电动幕由于安装容易,对装修影响较小
    发表于 02-10 10:28 7344次阅读

    linux内核kernel-api

    linux内核kernel-api,不知道从哪儿找的了,但是如果想要做内核编程,这是一部api函数详尽的工具书!!!五星推荐
    发表于 10-30 17:16 19次下载

    SYS_BIOS (TI-RTOS Kernel) v6.46 User's Guide

    "TI-RTOS Kernel" in some documents. This document describes SYS/BIOS 6.46, which is the version
    发表于 10-31 11:22 0次下载
    SYS_BIOS (TI-RTOS <b class='flag-5'>Kernel</b>) v6.46 User's Guide

    如何在OpenCL 2.0中实现Sierpinski Carpet Kernel

    在这个简短的视频中,您将学习如何在OpenCL 2.0中实现Sierpinski Carpet Kernel
    的头像 发表于 11-07 06:20 3147次阅读

    这些小细节知道申卡为什么被拒

      申请信用卡被拒绝了?微辰金服介绍这些小细节知道申卡为什么被拒。  申卡被拒的原因:  第一种:信用记录不良。这条犹如铁一般的纪律,对于“老赖”银行通常是无法容忍的。  第二种:银行记仇,申卡
    发表于 11-14 14:27 258次阅读

    知道linux kernel内存碎片防治技术?

    Linux kernel组织管理物理内存的方式是buddy system(伙伴系统),而物理内存碎片正式buddy system的弱点之一,为了预防以及解决碎片问题,kernel采取了一些实用技术,这里将对这些技术进行总结归纳。
    发表于 05-10 10:59 935次阅读

    知道linux kernel内存回收机制是怎样的?

    无论计算机上有多少内存都是不够的,因而linux kernel需要回收一些很少使用的内存页面来保证系统持续有内存使用。页面回收的方式有页回写、页交换和页丢弃三种方式:如果一个很少使用的页的后备存储器是一个块设备(例如文件映射),则可以将内存直接同步到块设备,腾出的页面可以被重用;
    发表于 05-10 11:37 898次阅读
    <b class='flag-5'>你</b><b class='flag-5'>知道</b>linux <b class='flag-5'>kernel</b>内存回收机制是怎样的?

    知道怎么在IP的kernel module里设置并使用IP interrupt吗

    有时我们需要为官方 IP 或者自己创建的 IP 生成 kernel module,然后在 linux kernel space 里使用 kernel module 来控制这个 IP。如果要使用 IP
    的头像 发表于 05-18 11:48 1444次阅读

    关于继电器应用那些知道细节

    继电器的应用,相信大家都知道,在电路中只要给它供电、断电也就可以工作了。本文讨论它的应用细节。 现在流行的接法 图中,继电器的线圈经过Q1作为开关,使其导通与断开。D1作为续流,消耗线圈中的能量
    的头像 发表于 11-01 09:55 2682次阅读
    关于继电器应用那些<b class='flag-5'>你</b>不<b class='flag-5'>知道</b>的<b class='flag-5'>细节</b>

    UBoot如何跳转Kernel

    首先我们知道kernel的镜像最开始是压缩的zImage格式的存在,然后Uboot有工具mkimage把其转换为uImage。 什么?不知道?好,那我先给你整两幅图瞅瞅,
    的头像 发表于 12-04 17:02 455次阅读
    UBoot如何跳转<b class='flag-5'>Kernel</b>