UNIX文件处理
专栏目录(文章在更新中)
> 汇编及C/C++汇编调用约定(汇总帖)
> 汇编编译和gdb调试命令列表
> gdb TUI使用方法
> 汇编C语言调用约定(标准函数)
> 汇编C语言调用约定(递归函数)
> C++内存模型以及寄存器指针rsp和rbp
1. 一些系统调用号
open
— 5
write
— 4
read
— 3
close
— 6
exit
— 1
2. 设置.bss缓冲区
.lcomm指令将创建一个符号my_buffer,指代我们用作缓冲区的500字节的存储位置
.section .bss
.lcomm my_buffer, 500
打开一个文件进行读取,文件描述符放置在%ebx中
3. gdb 设置参数
- 汇编命令行参数在内存中的位置参考这篇帖子:linux平台学x86汇编(十五):使用命令行参数
- 设置参数:
run args1 args2 ...
gdb --args ./testprg arg1 arg2
set args
— Set argument list to give program being debugged when it is started.
- 在gdb中查看参数:
4. 文件操作
- 打开文件
- open,系统调用5,当通过一个文件名打开一个文件时,系统会提供一个编号,即文件描述符,用它来指代该文件,直到使用close。关闭文件后文件描述符及失效。
- 打开文件时文件名第一个字符的地址应存放在%ebx,以数字表示的读/写意图应存放在%ecx,权限集合应作为数字存储在%edx中。
- Linux将返回文件描述符到%eax,即将在整个程序中用这个数字来指代这一文件。
- 打开输入文件:
.equ SYS_OPEN, 5 .equ ST_ARGV_1, 8 .equ O_RDONLY, 0 .equ LINUX_SYSCALL, 0X80 .equ ST_FD_IN, -4 movl $SYS_OPEN, %eax movl $ST_ARGV_1(%ebp), %ebx movl $O_RDONLY, %ecx movl $0666, %edx int $LINUX_SYSCALL # 保存返回的文件描述符 movl %eax, ST_FD_IN(%ebp)
- 打开输出文件:
.equ SYS_OPEN, 5 .equ ST_ARGV_2, 12 .equ O_CREAT_WRONLY_TRUNC, 03101 .equ LINUX_SYSCALL, 0X80 .equ ST_FD_IN, -8 movl $SYS_OPEN, %eax movl $ST_ARGV_2(%ebp), %ebx movl $O_CREAT_WRONLY_TRUNC, %ecx movl $0666, %edx int $LINUX_SYSCALL # 保存返回的文件描述符 movl %eax, ST_FD_OUT(%ebp)
- 关闭文件
- close,系统调用6,close的唯一参数就是文件描述符,应存储在%ebx中。
.equ SYS_CLOSE, 6 .equ ST_FD_OUT, -8 .equ LINUX_SYSCALL, 0X80 movl $SYS_CLOSE, %eax movl ST_FD_OUT(%ebp), %ebx int $LINUX_SYSCALL
- close,系统调用6,close的唯一参数就是文件描述符,应存储在%ebx中。
- 设置.bss缓冲区
.section .bss
.equ BUFFER_SIZE, 500
.lcomm BUFFER_DATA, BUFFER_SIZE
- 从文件中读/写数据到缓冲区
- 读过程 :将文件描述符存入%ebx,将存储数据的缓冲区地址存入%ecx,将缓冲区大小放入%edx,read将返回从文件中读取的字符数或错误代码(负数)
.section .bss .equ BUFFER_SIZE, 500 .lcomm BUFFER_DATA, BUFFER_SIZE .section .text .equ SYS_READ, 3 .equ ST_FD_IN, -4 movl $SYS_READ, %eax movl $ST_FD_IN(%ebp), %ebx movl $BUFFER_DATA, %ecx movl $BUFFER_SIZE, %edx int $LINUX_SYSCALL cmpl $END_OF_FILE, %eax jle end_loop
- 写过程:写的参数与读相同,唯一的区别是缓冲区应该已经填满了要写入的数据,将把写入的字节数或错误代码存入%eax
.section .bss .equ BUFFER_SIZE, 500 .lcomm BUFFER_DATA, BUFFER_SIZE .section .text .equ SYS_WRITE, 4 .equ ST_FD_OUT, -8 # eax 保存的是文件写入缓冲区后缓冲区的大小 movl %eax, %edx movl $SYS_READ, %eax movl $ST_FD_IN(%ebp), %ebx movl $BUFFER_DATA, %ecx int $LINUX_SYSCALL
- 读过程 :将文件描述符存入%ebx,将存储数据的缓冲区地址存入%ecx,将缓冲区大小放入%edx,read将返回从文件中读取的字符数或错误代码(负数)
5. 关于函数初始化时的栈指针偏移
.equ ST_SIZE_RESERVE, 8
.equ ST_FD_IN, -4
.equ ST_FD_OUT, -8
.globl _start
_start:
movl %esp, %ebp
subl $ST_SIZE_RESERVE, %esp
# 通过此将数据存入预留的栈空间中
store_fd_in:
movl %eax, ST_FD_IN(%ebp)
store_fd_out:
movl %eax, ST_FD_OUT(%ebp)
6. 完整示例
.section .data
# 系统调用号
.equ SYS_OPEN, 5
.equ SYS_WRITE, 4
.equ SYS_READ, 3
.equ SYS_CLOSE, 6
.equ SYS_EXIT, 1
# 文件打开选项
.equ O_RDONLY, 0
.equ O_CREAT_WRONLY_TRUNC, 03101
# 标准文件描述符
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2
# 系统调用中断
.equ LINUX_SYSCALL, 0x80
.equ END_OF_FILE, 0 # 读操作返回值 表明到达文件结尾
.equ NUMBER_ARGUMENTS, 2
.section .bss
.equ BUFFER_SIZE, 500
.lcomm BUFFER_DATA, BUFFER_SIZE
.section .text
.equ ST_SIZE_RESERVE, 8
.equ ST_FD_IN, -4
.equ ST_FD_OUT, -8
.equ ST_ARGC, 0 # 参数数量
.equ ST_ARGV_0, 4 # 程序名
.equ ST_ARGV_1, 8 # 第一个参数
.equ ST_ARGV_2, 12 # 第二个参数
.globl _start
_start:
movl %esp, %ebp
subl $ST_SIZE_RESERVE, %esp
open_files:
open_fd_in:
movl $SYS_OPEN, %eax
movl ST_ARGV_1(%ebp), %ebx
movl $O_RDONLY, %ecx
movl $0666, %edx
int $LINUX_SYSCALL
store_fd_in:
movl %eax, ST_FD_IN(%ebp)
open_fd_out:
movl $SYS_OPEN, %eax
movl ST_ARGV_2(%ebp), %ebx
movl $O_CREAT_WRONLY_TRUNC, %ecx
movl $0666, %edx
int $LINUX_SYSCALL
store_fd_out:
movl %eax, ST_FD_OUT(%ebp)
read_loop_begin:
movl $SYS_READ, %eax
movl ST_FD_IN(%ebp), %ebx
movl $BUFFER_DATA, %ecx
movl $BUFFER_SIZE, %edx
int $LINUX_SYSCALL
# 返回缓冲区中读取到的大小存入%eax
cmpl $END_OF_FILE, %eax
jle end_loop
continue_read_loop:
# 将缓冲区和缓冲区大小入栈
pushl $BUFFER_DATA
pushl %eax
call convert_to_upper
# 释放压栈的缓冲区和缓冲区大小
popl %eax
addl $4, %esp
movl %eax, %edx
movl $SYS_WRITE, %eax
movl ST_FD_OUT(%ebp), %ebx
movl $BUFFER_DATA, %ecx
int $LINUX_SYSCALL
jmp read_loop_begin
end_loop:
movl $SYS_CLOSE, %eax
movl ST_FD_OUT(%ebp), %ebx
int $LINUX_SYSCALL
movl $SYS_CLOSE, %eax
movl ST_FD_IN(%ebp), %ebx
int $LINUX_SYSCALL
movl $SYS_EXIT, %eax
movl $0, %ebx
int $LINUX_SYSCALL
.equ LOWERCASE_A, 'a'
.equ LOWERCASE_Z, 'z'
.equ UPPER_CONVERSION, 'A' - 'a'
# 栈上缓冲区基址寻址
.equ ST_BUFFER_LEN, 8
.equ ST_BUFFER, 12
convert_to_upper:
pushl %ebp
movl %esp, %ebp
movl ST_BUFFER(%ebp), %eax
movl ST_BUFFER_LEN(%ebp), %ebx
movl $0, %edi
cmpl $0, %ebx
je end_convert_loop
convert_loop:
movb (%eax, %edi, 1), %cl
cmpb $LOWERCASE_A, %cl
jl next_byte
cmpb $LOWERCASE_Z, %cl
jg next_byte
addb $UPPER_CONVERSION, %cl
movb %cl, (%eax, %edi, 1)
next_byte:
incl %edi
cmpl %edi, %ebx
jne convert_loop
end_convert_loop:
movl %ebp, %esp
popl %ebp
ret