目录
title: 文件IO
date: 2019/11/23 10:49:52
toc: true
---
文件IO
文件描述符
文件描述符是非负的整数,一般是系统调用的,这个与file_struct
区别开来. STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO
被定义在 <unistd.h>
获取最大支持的描述符
新的linux
已经不支持OPEN_MAX
来直接获取这个最大描述符了,
sysconf(_SC_OPEN_MAX)
shell 下这么查看,其中的open files (-n) 1024
就是了
reallin@ubuntu:/work/pan/apue$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 31609 max locked memory (kbytes, -l) 65536 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 31609 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
shell下使用ulimit -n <number>
来修改,这个-n
就是上面的显示出来的
代码获取参照获取最大文件描述符
open/openat/creat
int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); int openat(int dirfd, const char *pathname, int flags); int openat(int dirfd, const char *pathname, int flags, mode_t mode);
openat
使得oepn
的路径可以是相对于xxx
目录打开的实现,其中的xxx
目录实际就是使用open
或者opendir
返回的文件目录的文件描述符,dirfd
用来转换opendir
为普通描述符.如果fd
为AT_FDCWD
则等同与open
creat
函数可以被open
取代,注意还有个选项O_EXCL
,如果文件存在则创建失败
// O_TRUNC 是文件存在的话使长度为0 open(path, O_WRONLY|O_CREAT|O_TRUNC,mode) // 比如 open("./me.txt", O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU);
close
man里面写了close不能确保文件一定被写入硬盘,最好用fsync
int fsync(int fd);
lseek
lseek
不支持fifo
和管道 和网络套接字,返回-1
,这里一定要与-1
比较,因为有些文件允许负的偏移量
$ cat < /etc/passwd | ./exe seek at fd 0 is err
lseek
跨过文件大小,如果继续写则在后续位置写,造成文件空洞
off_t 类型说明
- 使用
sysconf
查看 - c99使用命令
getconf
查看 - 可以手动指定设置宏
_FILE_OFFSET_BITS
为32或者64
lseek与OAPPEND的区别
lseek到文件末尾只是一次性的,而使用OAPPEND
的形式打开时,write
操作时都会从内核维护的文件信息节点中获得seek偏移
内核维护的文件信息
文件描述符是针对进程范围的
file status flag 是内核维护的,针对的是所有指向这个文件的进程的


dup/dup2
// 返回最小的描述符 int dup(int fd); // 如果new_fd2 !=old,先关闭 new_fd2, // 再new_fd2=old_fd //如果这个fd2是标准输出,则可以实现重定位 fd 到标准输出了 int dup2(int old_fd, int new_fd2);
如果两个fd不等,fd2的FD_CLOEXEC
标志会被清,也就是exec后依然可用
还有就是即使关闭fd,但是实际的file table 有计数,只有当计数为0才真正关闭
FD_CLOEXEC
https://blog.csdn.net/chrisniu1984/article/details/7050663
子进程以写时复制(COW,Copy-On-Write)方式获得父进程的数据空间、堆和栈副本,这其中也包括文件描述符。刚刚fork成功时,父子进程中相同的文件描述符指向系统文件表中的同一项(这也意味着他们共享同一文件偏移量)。
在复杂系统中,有时我们fork子进程时已经不知道打开了多少个文件描述符(包括socket句柄等),这此时进行逐一清理确实有很大难度。我们期望的是能在fork子进程前打开某个文件句柄时就指定好:“这个句柄我在fork子进程后执行exec时就关闭”。其实时有这样的方法的:即所谓的 close-on-exec
int fd=open("foo.txt",O_RDONLY); int flags = fcntl(fd, F_GETFD); flags |= FD_CLOEXEC; fcntl(fd, F_SETFD, flags);
当fork子进程后,仍然可以使用fd。但执行exec后系统就会字段关闭子进程中的fd了
缓存同步到存储
// 等待写硬盘 int fsync(int fd); // 只影响数据,不影响属性,比如文件的修改时间 int fdatasync(int fd); // 告知内核的守护去写硬盘,但不会等到写完返回 void sync(void); int syncfs(int fd);
linux下这个fsync 和sync 是一样的,在man里面有写的
According to the standard specification (e.g., POSIX.1-2001), sync() schedules the writes, but may return before the actual writing is done. However Linux waits for I/O completions, and thus sync() or syncfs() provide the same guarantees as fsync called on every file in the system or filesystem respectively.
Linux 不支持使用fcntl
对O_SYNC
标志位的修改,只能是open
的时候确认的
fcntl改变文件属性
#include <fcntl.h> // 错误返回-1 成功返回其他值 int fcntl(int fd, int cmd, ... /* int arg */ ); 1. Duplicate an existing descriptor (cmd = F_DUPFD or F_DUPFD_CLOEXEC) 2. Get/set file descriptor flags (cmd = F_GETFD or F_SETFD) 3. Get/set file status flags (cmd = F_GETFL or F_SETFL) // O_RDONLY ...等属性 4. Get/set asynchronous I/O ownership (cmd = F_GETOWN or F_SETOWN) 5. Get/set record locks (cmd = F_GETLK, F_SETLK, or F_SETLKW)
获得文件状态
F_GETFL
,如果要判断读写权限,需要与val & O_ACCMODE
再去与O_RDONLY
等比较Linux 不支持
O_SYNC
标志位的修改,只能是open
的时候确认的
ioctl
这是一个杂类的接口,以前写驱动的时候也是会用到的这个
#include <sys/ioctl.h> int ioctl(int fd, unsigned long request, ...);
文件截断
#include <unistd.h> int truncate(const char *pathname, off_t length); int ftruncate(int fd, off_t length);
代码附录
获取最大文件描述符
extern "C" { #include "apue.h" } #include <stdio.h> #include <limits.h> #include <unistd.h> //sysconf int main(int argc ,char** argv) { printf("max open fd number is %ld\n",sysconf(_SC_OPEN_MAX)); } #if(0) max open fd number is 1024 #endif
使用openat来实现一种相对路径的打开
extern "C" { #include "apue.h" } #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> /* int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); int openat(int dirfd, const char *pathname, int flags); int openat(int dirfd, const char *pathname, int flags, mode_t mode); DIR *opendir(const char *name); DIR *fdopendir(int fd); int dirfd(DIR *dirp); */ int main(int argc ,char** argv) { int dir_fd; int file_fd; // 方式1,使用open打开dir,返回的文件描述为openat的dirfd // 方式2,使用opendir,返回值用dirfd 转换为fd dir_fd=open("../",O_RDONLY); //------this line is diff if(dir_fd<0) { err_quit("open ../ dir err"); } file_fd=openat(dir_fd,"./3-3-1/Makefile",O_RDONLY); if(file_fd<0) { err_quit("open Makefile err"); } unsigned char buf[100]={0}; read(file_fd,buf,99); printf("read by open Makefile for 99 byte :\n:%s\n",buf); DIR* dirfd_pt=opendir("../"); //------this line is diff if(dirfd_pt==NULL) { err_quit("opendir ../ dir err"); } dir_fd=dirfd(dirfd_pt); //------this line is diff if(dir_fd<0) { err_quit("dirfd convert err"); } file_fd=openat(dir_fd,"./3-3-1/Makefile",O_RDONLY); if(file_fd<0) { err_quit("open Makefile err"); } unsigned char buf2[100]={0}; read(file_fd,buf2,99); printf("read by opendir Makefile for 99 byte :\n:%s\n",buf2); } // read by open Makefile for 99 byte : // :SRCS = $(wildcard ./*.cpp) // #SRCS += $(wildcard ./*.c) // #OBJS := $(patsubst %.c, %.o, $(SRCS)) // OBJS // read by opendir Makefile for 99 byte : // :SRCS = $(wildcard ./*.cpp) // #SRCS += $(wildcard ./*.c) // #OBJS := $(patsubst %.c, %.o, $(SRCS)) // OBJS
lseek测试管道等
extern "C" { #include "apue.h" } #include <stdio.h> #include <sys/types.h> #include <unistd.h> /* off_t lseek(int fd, off_t offset, int whence); */ int main(int argc ,char** argv) { off_t l_seek =lseek(STDIN_FILENO, 0, SEEK_SET); if(l_seek==-1) { err_quit("lseek at fd 0 is err"); } printf("lseek success \n"); } // lseek 不支持 fifo 管道 和网络套接字 // reallin@ubuntu:~/work/pan/apue/study/3-6-1$ ./exe // lseek at fd 0 is err // reallin@ubuntu:~/work/pan/apue/study/3-6-1$ ./exe <Makefile // lseek success // reallin@ubuntu:~/work/pan/apue/study/3-6-1$ cat < Makefile |./exe // lseek at fd 0 is err
lseek文件跨越写
extern "C" { #include "apue.h" } #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> /* off_t lseek(int fd, off_t offset, int whence); */ int main(int argc ,char** argv) { int file_fd=open("me.o",O_CREAT|O_WRONLY|O_TRUNC,S_IRWXU); if(file_fd<0) { err_quit("creat file err"); } off_t l_seek =lseek(file_fd, 100, SEEK_SET); if(l_seek==-1) { err_quit("lseek at fd 0 is err"); } char* buf="hello in lseek at 100"; ssize_t len=write(file_fd, buf,(size_t)strlen(buf)); printf("lseek success \n"); } // reallin@ubuntu:~/work/pan/apue/study/3-6-2$ hexdump me.o // 0000000 0000 0000 0000 0000 0000 0000 0000 0000 // * // 0000060 0000 0000 6568 6c6c 206f 6e69 6c20 6573 // 0000070 6b65 6120 2074 3031 0030 // 0000079 // reallin@ubuntu:~/work/pan/apue/study/3-6-2$ ls -lh me.o // -rwx------ 1 reallin reallin 121 Nov 23 14:55 me.o
fcntl获取文件状态
extern "C" { #include "apue.h" } #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> //atoi int main(int argc ,char** argv) { if(argc==1) { err_quit("pls input a num as file descriptor\n"); } int val= fcntl(atoi(argv[1]), F_GETFL,NULL ); if(val<0) err_quit("get file descriptor err\n"); // O_RDONLY=00,O_WRONLY= 01,O_RDWR=02 const char* attr[]={"read only","write only","read write","unknown access mode"}; printf("%d-%s",val& O_ACCMODE,attr[val& O_ACCMODE]); if (val & O_APPEND) printf(", append"); if (val & O_NONBLOCK) printf(", nonblocking"); if (val & O_SYNC) printf(", synchronous writes"); putchar('\n'); exit(0); } // > ./exe 0 0</dev/tty // 0-read only // > ./exe 1 1>out.o // > cat out.o // 1-write only // > ./exe 2 2>>out.o // 1-write only, append // > cat out.o // 1-write only // > bash // $ ./exe 5 5<>out.o // 2-read write