uname从何处获取其信息?


40

uname真正从何处获得其信息?

我认为这应该很简单。不幸的是,我找不到任何仅包含该信息的标头。

假设有人想要将uname/ 的基本输出uname -s 从更改Linux为其他内容(本质上是重命名内核)。

他/她将如何以正确的方式进行操作(即更改源)?

Answers:


26

uname实用程序从uname()系统调用中获取其信息。它填充这样的结构(请参阅参考资料man 2 uname):

       struct utsname {
           char sysname[];    /* Operating system name (e.g., "Linux") */
           char nodename[];   /* Name within "some implementation-defined
                                 network" */
           char release[];    /* Operating system release (e.g., "2.6.28") */
           char version[];    /* Operating system version */
           char machine[];    /* Hardware identifier */
       #ifdef _GNU_SOURCE
           char domainname[]; /* NIS or YP domain name */
       #endif
       };

这直接来自正在运行的内核。我将承担所有的信息是硬编码到它,也许除了domainname(和事实证明,还nodenamemachinerelease见注释)。发行字符串from uname -r可以在编译时通过配置进行设置,但是我非常怀疑sysname字段可以使用-它是Linux内核,没有任何可能的理由使用它。

但是,由于它是开源的,因此您可以更改源代码并重新编译内核以使用所需的任何sysname。


2
domainname字段由domainname命令使用setdomainname系统调用设置。同样,该nodename字段由hostname命令使用sethostname系统调用设置。(nodename/ hostname值可以存储在/etc/nodename。)
Scott

2
这无关紧要,这个问题问在哪里进行更改。所以是的,该uname命令从系统调用中获取其信息。系统调用从何处获取其信息?(答案由其他发布者在此处提供:在编译时已在内核中进行了硬编码。)
Gilles'SO-别再邪恶了

@吉尔斯:是什么事?如果答案是“这里有其他消息提供者:它被硬编码到内核中……”,请注意我说的完全相同:“这直接来自运行中的内核。我认为所有信息都是困难的编码...由于它是开源的,因此您可以更改源代码并重新编译内核以使用所需的任何sysname,不是配置选项
goldilocks 2014年

2
@goldilocks为什么会machine改变?它可能不会硬编码到内核中,因为它可能会适应硬件,但是可以肯定的是,它将在引导时设置,并且此后不会更改。但是不能:可以为每个进程设置(例如,报告i686到x86_64上已处理的32位)。顺便说一句,release也可以在某种程度上针对每个过程进行自定义(尝试setarch i686 --uname-2.6 uname -a)。
吉尔斯(Gillles)“所以-别再邪恶了”

1
@Gilles我已经编辑了machinenodenamerelease在问题中引用了注释。再一次,问题实际上不是所有这些领域。
goldilocks 2014年

26

数据存储在init / version.c中:

struct uts_namespace init_uts_ns = {
        .kref = {
                .refcount       = ATOMIC_INIT(2),
        },
        .name = {
                .sysname        = UTS_SYSNAME,
                .nodename       = UTS_NODENAME,
                .release        = UTS_RELEASE,
                .version        = UTS_VERSION,
                .machine        = UTS_MACHINE,
                .domainname     = UTS_DOMAINNAME,
        },
        .user_ns = &init_user_ns,
        .proc_inum = PROC_UTS_INIT_INO,
};
EXPORT_SYMBOL_GPL(init_uts_ns);

字符串本身在include / generated / compile.h中:

#define UTS_MACHINE "x86_64"
#define UTS_VERSION "#30 SMP Fri Apr 11 00:24:23 BST 2014"

并在include / generate / utsrelease.h中:

#define UTS_RELEASE "3.14.0-v2-v"

UTS_SYSNAME可以在include / linux / uts.h中定义

#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

或作为makefile中的#define

最后,主机名和域名可以由/ proc / sys / kernel / {hostname,domainname}控制。这些是每个UTS命名空间:

# hostname
hell
# unshare --uts /bin/bash
# echo test > /proc/sys/kernel/hostname 
# hostname
test
# exit
# hostname
hell

通常,这是一个很好且完整的答案,但直接回答发布者的问题可能是值得的。我相信这将-更改相关文件中的相关条目并重新编译。您写了“或作为makefile中的#define”。你能详细说明吗?
Faheem Mitha 2014年

为+1 unshare。直到今天我还是以某种方式错过了这个命令。谢谢!
Tino

并且include/generated/compile.h由产生scripts/mkcompile_hunix.stackexchange.com/a/485962/32558
西罗桑蒂利新疆改造中心法轮功六四事件

8

Linux Cross Reference和您提到的帮助下/proc/sys/kernel/ostype,我跟踪ostypeinclude / linux / sysctl.h,其中有一条评论说,通过调用添加了名称register_sysctl_table

那么,是,从叫?一个地方是kernel / utsname_sysctl.c,其中包括include / linux / uts.h,我们在其中找到:

/*
 * Defines for what uname() should return 
 */
#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

因此,正如内核文档所述:

调整这些值的唯一方法是重建内核

:-)


6

如其他地方所述,该信息与unamesyscall一起提供,该信息在运行的内核中进行了硬编码。

通常在通过Makefile编译新内核时设置版本部分:

VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION =

当我有时间玩内核时,我曾经在EXTRAVERSION中添加一些东西。给了你uname -r 诸如此类的东西3.4.1-mytestkernel

我不完全了解它,但是我认为其余信息Makefile也在第944行附近设置:

# ---------------------------------------------------------------------------

# KERNELRELEASE can change from a few different places, meaning version.h
# needs to be updated, so this check is forced on all builds

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

define filechk_version.h
    (echo \#define LINUX_VERSION_CODE $(shell                         \
    expr $(VERSION) \* 65536 + 0$(PATCHLEVEL) \* 256 + 0$(SUBLEVEL)); \
    echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';)
endef

$(version_h): $(srctree)/Makefile FORCE
    $(call filechk,version.h)

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

PHONY += headerdep
headerdep:
    $(Q)find $(srctree)/include/ -name '*.h' | xargs --max-args 1 \
    $(srctree)/scripts/headerdep.pl -I$(srctree)/include

对于其余数据,sys_uname系统调用是使用宏(以非常复杂的方式)生成的, 如果您喜欢冒险,可以从这里开始。

更改此类信息的最佳方法可能是编写一个内核模块以覆盖unamesyscall。我从未这样做过,但是您可以在此页面的第4.2节中找到信息(对不起,没有直接链接)。但是请注意,该代码引用的是一个相当老的内核(现在的Linux内核具有uts名称空间,无论它们的含义如何),因此您可能需要对其进行很多更改。


感谢大家。我已经知道这与uname有关。但是,我无法理解的是如何在源中定义字符串“ Linux”的方式和位置。我所知道的是在运行时可以在哪里找到该信息(包含在/ proc / sys / kernel / ostype中)。我想说,弄清楚内核本身究竟如何知道它的专有名称将是更有趣的事情之一。
user237251 2014年

@ user237251在字符串上下文中的内核源代码中有多少个“ Linux”一词实例?如果不是很多,您可以检查文本搜索的结果,然后查看导致搜索结果的位置。
JAB

@JAB太多了。幸运的是,kernelnewbies.org上的某人帮助我解决了“谜”。Linux从/include/Linux/uts.h获得其sys名称。参见此处:lxr.free-electrons.com/source/include/linux/uts.h?v=3.10
user237251 2014年

2

虽然我在源代码中找不到任何东西可以表明这一点,但我相信它使用了uname syscall。

man 2 uname

应该告诉您更多有关它的信息。如果真是这样,那就直接从内核获取信息并进行更改可能需要重新编译。

您可以更改二进制文件,以便自己做任何想要的事情,只需用w / e程序覆盖它即可。缺点是某些脚本依赖于该输出。


3
如果您这样做strace uname,它将确认使用了uname系统调用。
Graeme 2014年

1

更改uname的正确方法是更改​​编译标头,然后像其他建议的那样重新编译。但是我不知道为什么当您可以做这样的事情时,为什么您会想遇到那么多麻烦,

alias uname 'uname \\!* | sed s/2.6.13/2.6.52/'

甚至

alias uname 'echo whatever'

0

Rmano的答案让我有些困惑,但是通过在内核源目录的命令行中传递该Q=选项,更容易发现真正的魔力make。它使您可以查看详细信息,其中之一是对脚本的调用:echo "4.4.19$(/bin/sh ./scripts/setlocalversion .)"。执行相同的代码片段将给出内核发行号4.4.19-00010-ge5dddbf。如果查看脚本,它将确定版本控制系统中的编号,并通过运行该脚本bash -x显示确切的过程:

+++ git rev-parse --verify --short HEAD
++ head=e5dddbf
+++ git describe --exact-match
++ '[' -z '' ']'
++ false
+++ git describe
++ atag=release/A530_os_1.0.0-10-ge5dddbf
++ echo release/A530_os_1.0.0-10-ge5dddbf
++ awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
++ git config --get svn-remote.svn.url
++ git diff-index --name-only HEAD
++ grep -qv '^scripts/package'
++ return
+ res=-00010-ge5dddbf
+ echo -00010-ge5dddbf
-00010-ge5dddbf

这显示给我的是,如果我想构建一个内核模块以与运行中的内核一起工作,那么我使用的是错误的标记发行版和错误的提交。我需要修复该问题,并至少构建DTB(make dtbs)才能创建具有正确版本号的生成文件。


事实证明,即使那还不够。我不得不替换scripts/setlocalversion为一个简单的方法:

#!/bin/sh
echo -0710GC0F-44F-01QA

然后重建自动生成的文件:

make Q= ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs

然后我可以构建Derek Molloy的示例驱动程序,并insmod成功完成了该工作。显然,关于Module.symvers不在场的警告并不重要。Linux用来确定模块是否可以正常工作的所有工具都是该localversion字符串。


0

scripts/mkcompile_h

在v4.19中,这是一个生成的文件,include/generated/compile.h其中包含以下几个有趣的部分/proc/versionhttps : //github.com/torvalds/linux/blob/v4.19/scripts/mkcompile_h

  • #<version>部分来自.version构建树上的文件,只要发生链接(需要更改文件/配置),该树就会递增scripts/link-vmlinux.sh

    可以被KBUILD_BUILD_VERSION环境变量覆盖:

    if [ -z "$KBUILD_BUILD_VERSION" ]; then
        VERSION=$(cat .version 2>/dev/null || echo 1)
    else
        VERSION=$KBUILD_BUILD_VERSION
    fi
    
  • 日期只是一个原始date电话:

    if [ -z "$KBUILD_BUILD_TIMESTAMP" ]; then
        TIMESTAMP=`date`
    else
        TIMESTAMP=$KBUILD_BUILD_TIMESTAMP
    fi
    

    同样,用户名来自whoamiKBUILD_BUILD_USER),主机名来自hostnameKBUILD_BUILD_HOST

  • 编译器版本来自gcc -v,似乎无法控制。

这是如何更改问题的内容版本:https : //stackoverflow.com/questions/23424174/how-to-customize-or-remove-extra-linux-kernel-version-details-shown-at-boot

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.