同样的 hello world 为什么 c 比 汇编的系统调用更多

ponponon
  • 1.5k

测试平台:

─➤  uname -a 
Linux aliyunserevr 5.4.0-99-generic #112-Ubuntu SMP Thu Feb 3 13:50:55 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

使用汇编输出 hello world

heelo.asm

;; hello.asm
;; nasm -f elf hello.asm; will output hello.o
;; ld -s -o hello hello.o

;; section, same to segment
segment .data      ; 数据段声明, 下列代码将放在数据段中
    msg db "Hello, world!", 0xA   ; 要输出的字符串
    len equ $ - msg         ; 字串长度

section .text      ; 代码段声明,下列代码将放入代码段中
global _start      ; 指定入口函数,global修饰是为了让外部可以引用_start
_start:         ; 在屏幕上显示一个字符串
    mov edx, len   ; 参数三:字符串长度
    mov ecx, msg   ; 参数二:要显示的字符串
    mov ebx, 1    ; 参数一:文件描述符(stdout)
    mov eax, 4    ; 系统调用号(sys_write)
    int 0x80     ; 调用内核功能
             ; 退出程序
    mov ebx, 0    ; 参数一:退出代码
    mov eax, 1    ; 系统调用号(sys_exit)
    int 0x80     ; 调用内核功能

编译

nasm -f elf64 hello.asm
ld -s -o hello hello.o

运行

strace ./hello     

输出如下:

execve("./hello", ["./hello"], 0x7ffc083e5060 /* 36 vars */) = 0
strace: [ Process PID=4717 runs in 32 bit mode. ]
write(1, "Hello, world!\n", 14Hello, world!
)         = 14
exit(0)                                 = ?
+++ exited with 0 +++

一共四个系统调用


使用 c 语言输出 hello world

c_hello.c

#include <stdio.h>

int main(void)
{
    printf("hello world\n");
    return 0;
}

编译

gcc -o c_hello c_hello.c              

运行

strace ./c_hello   

输出如下:

execve("./c_hello", ["./c_hello"], 0x7fff69f3eaa0 /* 36 vars */) = 0
brk(NULL)                               = 0x56185f27a000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc138ff1c0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=35708, ...}) = 0
mmap(NULL, 35708, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f959c1c0000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f959c1be000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f959bfcc000
mprotect(0x7f959bff1000, 1847296, PROT_NONE) = 0
mmap(0x7f959bff1000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f959bff1000
mmap(0x7f959c169000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7f959c169000
mmap(0x7f959c1b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f959c1b4000
mmap(0x7f959c1ba000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f959c1ba000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f959c1bf540) = 0
mprotect(0x7f959c1b4000, 12288, PROT_READ) = 0
mprotect(0x56185f1f1000, 4096, PROT_READ) = 0
mprotect(0x7f959c1f6000, 4096, PROT_READ) = 0
munmap(0x7f959c1c0000, 35708)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x3), ...}) = 0
brk(NULL)                               = 0x56185f27a000
brk(0x56185f29b000)                     = 0x56185f29b000
write(1, "hello world\n", 12hello world
)           = 12
exit_group(0)                           = ?
+++ exited with 0 +++

一共 n 个系统调用

回复
阅读 630
1 个回答

因为 C 语言在进入到 main 函数之前就已经做了很多事了,譬如初始化一些全局变量,为多线程的安全做一些准备工作等等,在你的 main 结束后它还会做一些释放类的工作。

举个例子,在标准 C 库里面用于保存函数错误码的 errno,这是个全局的变量,编译器并没有智能到检测你所有的代码,发现你没有使用 errno,然后把 errno 定义优化掉的能力。所以它按默认初始化了 errno,但你很可能根本没用到,这部分的代码对你来说就没用。

而你用汇编写代码的时候,这些都不会有,当然就简短的多。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏