ในอดีต/bin/true
และ/bin/false
ในเปลือกเป็นสคริปต์จริง
ตัวอย่างเช่นใน 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
$
ปัจจุบันอย่างน้อยในbash
การtrue
และfalse
คำสั่งที่จะดำเนินการในตัวคำสั่งเชลล์ ดังนั้นจะไม่มีการเรียกใช้ไฟล์ไบนารีที่เรียกใช้งานโดยค่าเริ่มต้นทั้งเมื่อใช้false
และtrue
คำสั่งในbash
บรรทัดคำสั่งและภายในเชลล์สคริปต์
จากbash
แหล่งที่มาbuiltins/mkbuiltins.c
:
ถ่าน * posix_builtins [] =
{
"alias", "bg", "cd", "command", "** false **", "fc", "fg", "getopts", "งาน",
"kill", "newgrp", "pwd", "read", "** true **", "umask", "unalias", "รอ"
(ถ่าน *) NULL
};
ยังมีต่อความคิดเห็น @meuh:
$ command -V true false
true is a shell builtin
false is a shell builtin
ดังนั้นจึงอาจกล่าวได้มีระดับสูงของความเชื่อมั่นtrue
และfalse
ไฟล์ปฏิบัติการที่มีอยู่ส่วนใหญ่สำหรับการถูกเรียกจากโปรแกรมอื่น ๆ
จากนี้ไปคำตอบจะมุ่งเน้นไปที่/bin/true
ไบนารีจากcoreutils
แพคเกจใน Debian 9/64 บิต ( /usr/bin/true
ใช้ RedHat. RedHat และ Debian ใช้ทั้ง coreutils
แพ็คเกจวิเคราะห์รุ่นที่คอมไพล์ของหลังซึ่งมีอยู่ในมือมากกว่า)
ในขณะที่มันสามารถเห็นได้ในแฟ้มแหล่งที่มาfalse.c
, /bin/false
จะรวบรวมกับ (เกือบ) รหัสที่มาเช่นเดียวกับ/bin/true
เพียงแค่กลับ EXIT_FAILURE (1) แทนเพื่อให้คำตอบนี้สามารถนำมาใช้สำหรับทั้งไบนารี
#define EXIT_STATUS EXIT_FAILURE
#include "true.c"
เนื่องจากยังสามารถยืนยันได้โดยโปรแกรมเรียกทำงานทั้งสองที่มีขนาดเท่ากัน:
$ 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
อนิจจาคำถามตรงไปยังคำตอบwhy are true and false so large?
อาจเป็นเพราะไม่มีอีกต่อไปดังนั้นกดเหตุผลที่จะดูแลเกี่ยวกับประสิทธิภาพสูงสุดของพวกเขา สิ่งเหล่านี้ไม่จำเป็นสำหรับbash
การแสดง แต่ไม่ถูกใช้โดยbash
(การเขียนสคริปต์) อีกต่อไป
ความเห็นที่คล้ายกันนั้นใช้กับขนาดของพวกเขา 26KB สำหรับประเภทของฮาร์ดแวร์ที่เรามีอยู่ทุกวันนี้นั้นไม่มีนัยสำคัญ พื้นที่ไม่ได้อยู่ที่พรีเมี่ยมสำหรับเซิร์ฟเวอร์ทั่วไป / สก์ท็อปอีกต่อไปและพวกเขาไม่ได้รำคาญอีกต่อไปที่จะใช้ไบนารีเดียวกันfalse
และขณะที่มันถูกนำไปใช้เพียงแค่สองครั้งในการกระจายการใช้true
coreutils
อย่างไรก็ตามในการมุ่งเน้นไปที่วิญญาณที่แท้จริงของคำถามทำไมสิ่งที่ควรจะเรียบง่ายและเล็กกลายเป็นเรื่องใหญ่?
การแจกแจงที่แท้จริงของส่วนต่าง ๆ/bin/true
นั้นเป็นไปตามแผนภูมิเหล่านี้แสดง; รหัส + ข้อมูลหลักจำนวนประมาณ 3KB ออกจากไบนารี 26 KB ซึ่งจะมีจำนวน 12% /bin/true
ของขนาดของ
true
ยูทิลิตี้มีรหัสแน่นอน cruft มากขึ้นกว่าปีที่สะดุดตาที่สุดสนับสนุนมาตรฐานและ--version
--help
อย่างไรก็ตามมันไม่ได้เป็นเหตุผลหลัก (เท่านั้น) สำหรับมันใหญ่ แต่ในขณะที่ถูกเชื่อมโยงแบบไดนามิก (ใช้ libs ที่ใช้ร่วมกัน) ยังมีส่วนหนึ่งของห้องสมุดทั่วไปที่ใช้โดยcoreutils
ไบนารีที่เชื่อมโยงกันเป็นห้องสมุดแบบคงที่ เมตาดาสำหรับการสร้างelf
ไฟล์ที่เรียกใช้งานได้นั้นมีส่วนสำคัญของไบนารีด้วยเช่นกันเพราะมันเป็นไฟล์ที่ค่อนข้างเล็กตามมาตรฐานในปัจจุบัน
ส่วนที่เหลือของคำตอบสำหรับการอธิบายวิธีที่เราได้สร้างแผนภูมิต่อไปนี้โดยมีรายละเอียดองค์ประกอบของ/bin/true
ไฟล์ไบนารีที่ปฏิบัติการได้และวิธีที่เรามาถึงข้อสรุปนั้น
ตามที่ @Maks พูดว่าไบนารีถูกรวบรวมจาก C; ตามความคิดเห็นของฉันมันยังได้รับการยืนยันมันมาจาก coreutils เรากำลังชี้ไปที่ผู้เขียน git https://github.com/wertarbyte/coreutils/blob/master/src/true.cแทนที่จะเป็น gnu git เป็น @Maks (แหล่งเดียวกันที่เก็บที่ต่างกัน - ที่เก็บนี้ ถูกเลือกเนื่องจากมีcoreutils
ไลบรารีเต็มรูปแบบ)
เราสามารถเห็นหน่วยการสร้างต่างๆของ/bin/true
ไบนารีที่นี่ (Debian 9 - 64 บิตจาก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
ของบรรดา:
- ข้อความ (รหัสปกติ) ประมาณ 24KB
- data (ตัวแปรเริ่มต้น, สตริงส่วนใหญ่) อยู่ที่ประมาณ 1KB
- bss (ข้อมูลที่ไม่ได้กำหนดค่าเริ่มต้น) 0.5KB
จาก 24KB ประมาณ 1KB สำหรับแก้ไขฟังก์ชั่นภายนอก 58 ตัว
ที่ยังเหลือประมาณ 23KB ประมาณรหัสที่เหลือ เราจะแสดงให้เห็นว่าไฟล์หลักที่แท้จริง - main () + การใช้งาน () รหัสคือประมาณ 1KB รวบรวมและอธิบายสิ่งที่ 22KB อื่น ๆ ที่ใช้สำหรับ
การเจาะลึกลงไปอีกไบนารีด้วยreadelf -S true
เราจะเห็นได้ว่าในขณะที่ไบนารีคือ 26159 ไบต์รหัสที่คอมไพล์จริงคือ 13017 ไบต์และส่วนที่เหลือเป็นข้อมูล / รหัสเริ่มต้นที่แตกต่างกัน
อย่างไรก็ตามtrue.c
ไม่ใช่เรื่องราวทั้งหมดและดูเหมือนว่า 13KB จะมากเกินไปหากไฟล์นั้นเป็นเพียงไฟล์นั้นเท่านั้น เราสามารถเห็นฟังก์ชั่นที่เรียกว่าmain()
ที่ไม่ได้อยู่ในรายการฟังก์ชั่นภายนอกที่เห็นในเอลฟ์ด้วยobjdump -T true
; ฟังก์ชั่นที่มีอยู่ใน:
ฟังก์ชั่นพิเศษเหล่านั้นไม่ได้เชื่อมโยงภายนอกmain()
คือ:
- set_program_name ()
- close_stdout ()
- version_etc ()
ดังนั้นความสงสัยครั้งแรกของฉันจึงถูกต้องบางส่วนในขณะที่ห้องสมุดใช้ไลบรารีแบบไดนามิก/bin/true
ไบนารีมีขนาดใหญ่ * เนื่องจากมีห้องสมุดแบบคงที่บางส่วนที่รวมอยู่ในนั้น * (แต่นั่นไม่ใช่สาเหตุเดียว)
การคอมไพล์รหัส C ไม่ใช่ว่าไม่มีประสิทธิภาพในการมีช่องว่างดังกล่าวดังนั้นฉันสงสัยว่ามีบางอย่างผิดปกติ
พื้นที่พิเศษเกือบ 90% ของขนาดของไบนารีนั้นเป็นเมตาดาต้า / ไลบรารีของเอลฟ์พิเศษ
ในขณะที่ใช้สิ่งที่กระโดดเพื่อแยกส่วน / แยกส่วนไบนารีเพื่อทำความเข้าใจว่าฟังก์ชั่นอยู่ที่ไหนจะเห็นรหัสไบนารีที่คอมไพล์ของฟังก์ชั่น true.c / usage () เป็นจริง 833 ไบต์และฟังก์ชั่น true.c / main () คือ 225 ไบต์ซึ่งประมาณน้อยกว่า 1KB เล็กน้อย ตรรกะสำหรับฟังก์ชั่นเวอร์ชันซึ่งถูกฝังอยู่ในห้องสมุดคงอยู่ประมาณ 1KB
main main () + compiled ที่ใช้จริง () + version () + strings + vars นั้นใช้งานได้เพียงประมาณ 3KB ถึง 3.5KB
เป็นเรื่องน่าขันจริง ๆ ที่สาธารณูปโภคขนาดเล็กและต่ำต้อยดังกล่าวได้มีขนาดใหญ่ขึ้นด้วยเหตุผลที่อธิบายไว้ข้างต้น
คำถามที่เกี่ยวข้อง: ทำความเข้าใจกับสิ่งที่ไบนารี Linux กำลังทำอยู่
true.c
main () ด้วยการเรียกฟังก์ชันที่ละเมิด:
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);
}
ขนาดทศนิยมของส่วนต่าง ๆ ของไบนารี:
$ 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
ผลผลิตของ 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)
เอาท์พุทของobjdump -T true
(ฟังก์ชั่นภายนอกเชื่อมโยงแบบไดนามิกในเวลาทำงาน)
$ 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
which
มันจะออก:true is a shell builtin
สำหรับทุบตี