3.6lseek函数
lseek函数用于当打开一个文件时与其关联的”当前文件偏移量“的操作相关。
函数格式:
off_t lseek(int fileds, off_t offset, int whence);
若成功则返回新的文件偏移,若出错则返回-1.。
对参数offset的解释与参数whence的值相关。
whence==SEEK_SET 距文件开始处offset个字节。
whence==SEEK_CUR 文件偏移量设置为当前offset
whence==SEEK_END 偏移量为文件长度加上offset。
下面的程序测试标准输入能否被设置位移量lseektest.c:
#include <sys/types.h>
#include "ourhdr.h"
int main(void)
{
if (lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
printf("cannot seek\n");
else
printf("seek ok\n");
exit(0);
}
运行结果如下:
[root@localhost apue]# gcc -o lseektest.out lseektest.c
[root@localhost apue]# ./lseektest.out
cannot seek
[root@localhost apue]# ./lseektest.out </etc/motd
seek ok
[root@localhost apue]# cat < /etc/motd | ./lseektest.out
cannot seek
lseek函数只将文件位移量记录在内核中,它并不引起任何I/O操作。然后该位移量用于下一个读或者写操作。
下面的程序创建一个具有空洞的文件:
fseekcreate.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ourhdr.h"
#include "error.c"
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int main(void)
{
int fd;
if ((fd = creat("file.hole", FILE_MODE)) < 0)
{
err_sys("creat error");
}
if (write(fd, buf1, 10) != 10)
err_sys("buf1 write error");
if (lseek(fd, 40, SEEK_SET) == -1)
err_sys("lseek error");
if (write(fd,buf2,10) != 10)
err_sys("buf2 write error");
exit(0);
}
文件中间30个未写字节都被读成0。
运行结果如下:
[root@localhost apue]# gcc -o fseekcreate.out fseekcreate.c
[root@localhost apue]# ./fseekcreate.out
[root@localhost apue]# ls -l file.hole
-rw-r--r-- 1 root root 50 12-26 19:54 file.hole
[root@localhost apue]# 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
0000062
3.7 read函数
用read函数从打开的文件中读取数据。
ssize_t read(int filedes, void *buff, size_t nbytes)
返回读到的字节数,如果已经到了文件尾,返回0,若出错返回-1。
从终端设备读取时,通常一次最多读取一行。
读操作从文件的当前位移量开始。
3.8write函数
用write函数向打开文件写数据。
ssize_t write(int fileds, const void *buff, size_t nbytes)
若成功读取返回已写的字节数,失败返回-1。
对于普通文件,写操作从文件的当前位移量处开始。
3.9 I/O的效率
下面的程序使用read和write函数来复制一个文件:
说明:所有的UNIX shell都提供一种方法,它在标准输入上打开一个文件用于读(STDIN_FILENO)。在标准输出上创建一个文件用于写(STDOUT__FILENO)。
stdinout.c:
#include "ourhdr.h"
#include "error.c"
#define BUFFSIZE 8192
int main(void)
{
int n;
char buf[BUFFSIZE];
while ((n = read(STDIN_FILENO,buf,BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n <0)
err_sys("read error");
exit(0);
}
运行结果如下:
[root@localhost apue]# gcc -o stdinout.out stdinout.c
[root@localhost apue]# ./stdinout.out
hello
hello
nihao
nihao
BUFFSIZE一般设置为8192处,系统CPU时间出现最小值。
3.10文件共享
UNIX支持在不同进程之间打开共享文件。
内核用三个数据结构用于文件共享操作实现:
每个进程在进程表中有一个记录项,每个记录项中有一张打开文件的描述符表。
内核为所有打开的文件维持一张文件表。
每个打开的文件或者设备都有一个V节点结构。
每个进程可以打开多个文件,每个文件对应一个文件表项。多个进程可以打开一个文件。
3.11原子操作
3.11.1添加至一个文件
在写入一个文件的时候,"定位档到文件结尾处,然后写"。这个操作不能被其他操作中断,这就是原子操作。
UNIX实现原子操作的方法是在打开文件时设置O_APPEND标志。
原子操作:指的是由多部组成的操作,该操作原子地执行完所有步或者一步也不执行。
3.12dup和dup2函数
下面两个函数都可用于复制一个现存的文件描述符:
int dup(int filedes)
int dup2(int filedes, int filedes2)
两函数都是若成功返回新的文件描述符,若错误返回-1。
由dup返回的新文件描述符一定是当前可用文件描述符中最小的整数。dup2则可用用filedes2参数指定新文件描述符的数值。
3.13fcntl函数
fcntl函数可以改变已经打开的文件的性质。
int fcntl(int filedes, int cmd, ...)
返回:若成功,则依赖于cmd,如出错则-1。
F_GETFL可以用于获取文件状态标志作为函数的返回值。
fcntl的返回值与命令有关,如果出错,所有命令都返回-1。
下面的程序对于指定的文件状态描述符打印文件标志:
fcontltest.c:
#include <sys/types.h>
#include <fcntl.h>
#include "ourhdr.h"
#include "error.c"
int main(int argc, char *argv[])
{
int accmode, val;
if (argc != 2)
err_quit("usage:a.out <descriptor#>");
if ((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0)
err_sys("fcntl error for fd %d", atoi(argv[1]));
accmode = val & O_ACCMODE;
if (accmode == O_RDONLY) printf("read only");
else if (accmode == O_WRONLY) printf("write only");
else if (accmode == O_RDWR) printf("read write");
else err_dump("unknow access mode");
if (val & O_APPEND) printf(", append");
if (val & O_NONBLOCK) printf(", nonblocking");
#if !defined(_POSIX_SOURCE) && defined(O_SYNC)
if (val & O_SYNC) printf(", synchronous writes");
#endif
putchar('\n');
exit(0);
}
说明:
如果第二个参数是F_GETFL,则设置并返回文件状态标识符为第一个参数的值。
O_ACCMODE<0003>:读写文件操作时,用于取出flag的低2位
atoi函数的功能是把字符串转换为整数。
运行结果:
[root@localhost apue]# vim fcontltest.c
[root@localhost apue]# gcc -o fcontltest.out fcontltest.c
[root@localhost apue]# ./fcontltest.out 0 < /dev/tty
read only
[root@localhost apue]# ./fcontltest.out 1> temp.foo
usage:a.out <descriptor#>
[root@localhost apue]# ./fcontltest.out 1 > temp.foo
[root@localhost apue]# cat temp.foo
write only
[root@localhost apue]# ./fcontltest.out 2 2> > temp.foo
bash: syntax error near unexpected token `>'
[root@localhost apue]# ./fcontltest.out 2 2>> temp.foo
write only, append
[root@localhost apue]# ./fcontltest.out 5 5>> temp.foo
write only, append
[root@localhost apue]# ./fcontltest.out 5 5<> temp.foo
read write
[root@localhost apue]# cat temp.foo
write only