Откуда uname получает информацию?

40

Откуда uname действительно получает информацию?

Я считаю, что это то, что должно быть простым. К сожалению, я не могу найти заголовок, содержащий только эту информацию.

Скажем, кто-то хотел изменить базовый вывод uname/ uname -s с Linuxчего-то другого (по сути, переименовав ядро).

Как он / она будет делать это надлежащим образом (то есть, смена источника)?

user237251
источник

Ответы:

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(и , как выясняется, также nodename, machineи releaseсмотрите комментарии). Строка релиза from uname -rможет быть установлена ​​через конфигурацию во время компиляции, но я очень сомневаюсь, что поле sysname может - это ядро ​​Linux, и для него нет никакой разумной причины использовать что-либо еще.

Однако, поскольку это открытый исходный код, вы можете изменить исходный код и перекомпилировать ядро, чтобы использовать любое имя, которое вы хотите.

лютик золотистый
источник
2
domainnameПоле задается domainnameкомандой, с помощью setdomainnameсистемного вызова. Аналогично, nodenameполе задается hostnameкомандой, используя sethostnameсистемный вызов. (Значение nodename/ hostnameможет храниться в /etc/nodename.)
Скотт
2
Это неактуально - вопрос задан, где это изменить. Так что да, unameкоманда получает информацию из системного вызова. И откуда системный вызов получает информацию? (Ответ, предоставленный другими авторами здесь: он жестко запрограммирован в ядре во время компиляции.)
Жиль "ТАК - перестать быть злым"
@ Жиль: Что не имеет отношения? Если ответ «предоставлен другими авторами здесь: он жестко запрограммирован в ядре ...», обратите внимание, я сказал то же самое: «Это исходит непосредственно от работающего ядра. Я бы предположил, что вся информация сложна» -кодированный в него ..., так как это открытый исходный код, вы можете изменить исходный код и перекомпилировать ядро, чтобы использовать любое имя sysname, которое вы хотите . Это не параметр конфигурации.
goldilocks
2
@goldilocks Почему бы machineкогда-нибудь измениться? Он не может быть жестко закодирован в ядре, потому что он может адаптироваться к аппаратному обеспечению, но, несомненно, тогда он будет установлен во время загрузки и не изменится после этого. Но нет: его можно установить для каждого процесса (например, для отчета i686в 32-битной обработке на x86_64). Кстати, releaseтакже может быть настроен для процесса в некоторой степени (попробуйте setarch i686 --uname-2.6 uname -a).
Жиль "ТАК - перестать быть злым"
1
@ Жиль я редактировал machine, nodenameи releaseв вопросе со ссылкой на комментарии. Опять же, вопрос был не о всех этих областях.
Златовласка
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 / generate / compile.h:

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

и в include / генерируется / 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 в make-файлах

Наконец, имя хоста и имя домена могут контролироваться / proc / sys / kernel / {имя хоста, имя домена}. Это для пространства имен UTS:

# hostname
hell
# unshare --uts /bin/bash
# echo test > /proc/sys/kernel/hostname 
# hostname
test
# exit
# hostname
hell
V13
источник
Как правило, это хороший и полный ответ, но, возможно, стоит ответить прямо на вопрос автора. Я считаю, что это будет равно - изменить соответствующую запись в соответствующем файле и перекомпилировать. Вы написали "или как #define в make-файлах". Можете ли вы уточнить?
Фахим Митха
+1 за unshare. Как-то мне удалось пропустить эту команду до сегодняшнего дня. Благодарность!
Тино
И include/generated/compile.hгенерируется scripts/mkcompile_h: unix.stackexchange.com/a/485962/32558
Сиро Сантилли 新疆 新疆 中 at 法轮功 六四 事件
8

С помощью Cross Reference Linux и ваше упоминание /proc/sys/kernel/ostype, я разыскал , ostypeчтобы включать / 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

Итак, как сказано в документации ядра :

Единственный способ настроить эти значения - перестроить ядро.

:-)

deltab
источник
6

Как unameотмечалось в другом месте, информация поступает с системным вызовом, который жестко запрограммирован в работающем ядре.

Часть версии обычно устанавливается при компиляции нового ядра с помощью 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системный вызов генерируется с помощью макросов (довольно запутанным образом), вы можете начать отсюда, если вы чувствуете себя авантюрным.

Вероятно, лучший способ изменить такую ​​информацию - написать модуль ядра для переопределения unameсистемного вызова; Я никогда этого не делал, но вы можете найти информацию на этой странице в разделе 4.2 (извините, прямой ссылки нет). Однако обратите внимание, что этот код ссылается на довольно старое ядро ​​(теперь ядро ​​Linux имеет utsпространства имен, что бы они ни значили), поэтому вам, вероятно, придется его сильно изменить.

Rmano
источник
Спасибо всем. Я уже знал, что это как-то связано с uname. Однако я не могу понять, как и где внутри источника определена строка «Linux». Все, что я знаю, это где я могу найти эту информацию во время выполнения (она содержится в / proc / sys / kernel / ostype). Я бы сказал, что выяснить, как именно само ядро ​​знает, что его собственное имя, было бы одной из самых интересных вещей.
user237251
@ user237251 Сколько экземпляров слова «Linux» встречается в источнике ядра в строковом контексте? Если это не так много, вы можете просто изучить результаты текстового поиска и посмотреть, к чему это вас приведет.
JAB
@JAB Слишком много. К счастью, кто-то на kernelnewbies.org помог мне разгадать «тайну». Linux получает свое имя системы из /include/Linux/uts.h. Смотрите здесь: lxr.free-electrons.com/source/include/linux/uts.h?v=3.10
user237251
2

Хотя я не смог найти ничего в источнике, чтобы указать на это, я считаю, что он использует системный вызов uname.

man 2 uname

должен рассказать вам больше об этом. Если это так, то получение информации напрямую из ядра и ее изменение, вероятно, потребует перекомпиляции.

Вы можете изменить двоичный файл так, чтобы он не создавался так, как вам хочется, просто напишите поверх него, пожалуйста, с помощью программы. Недостатком является то, что некоторые скрипты полагаются на этот вывод.

Livinglifeback
источник
3
Если вы это сделаете strace uname, он подтвердит, что unameсистемный вызов используется.
Грэм
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

тогда я мог собрать пример драйвера Дерека Моллоя и смог insmodуспешно. по-видимому, предупреждение об Module.symversотсутствии присутствия не имело значения. все, что Linux использовал, чтобы определить, будет ли работать модуль, это строка localversion.

jcomeau_ictx
источник
0

scripts/mkcompile_h

В версии 4.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
    

    и аналогично имя пользователя происходит из whoami( KBUILD_BUILD_USER) и имя хоста из hostname( KBUILD_BUILD_HOST)

  • Версия компилятора взята gcc -v, и, кажется, ее нельзя контролировать.

Вот как изменить версию этого вопроса: https://stackoverflow.com/questions/23424174/how-to-customize-or-remove-extra-linux-kernel-version-details-shown-at-boot

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
источник