主要有read write lseek close open.其中read write都是系统调用.
open或者creat会返回当前最小的文件描述符fd(>=0).0,1,2一般被系统占用.
creat只能以只写方式打开创建文件,如果创建后想写,只能creat,close,open.
close会关闭文件,并释放进程在文件上的所有记录锁.
当进程终止时,内核自动关闭所有打开文件.
每个被打开文件都有"当前文件偏移量",通常读写都会从文件偏移量开始.open时如果制定模式append就从文件末尾,否则从开始.
lseek将当前文件偏移量记录在内核,不引起IO操作.
下面看代码应用:"写空洞":
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "string.h"
#include "sys/stat.h"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main(){
int fd = creat("file.hole", FILE_MODE);
if (fd < 0){
printf("creat error\n");
exit(1);
}
char buf1[] = "aqweqwe";
char buf2[] = "ASDASDASD";
int buf1len = strlen(buf1);
if (write(fd, buf1, buf1len) != buf1len){
printf("write error\n");
exit(1);
}
if (lseek(fd, 1024, SEEK_SET) == -1){
printf("lseek error\n");
exit(1);
}
if (write(fd, buf2, strlen(buf2)) != strlen(buf2)){
printf("write error\n");
exit(1);
}
exit(0);
}
用od -l file.hole可以看到具体内容.顺便也几下 od这个命令,就是将文件内容dump出来.
3.7 read函数,这个函数日常使用还是挺频繁的.
ssize_t read(int fd, void* buf, size_t nbytes);
read返回读取的字节数.如果失败返回0;而且有可能返回的比想要读取的nbytes小,原因有以下几点:
1)读普通文件时,读到文件末尾,比如离末尾还要30bytes,却要读100bytes,当然只能返回30了,这时就不要读了,不然下回是0.
2)从终端读取,每次最多读一行;
3)从网络读取,由于缓冲结构可能会造成返回值小于要求读的字节数;
4)由于某信号造成中断,这时已经去了部分数据.
3.8 write
返回值与传入写入bytes应该一致,不然就是写入失败了.常见原因是:磁盘已满,或者超过给定进程的文件长度限制.
大多文件系统采用预读技术.当顺序读时,系统就会读入比要求多的数据.
3.10 文件共享
内核使用三种数据结构标示打开的文件:
1)在进程task_struct里有一个记录项,记录项里包含一章打开文件描述符表,每个fd占一项.与每个fd相关的是:a)fd标志,b)指向一个文件表项的指针.
2)内核为所用打开文件维护一章文件表,其中包含:a)文件状态标志,b)当前文件偏移量c)指向该文件v节点指针;
3)v节点结构.其中包含了 文件类型和对文件进行操作的各种函数指针.linux使用了i节点
如果两个进程分别打开了同一个文件,他们分别拿到了一个fd,各自维护文件表,但文件表却指向同一个i节点.
3.11 原子操作
write时如果加了a_append,unix就保证内核每次对文件写之前,都将进程当前偏移量设置到文件末尾,
传统的unix都在内核设置有缓冲区告诉缓存或者页面告诉缓存,大多数磁盘IO都通过缓存进行.采用延迟写来减少IO,但降低了文件更新速度.
fsync将修改过的块缓冲区排入写队列,然后返回,并不等待实际写磁盘结束.
通过被称为update的demon进程会周期性调用sync.保证了定期冲洗内核块缓冲区.
fsync要等待磁盘操作结束才返回,可用于数据库这样的程序,要求修改过的块立即写入.
3.14 fcntl可以改变已打开文件的性质.
3.15 ioctl 额,这个书上没讲什么,还是自己man吧
课后题
1.都是有缓冲机制的,比如延迟写.上面笔记也写到了;
2.实现与dup2功能相同函数,但不能调用fcntl 并有出错处理:
表示不会....
好了,该洗洗睡了.
明天开始第四章,顺便继续想想这几道课后题.