到第4章了,不知甚么时候才能把这本书看完,耽误的时间太多了。
第4章是在第3章的基础上,主要描写文件系统的其他性质和文件的性质。
4.2 stat、fstat、fstatat、lstat函数
首先来看看这4个函数的原型:
#include <sys/stat.h> ///usr/include/x86_64-linux-gnu/sys/
int stat (const char *__restrict __file, struct stat *__restrict __buf)
int fstat (int __fd, struct stat *__buf)
int fstatat (int __fd, const char *__restrict __file, struct stat *__restrict __buf, int __flag)
int lstat (const char *__restrict __file, struct stat *__restrict __buf)
这几个函数的功能概括起来就是:取得stat结构体。区分就在于stat函数通过文件名取得这1结构体;fstat通过文件描写符;lstat返回该符号链接的有关信息,而不是由该符号链接援用的文件的信息;fstatat返回当前文件夹下(由fd指向)某个文件(文件名为file)的stat结构体。
好了,不管上面的几个函数具体功能为什么,其核心都是返回stat结构体,stat定义以下:
struct stat
{
__dev_t st_dev; /* Device. */
#ifndef __x86_64__
unsigned short int __pad1;
#endif
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
__ino_t st_ino; /* File serial number. */
#else
__ino_t __st_ino; /* 32bit file serial number. */
#endif
#ifndef __x86_64__
__mode_t st_mode; /* File mode. */
__nlink_t st_nlink; /* Link count. */
#else
__nlink_t st_nlink; /* Link count. */
__mode_t st_mode; /* File mode. */
#endif
__uid_t st_uid; /* User ID of the file's owner. */
__gid_t st_gid; /* Group ID of the file's group.*/
#ifdef __x86_64__
int __pad0;
#endif
__dev_t st_rdev; /* Device number, if device. */
#ifndef __x86_64__
unsigned short int __pad2;
#endif
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
__off_t st_size; /* Size of file, in bytes. */
#else
__off64_t st_size; /* Size of file, in bytes. */
#endif
__blksize_t st_blksize; /* Optimal block size for I/O. */
#if defined __x86_64__ || !defined __USE_FILE_OFFSET64
__blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */
#else
__blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */
#endif
#ifdef __USE_XOPEN2K8
/* Nanosecond resolution timestamps are stored in a format
equivalent to 'struct timespec'. This is the type used
whenever possible but the Unix namespace rules do not allow the
identifier 'timespec' to appear in the <sys/stat.h> header.
Therefore we have to handle the use of this header in strictly
standard-compliant sources special. */
struct timespec st_atim; /* Time of last
access. */
struct timespec st_mtim; /* Time of last modification. */
struct timespec st_ctim; /* Time of last status change. */
# define st_atime st_atim.tv_sec /* Backward compatibility. */
# define st_mtime st_mtim.tv_sec
# define st_ctime st_ctim.tv_sec
#else
__time_t st_atime; /* Time of last
access. */
__syscall_ulong_t st_atimensec; /* Nscecs of last
access. */
__time_t st_mtime; /* Time of last modification. */
__syscall_ulong_t st_mtimensec; /* Nsecs of last modification. */
__time_t st_ctime; /* Time of last status change. */
__syscall_ulong_t st_ctimensec; /* Nsecs of last status change. */
#endif
#ifdef __x86_64__
__syscall_slong_t __glibc_reserved[3];
#else
# ifndef __USE_FILE_OFFSET64
unsigned long int __glibc_reserved4;
unsigned long int __glibc_reserved5;
# else
__ino64_t st_ino; /* File serial number. */
# endif
#endif
};
4.3文件类型
看过了上面的stat结构体,随便感受了1下stat结构体的内容,接下来对其中几个重要的字段进行研究,首先来看:
__mode_t st_mode;
先来看看“ __mode_t”的类型,相干定义位于:/usr/include/x86_64-linux-gnu/bits/type.h中,具体定义以下:
# define __STD_TYPE typedef
__STD_TYPE __MODE_T_TYPE __mode_t;
#define __MODE_T_TYPE __U32_TYPE ///usr/include/x86_64-linux-gnu/bits/typesize.h
#define __U32_TYPE unsigned int
这1大堆,来回来去的。
根据APUE,文件共包括以下几类:
- 普通文件
- 目录文件
- 块特殊文件
- 字符特殊文件
- FIFO
- 套接字
- 符号链接
好了,再来看看linux的有关内容,还是位于/usr/include/x86_64-linux-gnu/sys/stat.h中
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode) __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode) __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
#ifdef __S_IFIFO
# define S_ISFIFO(mode) __S_ISTYPE((mode), __S_IFIFO)
#endif
#ifdef __S_IFLNK
# define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK)
#endif
# define S_ISSOCK(mode) __S_ISTYPE((mode), __S_IFSOCK)
#ifdef __USE_POSIX199309
# define S_TYPEISMQ(buf) __S_TYPEISMQ(buf)
# define S_TYPEISSEM(buf) __S_TYPEISSEM(buf)
# define S_TYPEISSHM(buf) __S_TYPEISSHM(buf)
#endif
好了,通过上面的定义可以了解到这些宏定义说到底就是通过1个参数与__S_IFMT进行“与运算”,得到值后与函数的本来功能进行比较,最后返回0或1。
在上1章中我们留下了1个疑问,通过lseek函数可以设定文件的偏移量,但如果文件描写符指向管道、FIFO(命名管道)、网络套接字,则lseek返回⑴,并将errno设置为ESPIPE。所以我们得到结论文件描写符0、1、2是管道或命名管道。现在终究可以通过实验验证1下了:
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
struct stat buf;
fstat(STDIN_FILENO,&buf);
if(S_ISFIFO(buf.st_mode));
printf("stdin is fifo\n");
fstat(STDOUT_FILENO,&buf);
if(S_ISFIFO(buf.st_mode));
printf("stdout is fifo\n");
fstat(STDERR_FILENO,&buf);
if(S_ISFIFO(buf.st_mode));
printf("stderr is fifo\n");
return 0;
}
运行结果以下:
./test_stat
stdin is fifo
stdout is fifo
stderr is fifo
通过以上实验也验证了我们的推测——文件描写符0、1、2是管道或命名管道。
4.4 节主要讨论设置用户ID与用户组ID的问题,书中给出了1些分析,不过我感觉说的不是很清楚,所以给大家分享1篇blog:http://blog.chinaunix.net/uid⑴8905703-id⑶843177.html
关于以上概念,结合我看到的1些资料,给大家做1个简单的总结。关于这几个概念的辨别,还是从现象动身,1个程序要履行首先要知道程序的“有功效户ID”、“有效组ID”、“附属组ID”,知道了以上3个“ID”后还不够,我们还要检查当前的用户“ID”是不是与以上3个“ID”相匹配,而这个当前的用户“ID”就是“实际用户ID”与“实际组ID”,也就是登录系统时所用到的身份验证信息。1般来讲,当履行1个程叙文件时,进程的有功效户ID通常就是实际用户ID,有效组ID通常是实际组ID。但也有特殊情况就是书中提到的passwd命令的履行,关于这个命令的详细分析大家可以参考上面那篇blog。
4.5 文件访问权限
这1节主要要给大家分享1下在访问文件进程中所用到的隐藏权限,这里隐藏的权限是指除访问文件所需要的权限外,还需要的额外的权限,具体内容以下:
- 通过文件名打开任意类型的文件时,对文件名(绝对路径)中包括的每个目录,包括它可能隐含确当前工作目录都应具有履行权限。这里还要强调1下目录的读权限与履行权限的不同意义,读权限允许我们读目录,取得在该目录中所有文件名的列表。当1个目录是我们要访问文件的路径名的1个组成部份时,对该目录的履行权限使我们可通过该目录(也就是搜索该目录,寻觅1个特定的文件名)。
- 对1个文件的读权限决定了我们是不是能够打开现有文件进行读操作。这与open函数的O_RDONLY和O_RDWR相干。
- 对1个文件的写权限决定了我们是不是能够打开现有文件进行写操作。这与open函数的O_WRONLY和O_RDWR相干。
- 为了在open函数中对1个文件指定O_TRUNC标志,必须对该文件具有写权限。
- 为了在1个目录中创建1个新文件,必须对该目录具有写权限和履行权限。之所以需要履行权限是由于在创建文件前需要查找当前目录中是不是已有该文件。
- 为了删除1个现有文件,必须对包括该文件的目录具有写权限和履行权限(删除之前还是要进行查找操作)。对该文件本身则不需要有读、写权限。
- 如果用7个exec函数中的任何1个履行某个文件,都必须对该文件具有履行权限。该文件还必须是1个不同文件。
进程每次打开、创建或删除1个文件时,内核就进行文件访问权限测试,这类测试触及两方面:1方面是文件的所有者,另外一方面是履行操作的进程。对文件的所有者进行管理主要触及:st_uid(用户ID)、st_gid(用户组ID),对进程的权限管理需要3个字段:有功效户ID、有限组ID、附属组ID。
有了标识进程与文件权限的信息位,再来就是对这些信息位进行比较,若符合则代表具有权限,若不符则不代表具有相应的权限。以下是内核进行的具体测试进程:
- 若进程的有功效户ID是0(超级用户),则允许访问。这给予了超级用户对全部文件系统进行处理的最充分的自由。
- 若进程的有功效户ID等于文件的所有者ID(也就是进程具有此文件),那末如果所有者适当的访问权限位被设置,则允许访问;否则谢绝访问。
- 若进程的有效组ID或进程的附属组ID之1等于文件的组ID,那末如果组适当的访问权限位被设置,则允许访问;否则谢绝访问。
- 若其他用户适当的访问权限位被设置,则允许访问;否则谢绝访问。
以上4个步骤顺序履行,其中若履行到1个权限规则相匹配则跳出测试,不再进行接下来的测试步骤。
4.6 新文件和目录的所有权
使用open或create创建新文件时,新文件的用户ID设置为进程的有功效户ID。关于组ID可以有以下两个选项:
- 新文件的组ID可以是进程的有效组ID。
- 新文件的组ID可以是它所在目录的组ID。这类选项的作用是使在某个目录下创建的文件和目录都具有该目录的组ID,因而文件和目录的组所有权从该点向下传递。
对linux操作系统如果设置组ID位被设置则新文件的组ID设置为目录的组ID;否则新文件的组ID设置为进程的有效组ID。
好了,说了这么多有关于权限的问题,让我们回过头来再看看在上1章中留下的问题,当时留下的问题是“先在没有写权限的情况下创建文件,即此时文件仅具有读权限,然后写入数据,根据我的实验若文件不存在则可以成功写入数据。此时文件已存在,再通过读写权限打开文件则出现无权限毛病”。对后1点毛病我们已可以理解了,确切是没有无权限,因尔后来我们加入了“写权限”,则可以成功写入数据,但对第1点还不是很明白:“没甚么没有写权限却可以写入数据?”。
4.7 access和faccessat函数
先来看看这两个函数的原型:
#include <unistd.h>
extern int
access (const char *__name, int __type) __THROW __nonnull ((1));
extern int f
accessat (int __fd, const char *__file, int __type, int __flag)
__THROW __nonnull ((2)) __wur;
这两个函数的功能是依照实际用户ID和实际组ID来测试文件的访问能力,注意此处1定要两个权限都测试成功,才会返回成功,否则则返回失败。再来看看我们能测试哪些权限,在<unistd.h>中有以下定义,上述定义用于函数的type参数。
#define R_OK 4 /* Test for read permission. */ //通过注释就能够了解其功能,测试读权限
#define W_OK 2 /* Test for write permission. */ //测试写权限
#define X_OK 1 /* Test for execute permission. */ //测试履行权限
#define F_OK 0 /* Test for existence. */ //测试是不是存在
access函数通过其定义可以发现,使用文件名标识被测试的文件。faccessat函数通过fd(指向某个文件夹)与文件名共同肯定被测试文件。其中若faccessat函数的__flag参数设定为AT_EACCESS,则访问检查用的是调用进程的有功效户ID和有效组ID(一样依照前面的测试顺序,遇到匹配项就跳出),而不是实际用户ID和实际组ID。
通过书中的示例,对access函数的功能进行简单的测试,源码以下:
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
int main(int argc,char* argv[])
{
if(
access(argv[1],R_OK)<0 ) perror(argv[0]);
else printf("read
access OK\n");
if( open(argv[1],O_RDONLY)<0 ) perror(argv[0]);
else printf("open for reading OK\n");
return 0;
}
运行结果以下:
再来看看/etc/shadow文件的运行情况:
./test_
access /etc/shadow
./test_
access: Permission denied
./test_
access: Permission denied
没有权限运行,所以做1些调剂再试试:
su //切换到root用户
chown root test_
access //切换用户ID为root,通过这1步操作就能够使用open函数打开文件
chmod u+s test_
access //打开用户设置用户ID位,设置用户ID位的作用用1句话概括就是“履行者将具有该程序所有者的权限”,在我们的例子中就是普通用户具有root用户的读
权限
ls -l test_
access //此时程序的所有者已变成root用户,同时设置用户ID位也被设置——“s”。
-rwsrwxr-x 1 root 8712 6月 26 17:36 test_
access
exit //返回普通用户
./test_
access /etc/shadow
./test_
access: Permission denied //本来设置了“s”位,普通用户应当具有与root用户相同的权限,即普通用户一样可以读权限,但由于
access函数使用实际用户ID与实际组ID进行
测试,所以出现无权限毛病。
open for reading OK //同理由于设置了“s”位,而root用户具有读权限,普通用户具有与root用户相同的权限,则可以通过open函数打开。
(关于ubuntu如何切换到超级用户请见以下blog:http://blog.csdn.net/david_xtd/article/details/7229325)。
4.8 umask函数
umask函数为进程设置文件模式创建屏蔽字,并返回之前的值(这是少数几个没有出错返回函数中的1个)。函数原型:
#include <sys/stat.h>
extern __mode_t umask (__mode_t __mask) __THROW;
其中,参数cmask是由9个常量中的若干个按位“或”构成的。在文件模式创建屏蔽字中为1的位,在文件mode中的相应位1定被关闭。
通过1个实例感受以下函数的功能,源码以下:
#include <fcntl.h>
#include <unistd.h>
#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
int main(int argc,char* argv[])
{
umask(0);
if(creat("foo",RWRWRW)<0) perror(argv[0]);
umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if(creat("bar",RWRWRW)<0) perror(argv[0]);
return 0;
}
运行结果以下:
ls -l foo bar
-rw------- 1 andywang andywang 0 6月 26 21:16 bar
-rw-rw-rw- 1 andywang andywang 0 6月 26 21:16 foo
通过实验可以发现,umask(0)没有屏蔽任何字,所以foo文件可以依照我们设定的权限进行创建,但由于在创建bar文件之前通过umask函数屏蔽了S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH,则在创建bar时文件的读写权限仅保存S_IRUSR|S_IWUSR。通过这个实验可以得出结论umask的效果可以1直延续直到进程结束。更改进程的文件模式创建屏蔽字其实不影响其父进程(常常是shell)的屏蔽字。但不知道exec函数是不是能够延续umask函数的效果,此处留下1个疑问?
4.9 chmod、fchmod、fchmodat函数
先来看看函数原型:
#include <sys/stat.h>
extern int chmod (const char *__file, __mode_t __mode)
__THROW __nonnull ((1));
extern int fchmod (int __fd, __mode_t __mode) __THROW;
extern int fchmodat (int __fd, const char *__file, __mode_t __mode,
int __flag)
__THROW __nonnull ((2)) __wur;
以上3个函数的功能是更改现有文件的访问权限。chmod函数在指定的文件上进行操作,而fchmod函数则对已打开的文件进行操作。fchmodat函数则对已打开的文件进行操作。fchmodat函数与chmod函数在下面两种情况下是相同的:1种是pathname参数为绝对路径,另外一种是fd参数取值为AT_FDCWD而pathname参数为相对路径。否则,fchmodat计算相对打开目录(由fd参数指向)的pathname。flag参数可以用于改变fchmodat的行动,当设置了AT_SYMLINK_NOFOLLOW标志时,fchmodat其实不会跟随符号链接。
为了改变1个文件的权限位,进程的有功效户ID必须等于文件所有者ID,或该进程必须具有超级用户权限。
除前文中提到的9个权限位外还有6个权限位,全部权限以下:
#define S_ISUID __S_ISUID /* Set user ID on execution. */
#define S_ISGID __S_ISGID /* Set group ID on execution. */
# define S_ISVTX __S_ISVTX
#define S_IRUSR __S_IREAD /* Read by owner. */
#define S_IWUSR __S_IWRITE /* Write by owner. */
#define S_IXUSR __S_IEXEC /* Execute by owner. */
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC)
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */
/* Read, write, and execute by group. */
#define S_IRWXG (S_IRWXU >> 3)
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */
/* Read, write, and execute by others. */
#define S_IRWXO (S_IRWXG >> 3)
好了,还是结合书中的例子看1下,源码以下:
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
int main(int argc,char* argv[])
{
struct stat statbuf;
if( stat("foo",&statbuf)<0 ) perror(argv[0]);
if( chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) <0 ) perror(argv[0]);
if( chmod("bar", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) <0 ) perror(argv[0]);
return 0;
}
运行结果以下,还是结合umask函数创建的文件继续进行实验。
ls -l bar foo
-rw------- 1 andywang andywang 0 6月 27 22:31 bar
-rw-r--r-- 1 andywang andywang 0 6月 27 22:31 foo
运行chmod后结果以下:
ls -l bar foo
-rw-r--r-- 1 andywang andywang 0 6月 27 22:32 bar
-rw-rwSrw- 1 andywang andywang 0 6月 27 22:32 foo
先来看看bar文件。比较简单,就是摒弃原来的文件权限,依照新的文件权限进行设置。
再来是foo文件,首先通过stat函数取得已有的文件权限,再来通过操作关闭“组履行”权限,并打开“履行时设置组ID”标志(在ls命令中以“S”体现)。此处要注意的1点是:“S”代表不包括履行位,“s”代表包括履行位。chmod不会影响文件的最后修改时间。
好了,说了这么多让我们来看看文件权限计算究竟是怎样进行的,我们已知道了文件权限标志是如何定义的,让我们来看看具体值:
#define __S_ISUID 04000 /* Set user ID on execution. */
#define __S_ISGID 02000 /* Set group ID on execution. */
#define __S_ISVTX 01000 /* Save swapped text after use (sticky). */
#define __S_IREAD 0400 /* Read by owner. */
#define __S_IWRITE 0200 /* Write by owner. */
#define __S_IEXEC 0100 /* Execute by owner. */
(以上定义位于/usr/include/x86_64-linux-gnu/bits/stat.h中)
好了,文件权限的实际值就是:
#define S_ISUID __S_ISUID //04000,数字以“0”开头,说明是8进制数字
#define S_ISGID __S_ISGID //02000
# define S_ISVTX __S_ISVTX //01000
#define S_IRUSR __S_IREAD //0400
#define S_IWUSR __S_IWRITE //0200
#define S_IXUSR __S_IEXEC //0100
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC) // 0700
#define S_IRGRP (S_IRUSR >> 3) //左移3位,变成0040
#define S_IWGRP (S_IWUSR >> 3) //左移3位,变成0020
#define S_IXGRP (S_IXUSR >> 3) //左移3位,变成0010
/* Read, write, and execute by group. */
#define S_IRWXG (S_IRWXU >> 3) //左移3位,变成0070
#define S_IROTH (S_IRGRP >> 3) //在0040的基础上再次左移3位,变成0004
#define S_IWOTH (S_IWGRP >> 3) //0002
#define S_IXOTH (S_IXGRP >> 3) //0001
/* Read, write, and execute by others. */
#define S_IRWXO (S_IRWXG >> 3) //0007
通过上面的计算,可以发现权限位的值与“chmod”命令中所使用的值完全相同,不同权限之间使用“|”运算进行计算,这1点没有甚么问题。
再来看看关闭1个标志位所进行的计算,通过上面的例子可以看到,关闭1个权限位所使用的是“先取反,再进行与运算”。还是结合上面的代码来看1看,“S_IXGRP”的值为“0010”编程2进制就是“000001000”(首位的0不参与运算),此时期表唯一“组履行”权限开启,取反后变成“111110111”,唯一“组履行”位关闭,此时进行与运算,则“组履行”位1定会被关闭,而其他权限位则保持不变,如此就实现了关闭“组履行”位的功能。
4.10 粘着位
有关于粘着位的功能请见以下blog:http://os.51cto.com/art/201004/194994.htm
当前,粘着位对普通文件已没有作用了,但如果目录的粘着位被设置,则可以起到保护其中文件的作用。
4.11 chown、fchown、fchownat、lchown函数
已看过了更改权限的函数,以上几个函数的主要功能就是改变文件的主人,对应的命令就是chown。与改变权限的那几个函数特点类似,在此就不详细分析了。
4.12 文件长度
stat结构成员st_size表示以字节为单位的文件的长度。此字段只对普通文件、目录文件和符号链接成心义。对目录,文件长度通常是1个数(如16或512)的整倍数。对符号链接,文件长度是在文件名中的实际字节数。
同时本小节再次讨论了与文件空洞有关的内容:
在第3章中我们通过实验创建了两个文件,其中1个带有文件空洞,可通过du命令查看文件实际占用的磁盘块个数,命令以下:
du -s file.hole
8 file.hole
由于在我的机器上,POSIXLY_CORRECT没有被设置,所以du命令报告的是512字节块的块数,所以file.hole实际占用的磁盘容量是4096字节,通过wc命令可以查看正常的I/O操作读全部文件长度:
wc -c file.hole
16394 file.hole
该长度与通过ls得到的长度相同。
如果使用实用程序复制这个文件,那末所有这些空洞都会被填满,其中所有实际数据字节皆被填写为0,操作以下:
cat file.hole > file.hole.copy
du file.hole.copy
20 file.hole.copy //此时有空洞的文件所占用的磁盘块数也是20
好了,说了这么多,通过实验来研究1下,到底st_size的值与那个命令最后得到的值相同,源么以下:
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
int main(int argc,char* argv[])
{
struct stat statbuf;
if( stat("file.hole",&statbuf)<0 ) perror(argv[0]);
else printf("%ld\n",statbuf.st_size);
if( stat("file.nohole",&statbuf)<0 ) perror(argv[0]);
else printf("%ld\n",statbuf.st_size);
if( stat("file.hole.copy",&statbuf)<0 ) perror(argv[0]);
else printf("%ld\n",statbuf.st_size);
return 0;
}
运行结果以下:
./test_st_size
16394
16394
16394
与ls命令的结果相同。
4.13 文件截断
将文件截取为1定length可通过以下函数:
#include <unistd.h>
extern int truncate (const char *__file, __off_t __length)
__THROW __nonnull ((1)) __wur;
extern int ftruncate (int __fd, __off_t __length) __THROW __wur;
以上两个函数定义在unstd.h中,说明上述函数是系统调用,与上面的几个函数具有1定的区分。这里要注意的1点是如果该文件之前的长度大于length,则超过length之外的数据就不再能访问了。如果之前的长度小于length,文件长度将增加,在之前的文件尾端和新的文件尾端之间的数据将读作0。我认为这个函数的功能与lseek类似,都是直接设置当前文件偏移量。