二.文件IO/系统调用IO
fd是在文件IO中贯穿始终的类型
1、文件描述符的概念(整形数 数组下标 文件描述符优先使用当前可用范围内最小的 作用域:当前进程)
2、文件IO操作:open close read write lseek
1>打开一个文件:int open(const char *pathname, int flags, ...);
返回值:成功,返回文件描述符; 出错, 返回-1.
pathname参数是要打开或创建文件的名字,flags参数:O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读、写打开
......
2>关闭一个文件:int close(int fd);
返回值:成功,返回0; 出错, 返回-1.
关闭一个文件时还会释放该进程加在该文件上的所有记录锁.
3>读一个文件: ssize_t read(int fd, void *buf, size_t nbytes);
返回值:读到的字节数,若已到文件尾,返回0, 出错,返回-1.
fd表示读取那个文件, buf表示读取的字节放到buf中, nbytes表示读取的字节数.
4>写一个文件: ssize_t write(int fd, const void *buf, size_t nbytes);
返回值:成功,返回已写的字节数, 出错,返回-1.
fd表示要写入的那个文件, buf表示从buf中的内容读到fd当中, nbytes表示写入的字节数.
5>定位一个文件: off_t lseek(int fd, off_t offset, int whence);
返回值:成功,返回新的文件偏移量; 出错,返回-1.
参数fd表示要操作的文件,offset表示为偏移量,whence表示参照物(从哪里开始)
whence:SEEK_SET 偏移量设置为距文件开始处offset个字节;
SEEK_CUR 偏移量设置为当前值加offset个字节,offset可正可负;
SEEK_END 偏移量设置为文件长度加offset个字节,offset可正可负。
没有单位的数值没有意义。
注意:od -c 查看文件中的空洞,ll -s 查看文件长度,du –s 查看文件占用磁大小
注意:当用 read 读 STDIN_FILENO 时候,如果要读的字符数为 x,终端中有字符 y 个,
如果 x>=y,那么 read 会读到 y 个字符,如果 x<y,那么 read 会读到 x+1 个字符,最后一个是'\n'.
3、文件IO与标准Io的区别
区别:响应速度&吞吐量
文件IO 标准IO
面试:如何使一个程序变快?
用户体验上是吞吐量。
提醒:标准IO与文件IO不可混用。
转换:fileno fdpoen
atrace ./ab 跟踪一个文件执行过程。
4、IO的效率问题
习题:将mycpy.c程序进行更改,将BUFSIZE的值进行放大,并观察进程所消耗的时间,
注意性能最佳拐点出现时的BUFSIZE值,以及何时程序会出现问题。
5、文件共享:多个任务共同操作一个文件或者协同完成任务
面试:写程序删除一个文件的第10行
补充函数:truncate/ftruncate.
6、原子操作:不可分割的单位
原子:不可分割的最小单位
作用:解决竞争和冲突
如tmpnam创建临时文件
7、程序中的重定向:dup dup2
1>复制一个现有文件描述符: int dup(int oldfd);
返回值:成功,返回新的文件描述符; 出错,返回-1.
复制后两个文件描述符指向同一个文件表项(struct file 结构体)实现文件的共享,同时写一个文件时是接续写.
2>复制一个新的描述符newfd: int dup2(int oldfd, int newfd);
返回值:成功,返回新的文件描述符; 出错,返回-1.
注意:返回的新描述符一定是当前可用的文件描述符的最小值;
注意:如果 newfd 已经被打开则会先关闭newfd(解除文件描述符与文件表项的链接),如果 newfd 和 oldfd 相等则该函数什么都不做;
注意:新描述符和旧的描述符各有自己的文件描述符标志,新文件描述符的 FD_CLOEXEC标志被清除。
注意:交叉写入时是接续写。
8、同步 sync fsync fdatasync
文件同步
把所有修改过的文件块刷到内核的写队列,并不等待真正的写到磁盘
针对内核
void sync(void);
和 sync 一样,不过这个函数只是针对于特定文件
void syncfs(int fd);
把 fd 描述文件的内容都刷到磁盘之后才返回
int fsync(int fd);
返回值:成功,返回0; 失败, 返回-1.
和 fsync 一样,不过这个函数只是刷新文件的数据部分,属性部分并不会因这个函数而刷新
int fdatasync(int fd);
返回值:成功,返回0; 失败, 返回-1.
9、函数 fcntl 和 ioctl
改变已经打开文件的属性:int fcntl(int fd, int cmd, ... /* arg */ );
这个函数可以改变/获取一个文件的文件描述符标志和文件状态标志
返回值:成功, 则依赖于cmd; 出错, 返回-1。
参数1表示要操作那个文件, 参数2表示要进行那个操作命令。
文件描述符标志:
现在的系统就一个 O_CLOEXEC,如果一个文件描述符标志中没有设置 O_CLOEXEC,那么在 exec 一个新程序的时候这个文件描述符依然有效,
否则这个文件描述符在 exec 的时候是关闭的,dup(2)复制的新文件描述符总是被清除 FD_CLOEXEC 标志.
文件状态标志:O_APPEND,O_RDONLY, O_WRONLY, O_RDWR 等标志,
在用 fcntl 设置文件状态权限是不能设置文件访问权限和文件创建的权限的,
比如 O_RDONLY, O_WRONLY, O_RDWR,O_CREAT, O_EXCL, O_TRUNC。
复制文件描述符:cmd=F_DUPFD/F_DUPFD_CLOEXEC
ioctl():设备相关的内容
int ioctl(int fd, int request, ...);
注意:这个和函数和底层的具体实现有关系
返回值:出错,返回-1; 成功,返回其他值。
10、/dev/fd/目录:虚目录 显示的是当前进程的文件描述符信息.