sys_lseek (调用号为8)是 Linux 中一个用于操作文件描述符读写位置的系统调用。它的核心功能是重新定位(移动)一个已打开文件的读写偏移量(offset)。这个偏移量决定了下一次读或写操作将从文件的什么位置开始。通过它,我们可以实现文件的随机访问(即跳转到文件任意位置进行读写),而不仅仅是顺序读写。
sys_lseek函数原型
1
off_t lseek(int fd, off_t offset, int whence);
参数
fd: 文件描述符,指向一个已打开的文件。offset: 相对于whence参数的偏移量,以字节为单位。whence: 决定偏移量计算起点的位置,可取以下值:SEEK_SET(0): 将偏移量设置为从文件开始算起的offset字节。SEEK_CUR(1): 将偏移量设置为从当前位置算起加上offset字节(offset可为负数,表示向前移动)。SEEK_END(2): 将偏移量设置为从文件末尾算起加上offset字节(offset可为负数,表示向前移动)。
返回值
- 成功时返回新的、移动后的文件偏移量(从文件开头计算的字节数)。
- 失败时返回
-errno(如EBADF,EINVAL)。
用例
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68.section .data
filename:
.ascii "/tmp/text.txt\0"
content:
.ascii "HelloWorld\0" # 文件内容
len = . - content
buffer:
.space 5 # 用于读取数据的缓冲区
.section .text
.globl _start
_start:
# 1. 打开文件 (sys_open)
mov $2, %rax # 系统调用号 (2 = sys_open)
lea filename(%rip), %rdi # 文件名
mov $0101, %si # 标志位 (O_CREAT | O_WRONLY)
mov $0666, %dx # 模式
syscall
mov %rax, %r8 # 保存文件描述符到 %r8
# 2. 写入内容 content (sys_write)
mov $1, %rax # 系统调用号 (1 = sys_write)
mov %r8, %rdi # 文件描述符
lea content(%rip), %rsi # 要写入的数据
mov $len, %rdx # 数据长度
syscall
# 3. 关闭文件 (sys_close)
mov $3, %rax # 系统调用号 (3 = sys_close)
mov %r8, %rdi
syscall
# 4. 重新以只读方式打开文件 (sys_open)
mov $2, %rax
lea filename(%rip), %rdi
xor %rsi, %rsi # 标志位 (O_RDONLY)
syscall
mov %rax, %r8 # 保存新的文件描述符
# 5. 使用 sys_lseek 跳转到第6个字节('W'的位置)
mov $8, %rax # 系统调用号 (8 = sys_lseek)
mov %r8, %rdi # 文件描述符
mov $5, %rsi # offset = 5 (从起点后第5个字节,即第6个)
mov $0, %rdx # whence = SEEK_SET (0)
syscall
# 6. 从新位置读取5个字节 (sys_read)
mov $0, %rax # 系统调用号 (0 = sys_read)
mov %r8, %rdi
lea buffer(%rip), %rsi # 读取缓冲区
mov $5, %rdx # 读取5个字节
syscall
# 7. 将读取到的内容输出到标准输出 (sys_write)
mov $1, %rax
mov $1, %rdi # 文件描述符 (1 = stdout)
lea buffer(%rip), %rsi
mov $5, %rdx
syscall
# 8. 关闭文件并退出
mov $3, %rax # sys_close
mov %r8, %rdi
syscall
mov $60, %rax # sys_exit
xor %rdi, %rdi
syscall分析:
上述代码演示了
sys_lseek的一个典型用法:跳转到文件中间进行读取。程序首先创建并打开一个名为
test.txt的文件,写入字符串"HelloWorld"(共 10 字节),然后关闭它。然后,重新以只读模式打开该文件。此时文件偏移量在开头 (0)。此时,程序开始调用sys_lseek。我们首先将,系统调用号8存入%rax,文件描述符存入%rdi,偏移量5存入%rsi(我们希望跳过前5个字节"Hello"),whence参数SEEK_SET(0) 存入%rdx,表示从文件开头计算偏移。执行后,文件偏移量被设置为
5。紧接着调用sys_read,它会从新的偏移量(第 5 字节后,即第 6 个字节)开始读取。这里读取了 5 个字节,内容将是"World"。随后,程序开始将这个结果"World"写入标准输出。最终效果是,程序没有读取文件的开头,而是直接读取并输出了文件的后半部分。