本节书摘来自异步社区《linux系统编程(第2版)》一书中的第2章,第2.7节,作者:【美】robert love著,更多章节内容可以访问云栖社区“异步社区”公众号查看
一般情况下,i/o是线性的,由于读写引发的隐式文件位置更新都需要seek操作。但是,某些应用要跳跃式读取文件,需要随机访问而不是线性访问。lseek()系统调用能够将文件描述符的位置指针设置成指定值。lseek()只更新文件位置,没有执行其他操作,也并不初始化任何i/o:

lseek()调用的行为依赖于origin参数,该参数可以是以下任意值之一:
seek_cur
将文件位置设置成当前值再加上pos个偏移值,pos可以是负值、0或正值。如果pos值为0,返回当前文件位置值。
seek_end
将文件位置设置成文件长度再加上pos个偏移值,pos可以是负值、0或正值。如果pos值为0,就设置成文件末尾。
seek_set
将文件位置设置成pos值。如果pos值为0,就设置成文件开始。
调用成功时返回新的文件位置,错误时返回-1,并相应设置errno值。
举个例子,以下代码把fd的文件位置指针设置为1825:
下面是把fd的文件位置设置成文件末尾:
由于lseek()返回更新后的文件位置,可以通过seek_cur,把偏移pos设置成0,确定当前文件位置:
到目前为止,lseek()调用最常见的用法是将指针定位到文件的开始、末尾或确定文件描述符的当前文件位置。
2.7.1 在文件末尾后查找
lseek()支持在文件末尾之后进行查找。例如,以下代码会定位到fd指向文件末尾之后的1688字节。
对这种用法本身而言,查找到文件末尾之后没什么意义——对该新的文件位置的读请求会返回eof。但是,如果在该位置有个写请求,在文件的旧长度和新长度之间的空间会用0来填充。
这种零填充区间称为“空洞(hole)”。在unix系文件系统上,空洞不占用任何物理磁盘空间。这意味着文件系统上所有文件的大小加起来可以超过磁盘的物理大小。包含空洞的文件称为“稀疏文件(sparse file)”。稀疏文件可以节省很多空间,并提升性能,因为操作空洞不会产生任何物理i/o。
对文件空洞部分的读请求会返回相应的二进制0。
2.7.2 错误码
lseek()调用出错时,返回-1,并将errno值设置成如下四个值之一:
ebadf
给定的文件描述符没有指向任何打开的文件描述符。
einval
origin的值不是设置成seek_set、seek_cur或seek_end,或者结果文件位置是负值。对于einval,如果同时出现以上两种错误就太糟了。前者几乎可以肯定是个编译时错误,后者则是不太明显的运行时逻辑错误。
eoverflow
结果文件偏移不能通过off_t表示。只有在32位的体系结构上才会发生这种错误。当前文件位置已经更新,该错误表示无法返回更新的值。
espipe
给出的文件描述符和不支持查找操作的对象关联,比如管道、fifo或socket。
2.7.3 限制
最大文件位置是受限于off_t类型的大小。大部分计算机体系结构定义该值为c long类型,在linux上是指字长(word size)(即计算机的通用寄存器大小)。但是,内核在内部实现上是把偏移存储成c long long类型。这对于64位计算机没有什么问题,但是对于32位计算机,当执行查找操作时,可能会产生溢出eoverflow的错误。