汇编语言简易教程(12):系统服务
<h1 id="汇编语言简易教程12系统服务">汇编语言简易教程(12):系统服务</h1><blockquote>
<p>应用程序必须使用操作系统执行许多操作。 此类操作包括控制台输出、键盘输入、文件服务(打开、读取、写入、关闭等)、获取时间或日期、请求内存分配等</p>
<p>访问系统服务是应用程序请求操作系统执行某些特定操作(代表进程)的方式。 更具体地说,系统调用是执行进程和操作系统之间的接口</p>
<p>本节介绍如何使用一些基本的系统服务调用。</p>
<h1 id="系统调用">系统调用</h1>
<p>系统服务调用在逻辑上类似于调用函数,其中函数代码位于操作系统中。 该功能可能需要权限才能操作,这就是为什么必须将控制权转移到操作系统的原因</p>
<p>调用系统服务时,参数放置在标准参数寄存器中。系统服务通常不使用基于堆栈的参数。 这将系统服务的参数限制为六 (6) 个, 一般来说这不是一个严重的限制.</p>
<p>如果需要任何参数,则系统服务的参数将放在 RDI、RSI、RDX、R10、R8 和 R9 寄存器中(按此顺序)。 下表显示了与标准调用约定一致的参数位置</p>
<p><img src="assets/image-20240413161519-nzsh5nw.png"></p>
<p>设置调用代码和任何参数后,将执行 syscall 指令。 系统调用指令将暂停当前进程并将控制权转移到操作系统,操作系统将尝试执行 rax 寄存器中指定的服务。 当系统服务返回时,该过程将恢复.</p>
<h1 id="控制台输出">控制台输出</h1>
<p>将字符输出到控制台的系统服务是系统写入 (SYS_write)。与高级语言一样,字符被写入标准输出(STDOUT),即控制台。 STDOUT 是控制台的默认文件描述符。 filedescriptor 已打开,可用于程序(汇编语言和高级语言)</p>
<p><img src="assets/image-20240413162001-lkl1d09.png"></p>
<h2 id="参考代码">参考代码</h2>
<p>这部分的代码可以参考注释来阅读, 基本上没有什么难度</p>
<pre><code class="language-ass">section .data
LF equ 10
NULL equ 0
True equ 1
FALSE equ 0
EXIT_SUCCESS equ 0
STDIN equ 0
STDOUT equ 1
STDERR equ 2
SYS_read equ 0
SYS_write equ 1
SYS_open equ 2
SYS_close equ 3
SYS_fork equ 57
SYS_exit equ 60
SYS_create equ 85
SYS_time equ 201
message1 db "hello world.", LF, NULL
message2 db "Enter Answer: ", NULL
newLinedb LF, NULL
section .text
global _start
_start:
mov rdi, message1 ; 设置string头地址, 使用rdi存储
call printString; 调用printString 函数
exampleDone:
mov rax, SYS_exit
mov rdi, EXIT_SUCCESS
syscall
global printString
printString:
push rbx ;因为使用到了rbx的地址, 所以需要压栈, 为后续做准备
mov rbx, rdi ;复制rdi到rbx
mov rdx, 0 ;将rdx设为0
strCountLoop:
cmp byte , NULL
je strCountDone ; 如果字符为NULL,表示结束, 则直接跳转到结果.
inc rdx ; 增加rdx, 表示的字符串长度
inc rbx ; 增加rbx, 这里表示的是string的位置
jmp strCountLoop ; 继续统计
strCountDone:
cmp rdx, 0 ; 如果rdx == 0, 表示字符串长度为0, 不需要系统调用
je prtDone ; 输出结束
mov rax, SYS_write ; 调用sys_write
mov rsi, rdi
mov rdi, STDOUT
syscall
prtDone:
pop rbx
ret
</code></pre>
<h1 id="控制台输入">控制台输入</h1>
<p>将字符输出到控制台的系统服务是系统写入 (SYS_write)。与高级语言一样,字符被写入标准输出(STDOUT),即控制台。 STDOUT 是控制台的默认文件描述符。 filedescriptor 已打开,可用于程序(汇编语言和高级语言)。</p>
<p><img src="assets/image-20240413171118-wyzn7rz.png"></p>
<h2 id="示例代码">示例代码</h2>
<pre><code class="language-ass">section .data
LF equ 10
NULL equ 0
True equ 1
FALSE equ 0
EXIT_SUCCESS equ 0
STDIN equ 0
STDOUT equ 1
STDERR equ 2
SYS_read equ 0
SYS_write equ 1
SYS_open equ 2
SYS_close equ 3
SYS_fork equ 57
SYS_exit equ 60
SYS_create equ 85
SYS_time equ 201
STRLEN equ 50
pmpt db "Enter Text:", NULL
newLinedb LF, NULL
section .bss
chr resb 1
inLine resb STRLEN+2
section .text
global _start
_start:
mov rdi, pmpt
call printString ; 打印提示内容
mov rbx, inLine ; 将buffer地址计入rbx
mov r12, 0 ; 计数r12
readCharacters:
mov rax, SYS_read; 调用sys_read
mov rdi, STDIN
lea rsi, byte ; 读取的结果写入chr
mov rdx, 1 ; 每次只读取一个字符
syscall
mov al, byte ; 将chr复制到 A 寄存器
cmp al, LF ; 判断是否是 \n, 如果是则直接结束.
je readDone
inc r12 ; 增加字符统计数
cmp r12, STRLEN ; 如果超过最大长度, 则直接结束, 反之继续
jae readCharacters
mov byte , al
inc rbx ; 将结果写入 inLine, 同时 ptr = ptr + 1
jmp readCharacters
readDone:
mov byte , NULL
mov rdi, inLine
call printString
exampleDone:
mov rax, SYS_exit
mov rdi, EXIT_SUCCESS
syscall
global printString
printString:
push rbx; -----;Count characters in string.
mov rbx, rdi
mov rdx, 0
strCountLoop:
cmp byte , NULL
je strCountDone
inc rdx
inc rbx
jmp strCountLoop
strCountDone:
cmp rdx, 0
je prtDone
mov rax, SYS_write
mov rsi, rdi
mov rdi, STDOUT
syscall
prtDone:
pop rbx
ret
</code></pre>
<h1 id="文件打开操作符">文件打开操作符</h1>
<p>为了进行文件操作,如读取和写入,必须首先打开文件。有两种文件打开操作,打开和打开/创建。这两种打开操作在以下章节中有所解释。文件打开后,为了执行文件读取或写入操作,操作系统需要关于文件的详细信息,包括完整状态和当前的读/写位置。这是必要的,以确保读取或写入操作能够从上一次结束的地方继续。</p>
<p>如果文件打开操作失败,将返回一个错误代码。如果文件打开操作成功,将返回一个文件描述符。这适用于高级语言和汇编代码。</p>
<p>操作系统使用文件描述符来访问关于文件的完整信息。关于一个打开文件的完整信息集合存储在一个名为文件控制块(FCB)的操作系统数据结构中。本质上,文件描述符被操作系统用来引用正确的FCB。程序员的责任是确保文件描述符被存储和正确使用。</p>
<h2 id="打开文件">打开文件</h2>
<p>文件打开操作要求文件必须存在才能被打开。如果文件不存在,那就是一个错误。文件打开操作还需要参数标志来指定访问模式。访问模式必须包括以下之一:</p>
<ul>
<li>只读访问 → O_RDONLY</li>
<li>只写访问 → O_WRONLY</li>
<li>读/写访问 → O_RDWR</li>
</ul>
<p>必须使用这些访问模式之一。通过与这些模式中的一个进行逻辑或(OR)操作,可以使用额外的访问模式。这可能包括如追加模式(本文未提及)。参见附录C,系统服务以获取关于文件访问模式的附加信息。</p>
<p>文件打开系统服务的参数如下:</p>
<ul>
<li>寄存器 SYS_open</li>
<li>rax 调用代码 = SYS_open (2)</li>
<li>rdi 空终止文件名字符串的地址</li>
<li>rsi 文件访问模式标志</li>
</ul>
<p>假设以下声明:</p>
<ul>
<li>SYS_open equ 2;文件打开</li>
<li>O_RDONLY equ 000000q;只读</li>
<li>O_WRONLY equ 000001q;只写</li>
<li>O_RDWR equ 000002q;读写</li>
</ul>
<p>应当注意,常量是以八进制或基数8定义的(如q后缀所指定)。这与Linux文件权限有时指定的方式相匹配。</p>
<p>系统调用之后,rax寄存器将包含返回值。如果文件打开操作失败,rax将包含一个负值(即<0)。具体的负值提供了遇到错误类型的指示。参见附录C,系统服务以获取关于错误代码的附加信息。典型的错误可能包括无效的文件描述符、文件未找到或文件权限错误。</p>
<p>如果文件打开操作成功,rax包含文件描述符。文件描述符将需要用于进一步的文件操作,并应当被保存。参见关于示例文件读取的部分以获取一个完整的打开文件的例子。</p>
<h2 id="文件打开创建">文件打开/创建</h2>
<p>文件打开/创建操作将创建一个文件。如果文件不存在,将创建一个新文件。如果文件已经存在,它将被擦除并创建一个新文件。因此,文件以前的内容将丢失。</p>
<p>必须指定一个文件访问模式。由于文件正在被创建,访问模式必须包括在创建文件时将设置的文件权限。这将包括为用户、组或全世界指定读、写和/或执行权限,这是Linux文件权限的典型做法。本例中仅涉及到的权限是对文件用户或所有者的权限。因此,其他用户(即使用其他帐户的用户)将无法访问我们程序创建的文件。参见附录C,系统服务以获取关于文件访问模式的附加信息。</p>
<p>文件打开/创建系统服务的参数如下:</p>
<ul>
<li>寄存器 SYS_create</li>
<li>rax 调用代码 = SYS_create (85)</li>
<li>rdi 空终止文件名字符串的地址</li>
<li>rsi 文件访问模式标志</li>
</ul>
<p>假设以下声明:</p>
<ul>
<li>SYS_create qu 85;文件打开</li>
<li>O_CREAT equ 0x40</li>
<li>O_TRUNC equ 0x200</li>
<li>O_APPEND equ 0x400</li>
<li>S_IRUSR equ 00400q;所有者,读权限</li>
<li>S_IWUSR equ 00200q;所有者,写权限</li>
<li>S_IXUSR equ 00100q;所有者,执行权限</li>
</ul>
<p>文件状态标志“S_IRUSR | S_IWUSR”将允许同时读写,这是典型的。"|" 是逻辑或(OR)操作,因此组合了选择。</p>
<p>如果文件打开/创建操作没有成功,rax寄存器中将返回一个负值。如果文件打开/创建操作成功,将返回一个文件描述符。文件描述符用于所有后续的文件操作。参见关于示例文件写入的部分以获取一个完整的文件打开/创建的示例。</p>
<h1 id="文件读取">文件读取</h1>
<p><img src="assets/image-20240413173837-e437j5d.png"><br>
这个和IO Read是几乎一致的, 不再重复了</p>
<h1 id="文件写入">文件写入</h1>
<p><img src="assets/image-20240413173908-ylckmp3.png"></p>
<h1 id="示例">示例</h1>
<p>示例的内容太长了, 请看原文吧.</p>
</blockquote>
<p></p><br><br>
来源:https://www.cnblogs.com/pDJJq/p/18133135/simple-tutorial-of-assembly-language-12-system-service-zjnwec
頁:
[1]