web-dev-qa-db-ja.com

unameはどこから情報を取得しますか?

Unameは実際にどこから情報を取得しますか?

これは簡単なはずです。残念ながら、その情報だけを含むヘッダーは見つかりません。

誰かがuname/uname -sの基本的な出力をLinuxから別のものに変更したいとします(本質的には、カーネルの名前を変更します)。

彼/彼女はどのようにそれを適切な方法で行うのですか(つまり、ソースを変更しますか)?

42
user237251

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もコメントを参照してください)。 _uname -r_からのリリース文字列は、コンパイル時に構成を介して設定できますが、sysnameフィールドが設定できるかどうかは疑問です。これはLinuxカーネルであり、他に何かを使用する理由は考えられません。

ただし、オープンソースであるため、ソースコードを変更してカーネルを再コンパイルし、必要なsysnameを使用できます。

26
goldilocks

データは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/generated/utsrelease.h内:

#define UTS_RELEASE "3.14.0-v2-v"

UTS_SYSNAMEはinclude/linux/uts.hで定義できます

#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

またはメイクファイルの#defineとして

最後に、ホスト名とドメイン名は/ proc/sys/kernel/{hostname、domainname}で制御できます。これらはUTSネームスペースごとです。

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

Linux相互参照/proc/sys/kernel/ostypeについての言及を活用して、ostypeinclude/linux/sysctl.h まで追跡しました。コメントは、名前がregister_sysctl_tableを呼び出すことによって追加されることを示しています。

それで、それはどこにあるのですか から呼び出されます ? 1つの場所は kernel/utsname_sysctl.c で、これには include/linux/uts.h が含まれています。

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

つまり、 kernel documentation のように:

これらの値を調整する唯一の方法は、カーネルを再構築することです

:-)

8
deltab

他の場所でコメントされているように、情報はuname syscallに付属しています。この情報は、実行中のカーネルにハードコードされています。

バージョン部分は通常、新しいカーネルをコンパイルするときに Makefile によって設定されます。

VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION =

カーネルをコンパイルして遊ぶ時間があったとき、私はEXTRAVERSIONでそこに何かを追加するのが常でした。 uname -r3.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 syscallはマクロを使用して(非常に複雑な方法で)生成されます。冒険したい場合は ここ から開始できます。

おそらく、そのような情報を変更する最良の方法は、uname syscallをオーバーライドするカーネルモジュールを作成することです。私はそれをしたことはありませんが、セクション4.2で情報 このページ内 を見つけることができます(申し訳ありませんが、直接リンクはありません)。ただし、そのコードはかなり古いカーネルを参照していることに注意してください(現在、Linuxカーネルにはuts名前空間があります)。そのため、おそらく大幅に変更する必要があります。

7
Rmano

これを示すソースは何も見つかりませんでしたが、unameのシステムコールを使用していると思います。

man 2 uname

それについてもっと教えてください。その場合は、カーネルから直接情報を取得しており、変更するにはおそらく再コンパイルが必要になります。

Unameのバイナリを変更して、好きなことを実行できます。w/ eプログラムで上書きしてください。一部のスクリプトという欠点は、その出力に依存しています。

2
Livinglifeback

Unameを変更する適切な方法は、コンパイルヘッダーを変更して、他の人が提案するように再コンパイルすることです。しかし、次のようなことができるのに、なぜそんなに多くのトラブルを経験する必要があるのか​​、私にはわかりません。

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

あるいは

alias uname 'echo whatever'
1
Saurabh

scripts/mkcompile_h

V4.19では、これはinclude/generated/compile.hを生成するファイルであり、/proc/versionのいくつかの興味深い部分が含まれています。 https://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

Rmano の答えで途中まで行きましたが、本当の魔法は、カーネルソースディレクトリのmakeコマンドラインで_Q=_オプションを渡すことで簡単に発見できます。詳細を確認できます。その1つがスクリプトの呼び出しです: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
jcomeau_ictx