¿De dónde obtiene uname su información?


40

¿De dónde obtiene uname realmente su información?

Me imagino que esto es algo que debería ser sencillo. Desafortunadamente, no puedo encontrar ningún encabezado que contenga solo esa información.

Digamos que alguien quería cambiar la salida básica de uname/ uname -s de Linuxa otra cosa (esencialmente, renombrar el núcleo).

¿Cómo haría él / ella para hacerlo de la manera adecuada (es decir, cambiando la fuente)?

Respuestas:


26

La unameutilidad obtiene su información de la uname()llamada del sistema. Rellena una estructura como esta (ver 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
       };

Esto viene directamente del núcleo en ejecución. Yo asumiría toda la información está codificada en él, excepto tal vez domainname(y como resulta, también nodename, machiney release, ver comentarios). La cadena de lanzamiento, desde uname -r, se puede establecer a través de la configuración en tiempo de compilación, pero dudo mucho que el campo sysname pueda: es el kernel de Linux y no hay ninguna razón concebible para que use otra cosa.

Sin embargo, dado que es de código abierto, puede cambiar el código fuente y volver a compilar el núcleo para usar el nombre de sistema que desee.


2
El domainnamecampo lo establece el domainnamecomando, usando la setdomainnamellamada al sistema. Del mismo modo, el nodenamecampo lo establece el hostnamecomando, utilizando la sethostnamellamada al sistema. (El valor nodename/ hostnamepuede almacenarse en /etc/nodename.)
Scott

2
Esto es irrelevante: la pregunta se hizo para cambiar esto. Entonces sí, el unamecomando obtiene su información de una llamada al sistema. ¿Y de dónde obtiene su información la llamada del sistema? (Respuesta, proporcionada por otros carteles aquí: está codificado en el núcleo en el momento de la compilación.)
Gilles 'SO- deja de ser malvado'

@Gilles: ¿Qué es irrelevante? Si la respuesta es "proporcionada por otros carteles aquí: está codificada en el núcleo ..." tenga en cuenta que he dicho exactamente lo mismo: "Esto viene directamente del núcleo en ejecución. Supongo que toda la información es difícil -coded en ella ... ya que es de código abierto, puede cambiar el código fuente y recompilar el kernel para usar cualquier sysName desea es. no una opción de configuración.
gOLDILOCKS

2
@goldilocks ¿Por qué machinealguna vez cambiaría? Es posible que no esté codificado en el núcleo porque podría adaptarse al hardware, pero seguramente se establecería en el momento del arranque y no cambiaría después de eso. Pero no: se puede configurar por proceso (por ejemplo, para informar i686a 32 bits procesados ​​en x86_64). Por cierto, releasetambién se puede personalizar por proceso hasta cierto punto (prueba setarch i686 --uname-2.6 uname -a).
Gilles 'SO- deja de ser malvado'

1
@Gilles que he editado machine, nodenamey releaseen la pregunta con una referencia a los comentarios. Nuevamente, la pregunta no era realmente sobre todos esos campos.
Ricitos

26

Los datos se almacenan en 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);

Las cadenas en sí están en include / generate / compile.h:

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

y en incluir / generado / utsrelease.h:

#define UTS_RELEASE "3.14.0-v2-v"

UTS_SYSNAME puede definirse en include / linux / uts.h

#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

o como un #define en makefiles

Finalmente, el nombre de host y el nombre de dominio pueden ser controlados por / proc / sys / kernel / {hostname, domainname}. Estos son por espacio de nombres UTS:

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

Esta es generalmente una respuesta buena y completa, pero puede valer la pena responder directamente a la pregunta del afiche. Creo que esto equivaldría a: cambiar la entrada relevante en el archivo relevante y volver a compilar. Escribiste "o como #define en makefiles". ¿Puedes elaborar?
Faheem Mitha

+1 para unshare. De alguna manera me las arreglé para perder este comando hasta hoy. ¡Gracias!
Tino

Y include/generated/compile.hes generado por scripts/mkcompile_h: unix.stackexchange.com/a/485962/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

8

Con la ayuda de una referencia cruzada de Linux y su mención de /proc/sys/kernel/ostype, rastreéostype para incluir / linux / sysctl.h , donde un comentario dice que los nombres se agregan al llamar register_sysctl_table.

Entonces, ¿de dónde se llama eso ? Un lugar es kernel / utsname_sysctl.c , que incluye include / linux / uts.h , donde encontramos:

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

Entonces, como dice la documentación del kernel :

La única forma de ajustar estos valores es reconstruir el núcleo

:-)


6

Como se comentó en otra parte, la información viene con unamesyscall, que está codificada en el núcleo en ejecución.

La parte de la versión normalmente se establece cuando el Makefile compila un nuevo núcleo :

VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION =

cuando tenía tiempo para jugar a compilar mis núcleos, solía agregar cosas allí en EXTRAVERSIÓN; que dio uname -r con cosas como 3.4.1-mytestkernel.

No lo entiendo completamente, pero creo que el resto de la información está configurada Makefiletambién en la línea 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

Para el resto de los datos, la sys_unamellamada al sistema se genera utilizando macros (de una manera bastante complicada), puede comenzar desde aquí si se siente aventurero.

Probablemente la mejor manera de cambiar dicha información es escribir un módulo del núcleo para anular la unamellamada al sistema; Nunca hice eso, pero puedes encontrar información en esta página en la sección 4.2 (lo siento, no hay enlace directo). Sin embargo, tenga en cuenta que ese código se refiere a un kernel bastante antiguo (ahora el kernel de Linux tiene utsespacios de nombres, lo que sea que signifiquen), por lo que probablemente tendrá que cambiarlo mucho.


Gracias a todos. Ya sabía que tenía algo que ver con uname. Sin embargo, lo que no puedo entender es cómo y dónde dentro de la fuente se define la cadena "Linux". Todo lo que sé es dónde puedo encontrar esa información durante el tiempo de ejecución (está contenida dentro de / proc / sys / kernel / ostype). Descubrir cómo exactamente el núcleo mismo sabe que su nombre propio sería una de las cosas más interesantes, diría yo.
user237251

@ user237251 ¿cuántas instancias de la palabra "Linux" ocurren en la fuente del núcleo en contextos de cadena? Si no son tantos, simplemente puede examinar los resultados de una búsqueda textual y ver a dónde lo lleva.
JAB

@JAB Demasiados. Afortunadamente, alguien en kernelnewbies.org me ayudó a resolver el "misterio". Linux obtiene su nombre de sistema de /include/Linux/uts.h. Ver aquí: lxr.free-electrons.com/source/include/linux/uts.h?v=3.10
user237251

2

Si bien no pude encontrar nada en la fuente para indicar esto, creo que utiliza el syscall de uname.

man 2 uname

debería contarte más al respecto. Si ese es el caso, es obtener la información directamente del núcleo y cambiarla probablemente requerirá una nueva recopilación.

Sin embargo, puede cambiar el binario para que usted no haga lo que quiera, simplemente escriba sobre él con el programa que desee. La desventaja es que algunos scripts dependen de esa salida.


3
Si lo hace strace uname, confirmará que unamese utiliza la llamada al sistema.
Graeme

1

La forma correcta de cambiar uname sería cambiar los encabezados de compilación y volver a compilar como otros sugirieron. Pero no estoy seguro de por qué querrías pasar por tantos problemas cuando puedes hacer algo como,

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

o incluso

alias uname 'echo whatever'

0

La respuesta de Rmano me llevó a la mitad, pero la verdadera magia se descubre más fácilmente al pasar la Q=opción en su makelínea de comandos en el directorio de origen del núcleo. que le permite ver los detalles, uno de los cuales es una llamada a un script: echo "4.4.19$(/bin/sh ./scripts/setlocalversion .)". la ejecución de ese mismo fragmento da el número de versión del kernel, 4.4.19-00010-ge5dddbf. Si observa el script, determina el número del sistema de versiones y al ejecutarlo se bash -xmuestra el proceso exacto:

+++ 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

lo que esto me muestra es que si quiero construir un módulo de kernel para que funcione con mi kernel en ejecución, estoy en la versión etiquetada y la confirmación incorrecta. Necesito arreglar eso y construir al menos los DTB ( make dtbs) para crear los archivos generados con el número de versión correcto.


Resulta que incluso eso no fue suficiente. Tuve que reemplazar scripts/setlocalversiona uno que simplemente hace:

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

luego reconstruya los archivos autogenerados:

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

entonces pude construir el controlador de muestra de Derek Molloy y pude insmodhacerlo con éxito. aparentemente la advertencia de Module.symversno estar presente no importó. todo Linux estaba usando para determinar si el módulo funcionaría era esa cadena de versión local.


0

scripts/mkcompile_h

En v4.19, este es el archivo que genera include/generated/compile.hy contiene varias partes interesantes de /proc/version: https://github.com/torvalds/linux/blob/v4.19/scripts/mkcompile_h

  • la #<version>parte proviene del .versionarchivo en el árbol de compilación, que se incrementa cada vez que ocurre el enlace (requiere cambios de archivo / configuración) scripts/link-vmlinux.sh.

    Puede ser anulado por la KBUILD_BUILD_VERSIONvariable de entorno:

    if [ -z "$KBUILD_BUILD_VERSION" ]; then
        VERSION=$(cat .version 2>/dev/null || echo 1)
    else
        VERSION=$KBUILD_BUILD_VERSION
    fi
    
  • la fecha es solo una datellamada cruda :

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

    y de manera similar, el nombre de usuario proviene de whoami( KBUILD_BUILD_USER) y el nombre de host de hostname( KBUILD_BUILD_HOST)

  • La versión del compilador proviene de gcc -v, y parece que no se puede controlar.

Aquí hay una forma de cambiar la versión de la pregunta: https://stackoverflow.com/questions/23424174/how-to-customize-or-remove-extra-linux-kernel-version-details-shown-at-boot

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.