《UNIXLinux程序设计教程》一3.3 设置描述字的文件位置

3.3 设置描述字的文件位置

如同可以用fseek()设置流的文件位置一样,对文件描述字也有类似的操作。函数lseek()可改变一个描述字相连文件的文件位置。

#include <sys/types.h>
#include <unistd.h>

off_t lseek (int filedes,off_t offset,int whence);

参数filedes给出已打开的文件描述字,offset指明相对whence的位移字节数,whence的取值同fseek()一样,只能是符号常数SEEK_SET、SEEK_CUR、SEEK_END之一,分别指明offset是相对文件开始、当前文件位置还是文件尾的偏移。
lseek()的返回值是新文件位置相对文件开始的字节数。例如,如下调用
lseek(fd,0L,SEEK_SET);
lseek(fd,0L,SEEK_END);
分别移动文件位置到文件开始和文件尾。而
n = lseek(fd,0,SEEK_CUR);
读当前文件位置值。描述字没有专门的与ftell()类似的函数。
lseek()只移动文件的当前位置,它并不引起任何I/O动作。文件的新位置可以大于文件的当前大小,在这种情况下,下一次写将扩展该文件。这种情况也称为在文件中生成“空洞”,因为系统并不真正在磁盘为这段区间保留存放空间。如果在数据尾部之后到lseek()设置的偏移量之前这段区域的数据尚未写入,则后继读这段区域将读出0,程序3-2是这种情况的一个示例程序。
例3-2 程序3-2用lseek创建一个含有空洞的文件。
《UNIXLinux程序设计教程》一3.3 设置描述字的文件位置

运行这个程序可看到如下结果:

% rm file.hole                          /* 保证file.hole是新创建的文件 */
% a.out
% ls -l file.hole                       /* 查看文件大小 */
-rw-r--r--   1 zkj      users         50 Nov  2 15:27 file.hole
% od -c file.hole                       /* 查看文件的实际内容 */
0000000    a   b   c   d   e   f   g   h   i   j  \0  \0  \0  \0  \0  \0
0000020   \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040   \0  \0  \0  \0  \0  \0  \0  \0   A   B   C   D   E   F   G   H
0000060    I   J

od命令用于查看文件的内容,'-c'选项指定打印内容为字符。其输出内容中左边一列的7个数字为文件位移。我们可以看到30个未写的字节被读出为0,实际上这些字节在未写之前并不占用磁盘空间。
如果放开open调用中对O_APPEND标志的注释之后重新编译0并运行它,则将看到不同的结果:

% rm file.hole
% a.out
% ls -l file.hole
-rw-r--r--   1 zkj      users         20 Nov  2 16:30 file.hole
% od -c file.hole
0000000    a   b   c   d   e   f   g   h   i   j   A   B   C   D   E   F
0000020    G   H   I   J
0000024

结果显示紧接在lseek()之后由write()所写的内容并不在lseek()指定的文件位置。出现这种情况的原因是:1)lseek()只改变文件位置而不进行任何实际I/O动作,因而它不会改变inode中的文件大小。也就是说,即使lseek()设置的文件位置超过当前文件尾,它也不会导致当前文件尾发生改变。2)每当对用O_APPEND标志打开的文件执行write()时,当前文件位置将首先移到由inode给出的当前文件大小所指出的位置(回顾3.1节末尾指出的第4点),这强制每一次write()只能在当前文件尾添加数据。这两个原因导致lseek()定位的文件位置在这种情况下不起作用。不论将文件位置移至超过文件尾多远,对于用O_APPEND标志打开的文件,下一次写总是写在当前文件尾,并且当前文件位置将从文件尾起增加所写的字节数。
类似地,对如下调用

lseek(fd, 40, SEEK_END);
write(fd, buf2, 10);

write()写入的10个字节将紧接在当前文件尾之后,而不是在当前文件尾第40个字节之后(参见3.1节末尾提到的第5点)。
当同一个文件有多个打开的描述字时(被多次打开或由dup()重复),由不同open()得到的文件描述字具有独立的文件位置。lseek()只作用于指定的描述字而不会对另外的描述字造成影响,例如:

{
   int d1,d2;
   char buf[4];
   d1 = open("foo",O_RDONLY);
   d2 = open("foo",O_RDONLY);
   lseek(dl,1024,SEEK_SET);
   read(d2,buf,4);
}

read()将读文件foo的前4个字符。但是,由dup()重复而得的描述字与原描述字共享一个公共的文件位置,改变两个重复描述字中一个的文件位置,包括读或写数据,同时也改变了另一个描述字的文件位置。例如:

{
    int d1,d2;
    char buf1[80],buf2[80];
    d1 = open("foo",O_RDONLY);
    d2 = dup(d1);
    lseek(d1,1024,SEEK_SET);
    read(d1,buf1,4);
    read(d2,buf2,4);
}

第一个read()将从foo的第1024个字符处开始读4个字节,第二个read()则从第1028个字符处再读4个字节。

上一篇:Oozie安装的说明


下一篇:linux断电关机后,进度条满后卡在那里