文件IO

荒凉一梦 提交于 2019-11-28 07:57:16

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为普通描述符.如果fdAT_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 类型说明

  1. 使用sysconf查看
  2. c99使用命令getconf查看
  3. 可以手动指定设置宏_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 不支持使用fcntlO_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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!