网络知识 娱乐 哪个编程语言实现hello world最烦琐?

哪个编程语言实现hello world最烦琐?

哪个编程语言实hello world最烦琐?

说明:

·由于汇编是一种直接面向底层的语言,所以最简单的程序也会涉及到许多底层的细节从而显得晦涩(不C直接一printf搞定);

·本篇文章通过最简单hello world程序,理解寄存器、内存、节、指令、系统调用,在程序的简单运作原理;

Talk is cheap, show me your code!

;源代码文件名test.asm

;执行文件名test

;编译方法:

;nasm -f elf64 -g -F dwarf test.asm -l test.lst

;gcc -o test test.o -no-pie

section .data;.data节写入数据

msg: db "hello world",10;10对应ascii是换行符

msgLen equ $-msg;equ是伪指令,这行代码的意思msgLen指代msg字符串的占位长度(字节)

section .text;.text写入代码

global main;程序的代码入口标签main(使程序执行的时候能够找到第一个指令)

main:;这个标签实质指代.text节的第一个指令的内存地址

;屏幕打印

mov rax, 1;sys_write(x86-64)

mov rdi, 1;1是标准输出

mov rsi, msg

mov rdx, msgLen;字符串长度,不包0

syscall;64int 0x80指令

;退出程序

mov rax, 60;exit(x86-64)

mov rdi, 0;0,与上条指令结合exit(0)

section语句

要了section语句的作用,首先我们要先初步大概了解一下程序的执行原理,如下,

哪个编程语言实现hello world最烦琐?

·编译器在编译汇编代码的时候,会按LinuxELFlinux的可执行文件格式)进行编译(例如插ELF headerprogram headersection headergot等);

·程序运行的时候,则把代码和一些已经初始化的数据装载按照格式装载到内存(分布到各section);

·stack节,在程序运行的过程中会根据程序逻辑压入或者弹出数据stack节一般情况下不存储代码);

·heap节,用于程序的最自由的数据存储的区域(例如Cmalloc函数申请内存时使用的就是这个区域);

·bss节,用于存储定义了但是没有初始化的数据(例Cint ichar ch[10]),常用于程序的数据接收缓冲区;

·data节,用于存储已经定义了并且已经初始化的数据(类似C的常量);

·text节,用于存储代码(函数的代码也是存储在这个区域,而不是存储stack区域);

综上所述section语句的作用是区分汇编代码的区域。

syscall和中断向量表

在介syscall指令之前,需要先介linux的几个关键的概念,如下,

·用户空间(用户空间的本质是指定的内存空间。这些空间用于运行用户的程序,例nginxapache);

·内核空间(内核空间的本质也是内存空间,内核空间用于运行操作系统的代码,用户空间的应用程序原则上不能访问内核空间,又或者说不能直接访问内核空间,于是引入下面的概中断向量表);

·中断向量表(中断向量表的本质是一个数组,数组的元素是内存地址,这些地址指向内核空间)

syscall指令和中断向量表的关系如下,

哪个编程语言实现hello world最烦琐?用户进程想要调用系统服务(例如输出到屏幕、打开文件),需要统一通过向128个中断向量,根据既定的数据结构发送系统调用服务请求

·应用程序是不能随意访问内核空间,需要通过既定的规则来进行访问,所以设计出了中断向量表(这样设计有安全意义);

·因为中断向量表指示的方向意义,所以叫表;

综上所述,在终端(一般是屏幕)打hello world,实质上是一次调用系统服务的过程。

linux系统调用sys_write

上文中提到,想要调用系统服务,需要按照这个服务的既定数据结构,然后组织这些数据,向128号的中断向量发送服务调用请求,如下图,

哪个编程语言实现hello world最烦琐?把需要打印的内容组织好,通syscall指令向128号中断发标准输的服务调用请求

·寄存rax的内容要设置1。在系统调用的过程中,这个寄存器一般都是用于存储系统服务的调用编号。而打印的服务编号1(要查看系统调用服务编号可查看文/usr/include/x86_64-linux-gnu/asm/unistd_64.h

·寄存rdidestination index),用于指定打印输出的文件描述符(屏幕的文件描述符1);

·寄存rsisource index),用于指定输出内容的地址(字符串的存储地址);

·寄存rdx,用于指定输出内容的长度(这个可以自定义,你想打印多长自己决定;上一篇文章提过,字符串在汇编的角度来看,只不过是连续的内存存储空间)

综上所述,通俗地描述打hello world的过程(系统服务调用过程),如下,

·rax寄存器告诉操作系统,需要调用什么系统服务1号服务标准输服务);

·rdi寄存器告诉操作系统,要在那里打印(一般打印在终端,也就是屏幕);

·rsi寄存器告诉操作系统,需要打印的内容在哪里可以找到;

·rdx寄存器告诉操作系统,需要打印的内容有多长(字节);

最后,打印服务调用完毕后,需要结束程序,相当Cexit(0)函数,原理不再赘述,具体可参见上述提供的代码。