Trong quá khứ, /bin/true
và /bin/false
trong vỏ thực sự là các kịch bản.
Chẳng hạn, trong 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
$
Ngày nay, ít nhất là trong bash
, các lệnh true
và false
lệnh được triển khai như các lệnh tích hợp shell. Do đó không có file thực thi nhị phân được gọi theo mặc định, cả khi sử dụng false
và true
chỉ thị trong bash
dòng lệnh và bên trong kịch bản shell.
Từ bash
nguồn , builtins/mkbuiltins.c
:
char * posix_builtins [] =
{
"bí danh", "bg", "cd", "lệnh", "** false **", "fc", "fg", "getopts", "jobs",
"Giết", "newgrp", "pwd", "đọc", "** đúng **", "umask", "unalias", "chờ",
(char *) NULL
};
Ngoài ra mỗi bình luận @meuh:
$ command -V true false
true is a shell builtin
false is a shell builtin
Vì vậy, có thể nói với mức độ chắc chắn cao, các tệp thực thi true
và false
tồn tại chủ yếu được gọi từ các chương trình khác .
Từ giờ trở đi, câu trả lời sẽ tập trung vào /bin/true
nhị phân từ coreutils
gói trong Debian 9/64 bit. ( /usr/bin/true
chạy RedHat. RedHat và Debian sử dụng cả coreutils
gói, đã phân tích phiên bản được biên dịch của phiên bản sau có sẵn trong tay).
Như có thể thấy trong tệp nguồn false.c
, /bin/false
được biên dịch với (gần như) mã nguồn giống như /bin/true
, chỉ trả về EXIT_FAILURE (1), vì vậy câu trả lời này có thể được áp dụng cho cả hai nhị phân.
#define EXIT_STATUS EXIT_FAILURE
#include "true.c"
Vì nó cũng có thể được xác nhận bởi cả hai tệp thực thi có cùng kích thướ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
Than ôi, câu hỏi trực tiếp cho câu trả lời why are true and false so large?
có thể là bởi vì không còn lý do cấp bách nào để quan tâm đến hiệu suất hàng đầu của họ. Chúng không cần thiết cho bash
hiệu suất, không được sử dụng nữa bởi bash
(kịch bản).
Nhận xét tương tự áp dụng cho kích thước của chúng, 26KB cho loại phần cứng chúng ta hiện nay là không đáng kể. Không gian không còn cao cấp cho máy chủ / máy tính để bàn thông thường nữa và họ thậm chí không bận tâm sử dụng cùng một nhị phân cho false
và true
vì nó chỉ được triển khai hai lần trong các bản phân phối sử dụng coreutils
.
Tuy nhiên, tập trung vào tinh thần thực sự của câu hỏi, tại sao một cái gì đó đơn giản và nhỏ bé lại trở nên lớn đến vậy?
Phân phối thực sự của các phần /bin/true
là như những biểu đồ này cho thấy; mã chính + dữ liệu lên tới khoảng 3KB trong số nhị phân 26KB, chiếm tới 12% kích thước của /bin/true
.
Các true
tiện ích có mã thực sự cruft hơn trong những năm qua, đặc biệt là sự hỗ trợ tiêu chuẩn cho --version
và --help
.
Tuy nhiên, đó không phải là lý do chính (duy nhất) cho nó quá lớn, mà là, trong khi được liên kết động (sử dụng libs chung), cũng có một phần của thư viện chung thường được sử dụng bởi các coreutils
nhị phân được liên kết như một thư viện tĩnh. Metada để xây dựng một elf
tệp thực thi cũng chiếm một phần đáng kể của nhị phân, vì nó là một tệp tương đối nhỏ theo tiêu chuẩn của hôm nay.
Phần còn lại của câu trả lời là để giải thích cách chúng tôi xây dựng các biểu đồ sau đây chi tiết về thành phần của /bin/true
tệp nhị phân thực thi và cách chúng tôi đi đến kết luận đó.
Như @Maks nói, nhị phân được biên dịch từ C; theo nhận xét của tôi, nó cũng được xác nhận là từ coreutils. Chúng tôi đang chỉ trực tiếp đến tác giả git https://github.com/wertarbyte/coreutils/blob/master/src/true.c , thay vì gnu git như @Maks (cùng nguồn, kho lưu trữ khác nhau - kho lưu trữ này đã được chọn vì nó có nguồn đầy đủ của các coreutils
thư viện)
Chúng ta có thể thấy các khối xây dựng khác nhau của /bin/true
nhị phân ở đây (Debian 9 - 64 bit từ 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
Những:
- văn bản (thường là mã) khoảng 24KB
- dữ liệu (biến khởi tạo, chủ yếu là chuỗi) khoảng 1KB
- bss (dữ liệu chưa được khởi tạo) 0,5KB
Trong số 24KB, khoảng 1KB là để sửa 58 chức năng bên ngoài.
Điều đó vẫn còn khoảng 23KB cho phần còn lại của mã. Chúng tôi sẽ chỉ ra dưới đây rằng tệp chính thực tế - mã chính () + cách sử dụng () được biên dịch khoảng 1KB và giải thích 22KB khác được sử dụng để làm gì.
Đi sâu hơn vào nhị phân với readelf -S true
, chúng ta có thể thấy rằng trong khi nhị phân là 26159 byte, mã được biên dịch thực tế là 13017 byte và phần còn lại là các loại dữ liệu / mã khởi tạo.
Tuy nhiên, true.c
không phải là toàn bộ câu chuyện và 13KB dường như quá nhiều nếu chỉ là tập tin đó; chúng ta có thể thấy các hàm được gọi trong main()
đó không được liệt kê trong các hàm bên ngoài nhìn thấy trong elf với objdump -T true
; các chức năng có mặt tại:
Các chức năng bổ sung không được liên kết bên ngoài main()
là:
- set_program_name ()
- close_stdout ()
- phiên bản_etc ()
Vì vậy, sự nghi ngờ đầu tiên của tôi là một phần chính xác, trong khi thư viện đang sử dụng các thư viện động, /bin/true
nhị phân là lớn * bởi vì nó có một số thư viện tĩnh đi kèm với nó * (nhưng đó không phải là nguyên nhân duy nhất).
Biên dịch mã C không phải là thường mà không hiệu quả vì có không gian như vậy mất tích, vì thế ban đầu nghi ngờ một cái gì đó của tôi là không ổn.
Không gian thêm, gần 90% kích thước của nhị phân, thực sự là siêu thư viện / elf siêu dữ liệu.
Trong khi sử dụng Hopper để phân tách / dịch ngược tệp nhị phân để hiểu vị trí của các hàm, có thể thấy mã nhị phân được biên dịch của hàm true.c / used () thực sự là 833 byte và của hàm true.c / main () là 225 byte, gần như ít hơn 1KB. Logic cho các chức năng phiên bản, được chôn trong các thư viện tĩnh, là khoảng 1KB.
Chuỗi chính () + cách sử dụng () + phiên bản () + chuỗi + vars được biên dịch thực tế chỉ sử dụng tối đa khoảng 3KB đến 3,5KB.
Thật là mỉa mai, những tiện ích nhỏ và khiêm tốn như vậy đã trở nên lớn hơn về kích thước vì những lý do đã giải thích ở trên.
Câu hỏi liên quan: Hiểu những gì một nhị phân Linux đang làm
true.c
main () với các lệnh gọi hàm vi phạm:
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);
}
Kích thước thập phân của các phần khác nhau của nhị phân:
$ 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
Đầu ra của 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)
Đầu ra của objdump -T true
(các chức năng bên ngoài được liên kết động theo thời gian chạy)
$ 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
khôngwhich
. Nó sẽ xuất ra:true is a shell builtin
cho bash.