zoukankan      html  css  js  c++  java
  • 文件IO


    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);
    

    注意 使用open创建已经存在的文件,文件的权限位不变 apue 练习题4.4

    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 是内核维护的,针对的是所有指向这个文件的进程的

    mark

    mark

    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的时候确认的

    标准IO流

    标准io流如果要使用fsync,需要先使用fflush将存储从内存缓冲区输出到实际的流中,再存储.<P5.5练习题>

    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);
    

    目录遍历

    // fn 是对遍历到的文件做的钩子函数
    
    int nftw(const char *dirpath,
    	   int (*fn) (const char *fpath, const struct stat *sb,
    				  int typeflag, struct FTW *ftwbuf),
    	   int nopenfd, int flags);
    
    int ftw(const char *dirpath,
    	   int (*fn) (const char *fpath, const struct stat *sb,
    				  int typeflag),
    	   int nopenfd);
    
    

    也可以用另一个系列的函数遍历

     
     fts, fts_open, fts_read, fts_children, fts_set, fts_close - traverse a file hierarchy
     
    

    代码附录

    获取最大文件描述符

    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
    ",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 :
    :%s
    ",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 :
    :%s
    ",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 
    ");
    }
    
    // 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 
    ");
    }
    
    // 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
    ");
        }
    
        int val= fcntl(atoi(argv[1]), F_GETFL,NULL );
        if(val<0)
             err_quit("get file descriptor err
    ");
    
    
        // 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('
    ');
        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
    
  • 相关阅读:
    序列化与反序列化
    反射学习 :反射程序集
    转载 [TopLanguage]: 马一哥对开发“过程”的描述
    DNS域名规则
    Joel Spolsky: 好的界面设计应当符合用户预期
    开始在博客园中写博客
    2009牛年的答卷及2010虎年的题目
    Jenkins安装使用教程
    git第一次上传代码
    allure安装配置集成测试报告
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/11938693.html
Copyright © 2011-2022 走看看