文件IO

Posted zongzi10010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文件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);

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
",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

以上是关于文件IO的主要内容,如果未能解决你的问题,请参考以下文章

编写一个程序, 将 a.txt 文件中的单词与 b.txt 文件中的 单词交替合并到 c.txt 文件中, a.txt 文件中的单词用回车符 分隔, b.txt 文件中用回车或空格进行分隔。(代码片段

csharp C#代码片段 - 使类成为Singleton模式。 (C#4.0+)https://heiswayi.github.io/2016/simple-singleton-pattern-us

是否有一种方法可以将实时记录的音频片段连续发送到Flutter.io中的后端服务器?

Android android.view.InflateException Binary XML 文件第 16 行:膨胀类片段时出错

golang代码片段(摘抄)

为啥这段代码会泄露? (简单的代码片段)