sys_open(即调用号为2的系统调用)与sys_close(即调用号为3的系统调用)是 Linux 中的两个基本系统调用,用于对文件描述符进行相关操作。
sys_open会将被打开的文件路径转换为文件描述符int fd,该描述符可用于后续的读写操作。
sys_open函数原型
1
int open(const char * pathname, int flags, mode_t mode);
参数
pathname: 要打开的文件路径(字符串)flags: 打开标志,指定文件的打开方式(具体说明见下)mode: 创建文件时的权限模式(仅在使用O_CREAT时有效)(具体说明见下)
返回值
- 成功时返回文件描述符(非负整数
fd) - 失败时返回
-errno(如ENOENT,EACCES,EMFILE)
- 成功时返回文件描述符(非负整数
sys_close函数原型
1
int close(int fd);
参数
fd: 要关闭的文件描述符
返回值
- 成功时返回
0 - 失败时返回
-errno(如EBADF,EINTR,EIO)
- 成功时返回
flags的定义flags的赋值通常是由“基本访问模式”与“附加标志”通过或逻辑所附加而来的。在 Linux 的头文件fcntl.h中,这二者的定义如下:基本访问模式
宏定义 值 含义 O_RDONLY0只读打开 O_WRONLY1只写打开 O_RDWR2读写打开 附加标志
宏定义 值(八进制) 值(十六进制) 说明 O_CREAT00001000x40文件不存在则创建 O_EXCL00002000x80和 O_CREAT一起用,文件存在则失败O_NOCTTY00004000x100不把文件作为控制终端 O_TRUNC00010000x200截断已存在文件为 0长度O_APPEND00020000x400写操作追加到文件末尾 O_NONBLOCK00040000x800非阻塞 I/O O_NDELAY00040000x800与 O_NONBLOCK相同O_DSYNC00100000x1000数据同步写入 O_SYNC040100000x101000数据+元数据同步写入 O_RSYNC040100000x101000与 O_SYNC相同(兼容)FASYNC00200000x2000异步通知 O_DIRECT00400000x4000直接 I/O,绕过内核缓存 O_LARGEFILE001000000x8000大文件支持(32位) O_DIRECTORY002000000x10000必须是目录,否则出错 O_NOFOLLOW004000000x20000不跟随符号链接 O_NOATIME010000000x40000不更新时间戳 O_CLOEXEC020000000x80000设置 FD_CLOEXEC,exec 时关闭O_PATH0100000000x200000只获取路径引用,不打开文件 O_TMPFILE0200000000x400000创建匿名临时文件(需 O_DIRECTORY)O_TMPFILE_MASK03777700000- 内部掩码,用于屏蔽 O_TMPFILE
上述所有宏在给某一
flags赋值时,使用按位或运算符|进行连接。如:1
int flags = O_WRONLY | O_CREAT;
该
flags意为:打开文件时采用只写模式;且如果文件不存在,则自动创建之。可以想到,基本访问模式这三者只能独立存在,不能同时存在于一个表达式中。
权限模式的定义
Unix 系统的权限模式由三个部分所组成:所有者(
owner)的权限、所有者所在用户组中的其他用户(group)的权限、非以上用户的其他用户(others)的权限,通过数字进行区分。Unix 系统的权限主要有三种:读(
r)、写(w)、执行(x)。如下:权限 二进制 八进制 ---000 0 --x001 1 -w-010 2 -wx011 3 r--100 4 r-x101 5 rw-110 6 rwx111 7 其中
-表示用户不具有某一种权限。每一类用户通过八进制数来表示自身的权限。三类用户的排列顺序为:owner-group-others。如一个文件对所有用户可读、可写、可执行,那么它的权限模式为:
rwxrwxrwx,亦即0777。用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39.equ FLAGS, 0x241 # O_WRONLY | O_CREAT | O_TRUNC
.section .data
filename:
.asciz "/tmp/test.txt" # 要打开的文件路径
buffer:
.ascii "Hello from sys_open!\n\0"
buffer_len = . - buffer
.section .text
.globl _start
_start:
# 调用 sys_open 创建/打开文件
mov $2, %rax # 系统调用号 (2 = sys_open)
lea filename(%rip), %rdi # const char *pathname = &filename
mov $FLAGS, %rsi # 通过宏定义给 flags 赋值
mov $0644, %rdx # mode = 0644 (rw-r--r--)
syscall # 执行系统调用
# 保存文件描述符
mov %rax, %r8 # 保存文件描述符到 %r8
# 使用 sys_write 向文件写入数据
mov $1, %rax # 系统调用号 (1 = sys_write)
mov %r8, %rdi # 使用刚才打开的文件描述符
lea buffer(%rip), %rsi # const void *buf = &buffer
mov $buffer_len, %rdx # count = buffer_len
syscall # 执行系统调用
# 使用 sys_close 关闭文件
mov $3, %rax # 系统调用号 (3 = sys_close)
mov %r8, %rdi # 关闭文件描述符
syscall # 执行系统调用
# 退出
mov $60, %rax # 系统调用号 (60 = sys_exit)
xor %rdi, %rdi # return 0
syscall分析:
上述代码演示了如何使用
sys_open系统调用创建并打开一个文件进行写入操作。程序首先定义了要打开的文件路径
filename和要写入的数据buffer。在_start标签处,程序将系统调用号2(sys_open)存入%rax寄存器,将文件路径地址加载到%rdi,设置打开标志为0x241(O_WRONLY | O_CREAT | O_TRUNC,表示只写、如不存在则创建、截断为0长度),并设置文件权限为0644。这个操作,用 C 语言来写的话,就是:1
int fd = open("/tmp/test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
在执行
syscall指令后,系统调用会尝试打开指定的这个文件。如果成功,文件描述符会存储在%rax中;如果失败,%rax将包含负值。成功打开文件后,我们将文件描述符备份到
%r8寄存器,以防止在后面调用到其他系统调用时覆盖了原本的文件描述符。然后,我们使用sys_write向该文件中写入我们准备好的数据。最后,程序使用sys_close(系统调用号为3)关闭文件描述符,释放系统资源,并调用sys_exit正常退出。