1.read函数
调用read函数从文件去读数据,函数定义如下:
[cpp] view plain copy print ?
- #include <unistd.h>
- ssize_t read(int filedes, void* buff, size_t nbytes);
#include <unistd.h>
ssize_t read(int filedes, void* buff, size_t nbytes);
成功则返回实际读取的byte数,如果已经达到文件结尾则返回0,出错则返回-1.
2.write函数
调用write函数向打开的文件写入数据,函数定义如下:
[cpp] view plain copy print ?
- #include <unistd.h>
- ssize_t write(int filedes, void* buff, size_t nbytes);
#include <unistd.h>
ssize_t write(int filedes, void* buff, size_t nbytes);
成功则返回实际写入的byte数,出错则返回-1.
read和write的buff大小为一个文件block大小时,效率是最高的,在ext4文件系统中,一个文件block大小为4K,一般这个块长存放
在stat结构中,定义如下:
blksize_t st_blksize;
可以使用stat系列函数获取该值。
注意:
使用read,write操作管道,FIFO以及某些设备时,特别是终端,网络和STREAMS,有下列两种性质。
a.一次read操作所返回的数据可能少于所要求的数据,即使还没达到文件尾端也可能是这样。这不是一个错误,应该
继续读该设备。
b.一次write操作的返回值也可能少于指定输出的字节数。这也不是错误,应当继续写余下的数据至该设备。
3.lseek函数
每个打开的文件都有一个关联的“当前偏移量”,用于记录从文件到当前当前位置的偏移字节数,lseek函数是设置这个当前偏移量
的函数,函数的声明如下:
[cpp] view plain copy print ?
- #include <unistd.h>
- off_t lseek(int filedes, off_t offset, int whence);
#include <unistd.h>
off_t lseek(int filedes, off_t offset, int whence);
成功则返回新的文件偏移量,失败则返回-1.
如果whence是SEEK_SET,则文件的偏移量设置为文件开始加上offset个字节。
如果whence是SEEK_CUR,则文件的偏移量设置为当前偏移量开始加上offset个字节,offset可正可负。
如果whence是SEEK_END,则文件的偏移量设置为文件长度加上offset个字节,offset可正可负。
不是每个文件都能够设置偏移量,有些文件如管道,FIFO或socket,无法设置偏移量,可以使用如下函数测试是否可以设置偏移量,
如果返回-1,则表示不可以。
[cpp] view plain copy print ?
- off_t currpos;
- currpos = lseek(fd, 0, SEEK_CUR);
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);
创建一个文件,往文件中写入10个字符,然后再使用lseek定位文件开始加上4个字节的偏移量,然后读取接下来的内容。
[cpp] view plain copy print ?
- #include <fcntl.h>
- #include <stdio.h>
-
- int main(void){
- int fd,byteNum,result;
- char wbuf[10] = "123456789";
- char rbuf[10];
- if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
- perror("open");
- return -1;
- }
-
- if((byteNum = write(fd, wbuf, 10))<0){
- perror("write");
- return -1;
- }
-
- if((result = lseek(fd, 4, SEEK_SET))<0){
- perror("lseek");
- return -1;
- }
-
- if((byteNum = read(fd, rbuf, 10)) < 0){
- perror("read");
- return -1;
- }
-
- printf("read content:%s\n",rbuf);
-
- close(fd);
- return 0;
- }
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, 4, SEEK_SET))<0){
perror("lseek");
return -1;
}
if((byteNum = read(fd, rbuf, 10)) < 0){
perror("read");
return -1;
}
printf("read content:%s\n",rbuf);
close(fd);
return 0;
}
运行结果:
read content:56789
如果将偏移量设置为文件开始加上一个负数,调用lseek就会出错。
[cpp] view plain copy print ?
- #include <fcntl.h>
- #include <stdio.h>
-
- int main(void){
- int fd,byteNum,result;
- char wbuf[10] = "123456789";
- char rbuf[10];
- if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
- perror("open");
- return -1;
- }
-
- if((byteNum = write(fd, wbuf, 10))<0){
- perror("write");
- return -1;
- }
-
- if((result = lseek(fd, -1, SEEK_SET))<0){
- perror("lseek");
- return -1;
- }
-
- if((byteNum = read(fd, rbuf, 10)) < 0){
- perror("read");
- return -1;
- }
-
- printf("read content:%s\n",rbuf);
-
- close(fd);
- return 0;
- }
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, -1, SEEK_SET))<0){
perror("lseek");
return -1;
}
if((byteNum = read(fd, rbuf, 10)) < 0){
perror("read");
return -1;
}
printf("read content:%s\n",rbuf);
close(fd);
return 0;
}
运行结果:
lseek: Invalid argument
是不是offset就一定不能为负数呢,不一定,只要最终的偏移量不小于0就可以了,看下面的例子。
[cpp] view plain copy print ?
- #include <fcntl.h>
- #include <stdio.h>
-
- int main(void){
- int fd,byteNum,result;
- char wbuf[10] = "123456789";
- char rbuf[10];
- if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
- perror("open");
- return -1;
- }
-
- if((byteNum = write(fd, wbuf, 10))<0){
- perror("write");
- return -1;
- }
-
- if((result = lseek(fd, -4, SEEK_CUR))<0){
- perror("lseek");
- return -1;
- }
-
- if((byteNum = read(fd, rbuf, 10)) < 0){
- perror("read");
- return -1;
- }
-
- printf("read content:%s\n",rbuf);
-
- close(fd);
- return 0;
- }
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, -4, SEEK_CUR))<0){
perror("lseek");
return -1;
}
if((byteNum = read(fd, rbuf, 10)) < 0){
perror("read");
return -1;
}
printf("read content:%s\n",rbuf);
close(fd);
return 0;
}
运行结果:
read content:789
如果文件偏移量大于文件长度再写入数据,那么生成的文件就会出现空洞。先往文件写入10个字符,再跳过40960个字符,再写入10个字符。
[cpp] view plain copy print ?
- #include <fcntl.h>
- #include <stdio.h>
-
- int main(void){
- int fd,byteNum,result;
- char wbuf[10] = "123456789";
- char rbuf[10];
- if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR))<0){
- perror("open");
- return -1;
- }
-
- if((byteNum = write(fd, wbuf, 10))<0){
- perror("write");
- return -1;
- }
-
- if((result = lseek(fd, 40960, SEEK_END))<0){
- perror("lseek");
- return -1;
- }
-
- if((byteNum = write(fd, wbuf, 10)) < 0){
- perror("write");
- return -1;
- }
-
- close(fd);
- return 0;
- }
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, 40960, SEEK_END))<0){
perror("lseek");
return -1;
}
if((byteNum = write(fd, wbuf, 10)) < 0){
perror("write");
return -1;
}
close(fd);
return 0;
}
运行结果:
-rw------- 1 root root 40980 2013-09-09 15:03 a.txt
使用od命令查看文件内容:
root@virtual-machine:~# od -c a.txt
0000000 1 2 3 4 5 6 7 8 9 \0 \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0120000 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 1 2 3 4 5 6
0120020 7 8 9 \0
0120024
空洞的内容全部置为0
查看下文件占用的磁盘大小:
8 -rw------- 1 root root 40980 2013-09-09 15:41 a.txt
文件虽然有40980个字节,只占用了8个block,即8K(这边的block不是文件系统的block,而是kernel block,linux中,
kernel block大小为1K),如果是没有空洞的文件,则占用磁盘大小应该是44K。可见,虽然有空洞,但是实际存储时没
有占用文件大小的磁盘空间。
如果文件以O_APPEND方式打开,但是在写入前lseek到文件的某一个位置,结果会怎样?
[cpp] view plain copy print ?
- #include <stdio.h>
- #include <fcntl.h>
-
- int main(void){
- int fd,result;
- char wbuf[5] = "1234";
- if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){
- perror("open");
- return -1;
- }
-
- if((result = lseek(fd, 2, SEEK_SET)) < 0){
- perror("lseek");
- return -1;
- }
-
- if((result = write(fd, wbuf, 4))<0){
- perror("write");
- return -1;
- }
-
- close(fd);
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
int main(void){
int fd,result;
char wbuf[5] = "1234";
if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){
perror("open");
return -1;
}
if((result = lseek(fd, 2, SEEK_SET)) < 0){
perror("lseek");
return -1;
}
if((result = write(fd, wbuf, 4))<0){
perror("write");
return -1;
}
close(fd);
return 0;
}
程序执行前a.txt为:123456789
程序执行后a.txt为:
123456789
1234
因为设置为O_APPEND后,内核每次对这种文件写之前,进程当前的偏移量都会设置到文件的末尾。而且lseek和write
合起来是一个原子操作。
在使用lseek函数的时候发现一个问题,如果调用如下:lseek(fd, -2, SEEK_END),则还是在文件的末尾写入,不知道
为什么。
[cpp] view plain copy print ?
- #include <stdio.h>
- #include <fcntl.h>
-
- int main(void){
- int fd,result;
- char wbuf[5] = "1234";
- if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){
- perror("open");
- return -1;
- }
-
- if((result = lseek(fd, -2, SEEK_END)) < 0){
- perror("lseek");
- return -1;
- }
-
- if((result = write(fd, wbuf, 4))<0){
- perror("write");
- return -1;
- }
-
- close(fd);
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
int main(void){
int fd,result;
char wbuf[5] = "1234";
if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){
perror("open");
return -1;
}
if((result = lseek(fd, -2, SEEK_END)) < 0){
perror("lseek");
return -1;
}
if((result = write(fd, wbuf, 4))<0){
perror("write");
return -1;
}
close(fd);
return 0;
}
运行结果:
yan@yan-vm:~/ctest$ od -c a.txt
0000000 1 2 3 \n
0000004
yan@yan-vm:~/ctest$ ./a.out
yan@yan-vm:~/ctest$ od -c a.txt
0000000 1 2 3 \n 1 2 3 4
0000010