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
$
Zumindest in der heutigen Zeit bash
werden die Befehle true
und false
als eingebaute Shell-Befehle implementiert. Daher werden standardmäßig keine ausführbaren Binärdateien aufgerufen, sowohl bei Verwendung der Anweisungen false
und true
in der bash
Befehlszeile als auch 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
Man kann also mit hoher Sicherheit sagen, dass die true
und false
ausführbaren Dateien hauptsächlich zum Aufrufen aus anderen Programmen existieren .
Von nun an konzentriert sich die Antwort auf die /bin/true
Binärdatei aus dem coreutils
Paket in Debian 9/64-Bit. ( /usr/bin/true
RedHat wird ausgeführt. RedHat und Debian verwenden beide das coreutils
Paket, analysierten die kompilierte Version des letzteren und hatten es mehr zur Hand).
Wie in der Quelldatei zu sehen ist false.c
, /bin/false
wird mit (fast) demselben Quellcode kompiliert wie /bin/true
, indem stattdessen nur EXIT_FAILURE (1) zurückgegeben wird, sodass diese Antwort für beide Binärdateien angewendet werden kann.
#define EXIT_STATUS EXIT_FAILURE
#include "true.c"
Wie es auch von beiden 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
Leider könnte die direkte Frage zur Antwort why are true and false so large?
lauten, denn es gibt nicht mehr so dringende Gründe, sich um ihre Spitzenleistung zu kümmern. Sie sind für die bash
Leistung nicht wesentlich und werden von bash
(scripting) nicht mehr verwendet .
Ä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 knapp und sie müssen nicht einmal mehr dieselbe Binärdatei für false
und verwenden true
, da er in Distributionen mit nur noch zweimal bereitgestellt wird coreutils
.
Fokussiert man sich jedoch im eigentlichen Sinne auf die Frage, warum etwas, das so einfach und klein sein sollte, so groß wird?
Die tatsächliche Verteilung der Abschnitte von /bin/true
ist wie in diesen Diagrammen dargestellt. Der Hauptcode + Daten beläuft sich auf ungefähr 3 KB aus einer 26 KB großen Binärdatei, was 12% der Größe von entspricht /bin/true
.
Das true
Dienstprogramm bekam in der Tat mehr cruft Code im Laufe der Jahre, vor allem den Standard - Support für --version
und --help
.
Dies ist jedoch nicht der (einzige) Hauptgrund dafür, dass es so umfangreich ist, sondern dass es zwar dynamisch verknüpft ist (mithilfe von gemeinsam genutzten Bibliotheken), aber auch Teil einer generischen Bibliothek ist, die üblicherweise von coreutils
Binärdateien verwendet wird, die als statische Bibliothek verknüpft sind. Die Metada zum Erstellen einer elf
ausführbaren Datei macht auch einen erheblichen Teil der Binärdatei aus, da sie nach heutigen Maßstäben relativ klein ist.
Der Rest der Antwort dient der Erläuterung, wie wir die folgenden Diagramme erstellen mussten, in denen die Zusammensetzung der /bin/true
ausführbaren Binärdatei detailliert beschrieben ist, und wie wir zu dieser Schlussfolgerung gekommen sind.
Wie @Maks sagt, wurde die Binärdatei aus C kompiliert. Nach meinem Kommentar wurde 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 anstelle des gnu-git als @Maks (gleiche Quellen, verschiedene Repositorys - dieses Repository) wurde ausgewählt, da es die vollständige Quelle der coreutils
Bibliotheken enthält)
Wir können die verschiedenen Bausteine der /bin/true
Binärdatei hier sehen (Debian 9 - 64 Bits 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
Von diesen:
- Text (normalerweise Code) ist ungefähr 24 KB groß
- Daten (initialisierte Variablen, meistens Strings) sind ca. 1 KB groß
- bss (nicht initialisierte Daten) 0,5 KB
Von den 24KB dient etwa 1KB zur Einrichtung der 58 externen Funktionen.
Für den Rest des Codes verbleiben noch ungefähr 23 KB. Wir werden unten zeigen, dass der eigentliche Code der Hauptdatei - main () + usage () - etwa 1 KB groß ist, und erklären, wofür die anderen 22 KB verwendet werden.
Weiter unten in der Binärdatei mit sehen readelf -S true
wir, dass die Binärdatei 26159 Bytes umfasst, der tatsächlich kompilierte Code 13017 Bytes und der Rest verschiedene Daten / Initialisierungscodes enthält.
Es true.c
ist jedoch nicht die ganze Geschichte und 13 KB scheinen ziemlich übertrieben, wenn es nur diese Datei wäre; wir können aufgerufene main()
Funktionen sehen, die nicht in den externen Funktionen aufgelistet sind, die in der Elfe mit zu sehen sind objdump -T true
; Funktionen, die vorhanden sind bei:
Die nicht extern eingebundenen Zusatzfunktionen main()
sind:
- set_program_name ()
- close_stdout ()
- version_etc ()
Mein erster Verdacht war also teilweise richtig, während die Bibliothek dynamische Bibliotheken verwendet, ist die /bin/true
Binärdatei groß *, weil sie einige statische Bibliotheken enthält * (aber das ist nicht die einzige Ursache).
Das Kompilieren von C-Code ist normalerweise nicht so ineffizient, wenn man solchen Speicherplatz nicht berücksichtigt, daher war mein anfänglicher Verdacht falsch.
Der zusätzliche Speicherplatz, fast 90% der Größe der Binärdatei, besteht in der Tat aus zusätzlichen Bibliotheken / Elf-Metadaten.
Während der Verwendung von Hopper zum Zerlegen / Dekompilieren der Binärdatei, um zu verstehen, wo sich Funktionen befinden, ist ersichtlich, dass der kompilierte Binärcode der Funktion true.c / usage () tatsächlich 833 Byte und der der Funktion true.c / main () 225 Byte beträgt Bytes, was ungefähr etwas weniger als 1 KB ist. Die Logik für Versionsfunktionen, die in den statischen Bibliotheken vergraben ist, beträgt ungefähr 1 KB.
Die tatsächlich kompilierten main () + usage () + version () + strings + vars belegen nur etwa 3 KB bis 3,5 KB.
Es ist in der Tat ironisch, dass solche kleinen und bescheidenen Versorgungsunternehmen aus den oben erläuterten Gründen größer geworden sind.
Verwandte Frage: Verstehen, was eine Linux-Binärdatei tut
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 zur Laufzeit dynamisch verknüpft)
$ 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
command -V true
nicht verwendenwhich
. Es wird ausgegeben:true is a shell builtin
für Bash.