GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Linux – Warum sind wahr und falsch so groß?

Nachdem Sie herausgefunden haben, dass mehrere gängige Befehle (wie read ) sind eigentlich Bash-Builts (und wenn ich sie an der Eingabeaufforderung ausführe, führe ich tatsächlich ein zweizeiliges Shell-Skript aus, das nur zum Built-in weiterleitet), wollte ich sehen, ob dasselbe für true und false .

Nun, sie sind definitiv Binärdateien.

sh-4.2$ which true
/usr/bin/true
sh-4.2$ which false
/usr/bin/false
sh-4.2$ file /usr/bin/true
/usr/bin/true: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=2697339d3c19235
06e10af65aa3120b12295277e, stripped
sh-4.2$ file /usr/bin/false
/usr/bin/false: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=b160fa513fcc13
537d7293f05e40444fe5843640, stripped
sh-4.2$

Was mich jedoch am meisten überraschte, war ihre Größe. Ich habe erwartet, dass sie jeweils nur ein paar Bytes lang sind, als true ist im Grunde nur exit 0 und false ist Ausgang 1 .

sh-4.2$ true
sh-4.2$ echo $?
0
sh-4.2$ false
sh-4.2$ echo $?
1
sh-4.2$

Allerdings stellte ich zu meiner Überraschung fest, dass beide Dateien über 28 KB groß sind.

sh-4.2$ stat /usr/bin/true
  File: '/usr/bin/true'
  Size: 28920           Blocks: 64         IO Block: 4096   regular file
Device: fd2ch/64812d    Inode: 530320      Links: 1                     
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-01-25 19:46:32.703463708 +0000
Modify: 2016-06-30 09:44:27.000000000 +0100
Change: 2017-12-22 09:43:17.447563336 +0000
 Birth: -
sh-4.2$ stat /usr/bin/false
  File: '/usr/bin/false'
  Size: 28920           Blocks: 64         IO Block: 4096   regular file
Device: fd2ch/64812d    Inode: 530697      Links: 1                     
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-01-25 20:06:27.210764704 +0000
Modify: 2016-06-30 09:44:27.000000000 +0100
Change: 2017-12-22 09:43:18.148561245 +0000
 Birth: -
sh-4.2$

Meine Frage ist also:Warum sind sie so groß? Was enthält die ausführbare Datei außer dem Rückgabecode?

PS:Ich verwende RHEL 7.4

Akzeptierte Antwort:

In der Vergangenheit /bin/true und /bin/false in der Shell waren eigentlich Skripte.

Zum Beispiel in einem PDP/11-Unix-System 7:

$ ls -la /bin/true /bin/false
-rwxr-xr-x 1 bin         7 Jun  8  1979 /bin/false
-rwxr-xr-x 1 bin         0 Jun  8  1979 /bin/true
$
$ cat /bin/false
exit 1
$
$ cat /bin/true
$  

Heutzutage zumindest in bash , die true und false Befehle sind als in die Shell integrierte Befehle implementiert. Daher werden standardmäßig keine ausführbaren Binärdateien aufgerufen, sowohl bei Verwendung des false und wahr Direktiven in der bash Befehlszeile und innerhalb von Shell-Skripten.

Aus der bash Quelle, builtins/mkbuiltins.c :

char *posix_builtins[] =
    {
      "alias", "bg", "cd", "command", "**false**", "fc", "fg", "getopts", "jobs",
      "kill", "newgrp", "pwd", "read", "**true**", "umask", "unalias", "wait",
      (char *)NULL
    };

Auch per @meuh Kommentare:

$ command -V true false
true is a shell builtin
false is a shell builtin

Es kann also mit hoher Sicherheit von wahr gesprochen werden und false ausführbare Dateien existieren hauptsächlich, um von anderen Programmen aufgerufen zu werden .

Von nun an konzentriert sich die Antwort auf /bin/true binär aus den coreutils Paket in Debian 9 / 64 Bit. (/usr/bin/true läuft RedHat. RedHat und Debian verwenden beide die coreutils Paket, analysierte die kompilierte Version des letzteren, da es mehr zur Hand war).

Wie es in der Quelldatei false.c zu sehen ist , /bin/false wird mit (fast) dem gleichen Quellcode wie /bin/true kompiliert , stattdessen wird nur EXIT_FAILURE (1) zurückgegeben, sodass diese Antwort auf beide Binärdateien angewendet werden kann.

#define EXIT_STATUS EXIT_FAILURE
#include "true.c"

Wie es auch durch beide ausführbaren Dateien mit der gleichen Größe bestätigt werden kann:

$ ls -l /bin/true /bin/false
-rwxr-xr-x 1 root root 31464 Feb 22  2017 /bin/false
-rwxr-xr-x 1 root root 31464 Feb 22  2017 /bin/true

Ach, die direkte Frage zur Antwort warum sind wahr und falsch so groß? könnte, denn es gibt keine so dringenden Gründe mehr, sich um ihre Spitzenleistung zu kümmern. Sie sind für bash nicht unbedingt erforderlich Leistung, wird nicht mehr von bash verwendet (Skripterstellung).

Ähnliche Kommentare gelten für ihre Größe, 26 KB für die Art von Hardware, die wir heutzutage haben, sind unbedeutend. Speicherplatz ist für den typischen Server/Desktop nicht mehr von Bedeutung, und sie machen sich nicht einmal mehr die Mühe, dieselbe Binärdatei für false zu verwenden und wahr , da es in Distributionen mit coreutils nur zweimal bereitgestellt wird .

Fokussiert jedoch auf den eigentlichen Geist der Frage, warum etwas, das so einfach und klein sein sollte, so groß wird?

Die tatsächliche Verteilung der Abschnitte von /bin/true ist, wie diese Diagramme zeigen; Der Hauptcode + die Daten machen ungefähr 3 KB aus einer 26-KB-Binärdatei aus, was 12 % der Größe von /bin/true entspricht .

Das true Das Dienstprogramm hat im Laufe der Jahre tatsächlich mehr Cruft-Code erhalten, insbesondere die Standardunterstützung für --version und --help .

Dies ist jedoch nicht die (einzige) Hauptbegründung dafür, dass es so groß ist, sondern, obwohl es dynamisch gelinkt ist (unter Verwendung von gemeinsam genutzten Bibliotheken), auch einen Teil einer generischen Bibliothek hat, die häufig von coreutils verwendet wird Binärdateien, die als statische Bibliothek verknüpft sind. Die Metadaten zum Erstellen eines elf ausführbare Datei macht auch einen erheblichen Teil der Binärdatei aus, da es sich um eine relativ kleine Datei nach heutigen Maßstäben handelt.

Der Rest der Antwort dient dazu, zu erklären, wie wir die folgenden Diagramme erstellt haben, die die Zusammensetzung von /bin/true detailliert beschreiben ausführbare Binärdatei und wie wir zu dieser Schlussfolgerung gelangt sind.

Verwandte:Verwenden Sie _roff, um Wörter zu unterstreichen?

Wie @Maks sagt, wurde die Binärdatei aus C kompiliert; Gemäß meinem Kommentar wird auch bestätigt, dass es von Coreutils stammt. Wir verweisen direkt auf den/die Autor(en)-Git https://github.com/wertarbyte/coreutils/blob/master/src/true.c, anstatt auf den GNU-Git als @Maks (gleiche Quellen, unterschiedliche Repositories – dieses Repository wurde ausgewählt, da es die vollständige Quelle der coreutils enthält Bibliotheken)

Wir können die verschiedenen Bausteine ​​von /bin/true sehen binär hier (Debian 9 – 64 Bit von coreutils ):

$ file /bin/true
/bin/true: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9ae82394864538fa7b23b7f87b259ea2a20889c4, stripped

$ size /bin/true
    text       data     bss     dec     hex filename
   24583       1160     416   26159    662f true

Davon:

  • Text (normalerweise Code) ist etwa 24 KB groß
  • Daten (initialisierte Variablen, meistens Strings) sind etwa 1 KB groß
  • bss (nicht initialisierte Daten) 0,5 KB

Von den 24 KB dient etwa 1 KB dazu, die 58 externen Funktionen zu reparieren.

Damit bleiben noch etwa 23 KB für den Rest des Codes übrig. Wir werden unten zeigen, dass die eigentliche Hauptdatei – main()+usage()-Code ungefähr 1 KB kompiliert ist, und erklären, wofür die anderen 22 KB verwendet werden.

Bohren Sie die Binärdatei mit readelf -S true weiter nach unten , können wir sehen, dass, während die Binärdatei 26159 Bytes groß ist, der tatsächlich kompilierte Code 13017 Bytes groß ist und der Rest sortierte Daten/Initialisierungscodes sind.

Allerdings true.c ist nicht die ganze Geschichte und 13 KB scheinen ziemlich übertrieben zu sein, wenn es nur diese Datei wäre; wir können Funktionen sehen, die in main() aufgerufen werden die nicht in den externen Funktionen aufgelistet sind, die im Elf mit objdump -T true zu sehen sind; Funktionen, die vorhanden sind unter:

  • https://github.com/coreutils/gnulib/blob/master/lib/progname.c
  • https://github.com/coreutils/gnulib/blob/master/lib/closeout.c
  • https://github.com/coreutils/gnulib/blob/master/lib/version-etc.c

Diese zusätzlichen Funktionen, die nicht extern in main() verlinkt sind sind:

  • set_program_name()
  • close_stdout()
  • version_etc()

Mein erster Verdacht war also teilweise richtig, während die Bibliothek dynamische Bibliotheken verwendet, die /bin/true binär ist groß, *weil es einige hat darin enthaltene statische Bibliotheken* (aber das ist nicht die einzige Ursache).

Das Kompilieren von C-Code ist normalerweise das nicht ineffizient, weil so viel Speicherplatz nicht berücksichtigt wurde, daher mein anfänglicher Verdacht, dass etwas nicht stimmt.

Der zusätzliche Speicherplatz, fast 90 % der Größe der Binärdatei, sind tatsächlich zusätzliche Bibliotheken/Elf-Metadaten.

Bei der Verwendung von Hopper zum Disassemblieren/Dekompilieren der Binärdatei, um zu verstehen, wo sich Funktionen befinden, ist zu sehen, dass der kompilierte Binärcode der Funktion true.c/usage() tatsächlich 833 Bytes und der der Funktion true.c/main() 225 Bytes beträgt Bytes, was ungefähr etwas weniger als 1 KB entspricht. Die Logik für Versionsfunktionen, die in den statischen Bibliotheken vergraben ist, ist ungefähr 1 KB groß.

Die tatsächlich kompilierten main()+usage()+version()+strings+vars verbrauchen nur etwa 3 KB bis 3,5 KB.

Es ist in der Tat ironisch, dass solche kleinen und bescheidenen Dienstprogramme aus den oben erläuterten Gründen größer geworden sind.

verwandte Frage:Verstehen, was eine Linux-Binärdatei macht

true.c main() mit den anstößigen Funktionsaufrufen:

int
main (int argc, char **argv)
{
  /* Recognize --help or --version only if it's the only command-line
     argument.  */
  if (argc == 2)
    {
      initialize_main (&argc, &argv);
      set_program_name (argv[0]);           <-----------
      setlocale (LC_ALL, "");
      bindtextdomain (PACKAGE, LOCALEDIR);
      textdomain (PACKAGE);

      atexit (close_stdout);             <-----

      if (STREQ (argv[1], "--help"))
        usage (EXIT_STATUS);

      if (STREQ (argv[1], "--version"))
        version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,  AUTHORS,  <------
                     (char *) NULL);
    }

  exit (EXIT_STATUS);
}

Die Dezimalgröße der verschiedenen Abschnitte der Binärdatei:

$ size -A -t true 
true  :
section               size      addr
.interp                 28       568
.note.ABI-tag           32       596
.note.gnu.build-id      36       628
.gnu.hash               60       664
.dynsym               1416       728
.dynstr                676      2144
.gnu.version           118      2820
.gnu.version_r          96      2944
.rela.dyn              624      3040
.rela.plt             1104      3664
.init                   23      4768
.plt                   752      4800
.plt.got                 8      5552
.text                13017      5568
.fini                    9     18588
.rodata               3104     18624
.eh_frame_hdr          572     21728
.eh_frame             2908     22304
.init_array              8   2125160
.fini_array              8   2125168
.jcr                     8   2125176
.data.rel.ro            88   2125184
.dynamic               480   2125272
.got                    48   2125752
.got.plt               392   2125824
.data                  128   2126240
.bss                   416   2126368
.gnu_debuglink          52         0
Total                26211

Ausgabe von readelf -S true

$ readelf -S true
There are 30 section headers, starting at offset 0x7368:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
       000000000000003c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002d8  000002d8
       0000000000000588  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000000860  00000860
       00000000000002a4  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000000b04  00000b04
       0000000000000076  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000000b80  00000b80
       0000000000000060  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000000be0  00000be0
       0000000000000270  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000000e50  00000e50
       0000000000000450  0000000000000018  AI       5    25     8
  [11] .init             PROGBITS         00000000000012a0  000012a0
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000000012c0  000012c0
       00000000000002f0  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         00000000000015b0  000015b0
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         00000000000015c0  000015c0
       00000000000032d9  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         000000000000489c  0000489c
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000000048c0  000048c0
       0000000000000c20  0000000000000000   A       0     0     32
  [17] .eh_frame_hdr     PROGBITS         00000000000054e0  000054e0
       000000000000023c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000005720  00005720
       0000000000000b5c  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000206d68  00006d68
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000206d70  00006d70
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000206d78  00006d78
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .data.rel.ro      PROGBITS         0000000000206d80  00006d80
       0000000000000058  0000000000000000  WA       0     0     32
  [23] .dynamic          DYNAMIC          0000000000206dd8  00006dd8
       00000000000001e0  0000000000000010  WA       6     0     8
  [24] .got              PROGBITS         0000000000206fb8  00006fb8
       0000000000000030  0000000000000008  WA       0     0     8
  [25] .got.plt          PROGBITS         0000000000207000  00007000
       0000000000000188  0000000000000008  WA       0     0     8
  [26] .data             PROGBITS         00000000002071a0  000071a0
       0000000000000080  0000000000000000  WA       0     0     32
  [27] .bss              NOBITS           0000000000207220  00007220
       00000000000001a0  0000000000000000  WA       0     0     32
  [28] .gnu_debuglink    PROGBITS         0000000000000000  00007220
       0000000000000034  0000000000000000           0     0     1
  [29] .shstrtab         STRTAB           0000000000000000  00007254
       000000000000010f  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

Ausgabe von objdump -T true (externe Funktionen dynamisch zur Laufzeit gelinkt)

$ objdump -T true

true:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __uflow
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 getenv
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 free
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 abort
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __errno_location
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strncmp
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 _exit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __fpending
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 textdomain
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fclose
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 bindtextdomain
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 dcgettext
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __ctype_get_mb_cur_max
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strlen
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.4   __stack_chk_fail
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 mbrtowc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strrchr
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 lseek
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 memset
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fscanf
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 close
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __libc_start_main
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 memcmp
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fputs_unlocked
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 calloc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strcmp
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.14  memcpy
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fileno
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 malloc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fflush
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 nl_langinfo
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 ungetc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __freading
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 realloc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fdopen
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 setlocale
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.4 __printf_chk
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 error
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 open
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fseeko
0000000000000000  w   D  *UND*  0000000000000000              _Jv_RegisterClasses
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_atexit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 exit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fwrite
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3.4 __fprintf_chk
0000000000000000  w   D  *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 mbsinit
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 iswprint
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_finalize
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   __ctype_b_loc
0000000000207228 g    DO .bss   0000000000000008  GLIBC_2.2.5 stdout
0000000000207220 g    DO .bss   0000000000000008  GLIBC_2.2.5 __progname
0000000000207230  w   DO .bss   0000000000000008  GLIBC_2.2.5 program_invocation_name
0000000000207230 g    DO .bss   0000000000000008  GLIBC_2.2.5 __progname_full
0000000000207220  w   DO .bss   0000000000000008  GLIBC_2.2.5 program_invocation_short_name
0000000000207240 g    DO .bss   0000000000000008  GLIBC_2.2.5 stderr

Linux
  1. Warum sich meine öffentliche Bibliothek für Linux und Open Source entscheidet

  2. So finden Sie große Dateien unter Linux mit den Befehlen find und du

  3. Linux – Was sind High Memory und Low Memory unter Linux?

  4. Welche Zeichen sind in Windows- und Linux-Verzeichnisnamen verboten?

  5. Was sind High Memory und Low Memory unter Linux?

7 Gründe, warum ich Manjaro Linux verwende und Sie es auch sollten

Was sind Linux-Protokolle und wo sind sie zu finden?

Warum ist Linux in jeder Hinsicht so schlecht und Windows 11 besser?

Warum sind einige Emoji B&W und andere zu groß?

Wie und warum Linux zur Installation von Telnet verwendet wird

Warum sind wahr und falsch so groß?