linux的文件是unix的文件处理方式,因此,无论linux文件是什么程序创建的,都可以作为连续的字节流进行访问,明确这一点是相当重要的。
首先看看几个关于文件的系统调用号
3 //读
4 //写
5 //打开
6 //关闭
如果想知道更多linux系统调用号请移步Linux系统调用号。
对文件的操作都需要系统来完成,因此当我们需要打开或者读写一个文件时,需要向系统发出中断信号,由系统来完成这些操作,因为操作系统的安全性,这些对文件的操作都是放在管态中执行的,向系统发送中断信号都是通知cpu从目态进入管态。所以在文件读写中,向系统发送中断信号是必不可少的。
当我们需要打开一个文件时,我们要告诉linux打开的文件名,并以特定的方式打开(read,write,create等),这是通过open系统调用处理的,通常来说,需要对文件操作都要经过打开这一步骤,但是有三个文件例外,这三个文件不是说不需要打开,而是系统在程序运行的过程中就已经帮我们完成了,它们是stdin,stdout,stderr分别对应标准输入文件,标准输出文件,标准错误文件,stdin通常指代键盘,用来接收键盘的输入,stdout通常指带屏幕,用来输出东西在屏幕上,stderr,当有错误发生的时,可以往里面写入错误信息。
在打开文件之前,我们需要将系统调用号放入%eax,文件的首地址放在%ebx中,以数字表示的读写意图放在%ecx中(通常用0来表示读,03101表示写),权限集合作为数字放在%edx中(默认为0666)。
把指定的信息放入相应的寄存器之后,就可以向系统发送中断信号来通知打开文件了。
打开文件过后,文件描述符通常放在%eax中,文件描述符可以代表一个文件,在整个程序中你都可以通过文件描述符对文件进行操作,在文件使用完毕后,你可以通知linux将文件关闭,这时,你的文件描述符不会有效。
以下是一个简单的打开文件示例,我们以打开当前目录下的data文件为例
openFile.s
.section .data
.section .text
.globl _start
_start:
mov %esp, %ebp
#系统调用号
movl $5, %eax
#文件名
movl 8(%ebp), %ebx
#读写意图为只读
movl $0, %ecx
#系统权限
movl $0666, %edx
#系统中断
int $0x80
movl %eax, %ebx
movl $1, %eax
int $0x80
汇编连接运行
as openFile.s -o openFile.o
ld openFile.o -o openFile
./openFile data
打开文件之后,我们可以对文件进行读的操作,为了进行该调用,你必须将文件描述符放入%ebx,将储存数据的缓冲地址放入%ecx,将缓冲区的大小放入%edx,读操作返回读取的字符数或者一个错误代码,错误代码通常是负数。
写入文件和读取文件参数基本一致,唯一的区别是缓冲区里应该已经填满了要写的数据。
下面我上一个示例,在data文件中,我写上一些信息,然后写一个汇编程序,将该文件里的内容输出到屏幕上:
为了方便我会用.equ定义一系列的别名
test.s
.section .data
#系统调用号
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_OPEN, 5
.equ SYS_CLOSE, 6
#文件打开选项
.equ FILE_READONLY, 0
.equ FILE_WRITE, 03101
#系统调用中断
.equ SYS_INTERRUP, 0x80
#标准文件描述符
.equ STD_IN, 0
.equ STD_OUT, 1
.equ STD_ERR, 2
.section .bss
#申请缓冲区20byte
.equ BUFFER_SIZE, 20
.lcomm BUFFER_DATA, BUFFER_SIZE
.section .text
.globl _start
_start:
#文件名入栈
movl %esp, %ebp
pushl 8(%ebp)
# 打开文件
call openFile
addl $4, %esp
# 文件描述符保存在%eax
pushl %eax
# 读取文件
call readFile
addl $4, %esp
call writeFile
movl %eax, %ebx
movl $SYS_EXIT, %eax
int $SYS_INTERRUP
#系统调用打开文件
.type openFile, @function
openFile:
pushl %ebp
movl %esp, %ebp
#系统调用号
movl $SYS_OPEN, %eax
#文件名
movl 8(%ebp), %ebx
#读写意图
movl $FILE_READONLY, %ecx
#系统权限
movl $0666, %edx
#系统中断
int $SYS_INTERRUP
popl %ebp
ret
#读取文件
.type readFile, @function
readFile:
pushl %ebp
movl %esp, %ebp
#文件描述符
movl 8(%ebp), %ebx
#系统调用号
movl $SYS_READ, %eax
#缓冲区
movl $BUFFER_DATA, %ecx
#缓冲区大小
movl $BUFFER_SIZE, %edx
#系统调用
int $SYS_INTERRUP
popl %ebp
ret
#写入文件
.type writeFile, @function
writeFile:
pushl %ebp
movl %esp, %ebp
#文件描述符
movl $1, %ebx
#系统调用号
movl $SYS_WRITE, %eax
#缓冲区
movl $BUFFER_DATA, %ecx
#缓冲区大小
movl $BUFFER_SIZE, %edx
#系统调用
int $SYS_INTERRUP
popl %ebp
ret
我们在data里写上
hello assembly
汇编连接
$as test.s -o test.o
$ld test.o -o test
$./test data
hello assembly
大功告成!这里为了划分功能模块,把他们都写成了函数。值得注意的是,在进行传入文件名操作的时候,文件名为当前esp的往上8个地址