文件IO

标准输入:0
标准输出:1
错误输出:2
符号常量为:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO

open或者openat:
int open(const char* path,int oflag,....);
int openat(int fd,const char *path,int oflag,...);
path:要打开或者要创建的名字
oflag:说明此函数的多个选项
    O_RDONLY    只读打开
    O_WRONLY    只写打开
    O_RDWR      读写打开
大多数的实现将O_RDONLY定义为0,O_WRONLY定义为1,O_RDWR定义为2
    O_EXEC      只执行打开
    O_SEARCH    只搜索打开

这5个常量中必须指定一个且只能指定一个。下列常量则是可选的:
    O_APPEND    每次都追加到文件的尾端
    O_CLOEXEC   把FD_CLOEXEC常量设置为文件描述符标志
    O_CREAT     若文件不存在则创建它。使用这个选项的时候,
            open参数需同时说明第三个参数mode(openat则是第四个)
            用mode指定该新文件的访问权限位,可以使用进程umask
            值修改它
    O_DIRECTORY     如果path不是目录,则出错
    O_EXCL      如果同时指定了O_CREAT,而文件已经存在,则出错
    O_NOCTTY    如果path引用的是终端设备,则不将该设备分配作为此进程的
            终端控制
    O_NOFOLLOW  如果path引用的是一个符号连接,则出错
    O_NONBLOCK  如果path引用的是一个FIFO、一个块特殊文件或一个字符
            特殊文件,则此选项为文件的本次打开操作和后续的I/O
            操作设置非组塞模式
    O_SYNC      每次write等待物理I/O操作完成,包括由该write操作引起
            的文件属性跟新所需的I/O
    O_TRUNC     如果此文件存在,而且为只写或者是读-写成功打开,则将其
            长度截断为0
    O_TTY_INIT  如果打开一个还未打开的终端设备,设置非标准termios参数
            值,使其符合signal UNIX Specification
    O_DSYNC     使每次write要等待物理I/O操作完成,但是如果写操作并不
            影响读取刚写入的数据,则不需等待文件属性被更新
    O_RSYNC     使每一个文件描述符作为参数进行的read操作等待,直至
            所有的对文件同一部分挂起的写操作都完成了

creat函数:创建一个新的文件
    int creat(const char *path,mode_t mode);
    等效于open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);
    不足是它以只写的方式打开所创建的文件。
    使用open实现:
    open(path,O_RDWR | O_CREAT | O_TRUNC,mode);


close函数:打开一个文件
    #include <unistd.h>
    int close(int fd);
    关闭一个文件的时候还会释放该进程加在该文件上的所有记录锁。
    当一个进程终止的时候,内核自动关闭它所有的打开文件。

lseek函数:当前文件偏移量
    调用lseek显式为一个打开文件设置偏移量
    #include <unistd.h>
    off_t lseek(int fd,off_t offset,int whence);
    其中offset于参数whence有关:
    SEEK_SET:设置偏移量为离文件开始处offset个字节
    SEEK_CUR:设置偏移量为离当前位置加offset,可正可负
    SEEK_END:设置偏移量为离文件末尾加offset
        off_t currpos;
        currpos=lseek(fd,0,SEEK_CUR);
        如果文件描述符指向的是一个管道、FIFO、网络套接字,则lseek
        返回-1,并将errno设置为ESPIPE.
    whence:0(绝对偏移量)
    whence:1(相对当前位置的偏移量)
    whence:2(相对文件末尾的偏移量)

        #include <unistd.h>
        #include <stdio.h>
        #include <stdlib.h>

        int main(void){
            if(lseek(STDIN_FILENO,0,SEEK_CUR)==-1){
                printf("cannot seek
");
            }
            else
                printf("seek ok
");
            exit(0);
        }

read函数:从打开文件中读取数据
    #include <unistd.h>
    ssize_t read(int fd,void *buf,size_t nbytes);
    read成功,则返回读到的字节数。如已到达文件末尾,则返回0。


write函数:向打开文件写数据
    #include <unistd.h>
    ssize_t write(int fd,const void *buf,size_t nbytes);
    若返回成功,表示的是以写的字节数,若失败,返回的是-1。


pread和pwrite:
    #include <unistd.h>
    ssize_t pread(int fd,void *buf,size_t nbytes,off_t offset);
    返回值:读到的字节数,若已到文件尾,返回0;若出错,返回-1
    ssize_t pwrite(int fd,const void *buf,size_t nbytes,off_t offset);
    返回值:若成功,返回已写的字节数;若出错,返回-1

    调用pread相当于调用lseek后调用read,但是pread有区别:
    1、调用pread时,无法中断其定位和读操作
    2、不更新当前文件偏移量
    调用pread相当于调用lseek后调用write,但是pwrite有类似的区别

dup和dup2函数:复制一个现有的文件描述符
    #include <unistd.h>
    int dup(int fd);
    int dup2(int fd,int fd2);
    有dup返回的新文件描述符一定是当前可用文件描述符中的最小的数值。
    对于dup2,可以用fd2参数指定新描述符的值。如果fd2已经打开,则先将其关闭。
    如若fd等于fd2,则dup2返回fd2,而不关闭它。否则,fd2的FD_CLOSEXEC文件
    描述符标志就被清除,这样fd2在进程调用exec时是打开状态.

    调用dup(fd);
    等效于fcntl(fd,F_DUPFD,0);
    而调用dup2(fd,fd2);
    等效于:
        close(fd2);
        fcntl(fd,F_DUPFD,fd2);
    dup2并不完全等同于以上,区别如下:
        1、dup2是一个原子操作,而close和fcntl包括两个函数调用。
        可能在close和fcntl之间调用了信号捕获函数,它们可能修改文件
        描述符,如果不同线程改变了文件描述符也会出现问题
        2、dup2和fcntl有一些不同的errno

sync、fsync和fdatasync函数:
    传统的UNIX系统实现在内核中设有缓冲区高速缓存或者页高速缓存,大多数磁盘
    I/O都通过缓冲区进行。
    当我们向文件写入数据时,内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候
    再写入磁盘。
    通常,当内核需要重用缓冲区来存放其他磁盘块数据时,它会把所以延迟写数据块写入磁盘
    为了保证磁盘上实际文件系统与缓冲区中内容的一致性,UNIX系统提供了sync、fsync、
    fdatasync函数。

    #include <unistd.h>
    int fsync(int fd);
    int fdatasync(int fd);
    void sync(void);

    sync只是将所以修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘
    操作结束。
    通常,成为update的系统守护进程周期性地调用(一般隔30秒)sync函数。这就
    保证了定期冲洗(flush)内核的块缓冲区。命令sync(l)也调用sync函数
    fsync只对由文件描述符fd指定的一个文件起作用,并且等待  写磁盘操作结束。
    fsync可用于数据库这样的应用程序。
    fdatasync函数类似于fsync函数,但它只影响文件的数据部分。而除数据外fsync
    还会同步更新文件的属性。

简单说文件描述符:
    文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护
    的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回
    一个文件描述符。

fcntl函数:改变已经打开文件的属性
    #include <fcntl.h>
    int fcntl(int fd,int cmd,.../*int arg*/);
    返回值:成功,则依赖于cmd,出错,返回-1。

    fcntl函数的5种功能:
    1、复制一个已有的描述符(cmd=F_DUPFD或者F_DUPFD_CLOEXEC)
    2、获取/设置文件描述符标志(cmd=F_GETFD或者F_SETFD)。
    3、获取/设置文件状态标志(cmd=F_GETFL或者F_SETFL)。
    4、获取/设置异步I/O所有权(cmd=F_GETOWN或者F_SETOWN)。
    5、获取/设置记录锁(cmd=F_GETLK、F_SETLK或者F_SETLKW);

    F_DUPFD     复制文件描述符fd。新文件描述符作为函数只返回。它是尚未打开的
            各描述符大于或者等于第3个参数值(取为整形值)中各值的最小值。
            新描述符与fd共享同一文件表项。但是,新描述符有它自己的一套文件
            描述符标志,其FD_CLOEXEC文件描述符标志被清除(这表示该描述符在
            exec时仍保持有效)

    F_DUPFD_CLOEXEC 复制文件描述符,设置与新描述符关联的FD_CLOEXEC文件描述符标
            志的值,返回新文件描述符。

    F_GETFD     对应与fd的文件描述符标志作为函数值返回。当前只定义了一个文件
            描述符标志FD_CLOEXEC。

    F_SETFD     用于设置文件描述符标志。新标志值按第3个参数(取为整数值)设置
            现在很多程序不使用FD_CLOEXEC,而是将此标志设置为0,(系统默认)
            在exec时不关闭,1在exec关闭

    F_GETFL     对应与fd的文件状态标志作为函数值返回

    F_SETFL     将文件状态标志设置为第3个参数的值,(取整形值)。
            可以更改的标志为:O_APPEND  O_NONBLOCK  O_SYNC  O_DSYNC
            O_RSYNC  O_FSYNC  O_ASYNC

    F_GETOWN    获取当前接受SIGIO和SIGURG信号的进程ID或进程组ID。

    F_SETOWN    设置接受SIGIO和SIGURG信号的进程ID或进程组ID。
            正的arg指定一个进程ID,负的arg表示等于arg绝对值的一个进程组ID

fcntl的返回值与命令有关。如果出错,所以命令都返回-1,如果成功返回其他值
F_DUPFD:返回的是新的文件描述符
F_GETFD:返回的时相应的标志
F_GETFL:返回的时相应的标志
F_GETOWN:返回一个正的进程ID或者一个负的进程组ID

例子:
O_ACCMODE<0003>:读写文件操作时,用于取出flag的低2位
O_RDONLY<00>:只读打开
O_WRONLY<01>:只写打开
O_RDWR<02>:读写打开 
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>

    int main(int argc,char *argv[]){
        int val;

        if(argc!=2){
                printf("usage:a.out <descriptor#>");
        }

        if((val=fcntl(atoi(argv[1]),F_GETFL,0))<0)
                printf("fcntl error for fd %d",atoi(argv[1]));


        switch(val & O_ACCMODE){
                case O_RDONLY:
                        printf("read only");
                        break;
                case O_WRONLY:
                        printf("write only");
                        break;

                case O_RDWR:
                        printf("read write");
                        break;
                default:
                        printf("unknown access mode");
        }

        if(val & O_APPEND)
                printf(",append");
        if(val & O_NONBLOCK)
                printf(",nonblocking");
        if(val & O_SYNC)
                printf(",synchronous writes");

        #if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)
                if(val & O_FSYNC)
                        printf(",synchronous writes");
        #endif

        putchar('
');
        exit(0);
    }
运行:
    ./a.out 0 < /dev/tty
    ./a.out 1 > temp.foo
    ./a.out 2 2>>temp.foo
    ./a.out 5 5<>temp.foo
    字句5<>temp.foo表示的时文件描述符5上打开文件temp.foo以供读、写。
修改文件描述符标志或者是文件状态标志(示例函数):
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>

    void set_fl(int fd,int flags)
    {
        int val;

        if((val=fcntl(fd,F_GETFL,0))<0)
        {
            printf("fcntl F_GETFL error");
        }

        val |= flags;

        if(fcntl(fd,F_SETFL,val)<0)
            printf("fcntl F_SETFL error");
    }
    其中将一条语句改为:
    val &=~flags;//表示的时关闭某个文件标志
    如果加上set_fl(STDOUT_FILENO,O_SYNC);开启了同步写操作
    这样使得每次write都要等待,直到数据已写到磁盘上再返回。

ioctl函数:
    ioctl函数一直是I/O操作的杂物箱。
    #include <unistd.h>
    #include <sys/ioctl.h>
    int ioctl(int fd,int request,...)

/dev/fd:
    较新的系统都提供名为/dev/fd的目录,其目录项是名为0,1,2等文件
    函数的调用:
    fd=open("/dev/fd/0",mode);
    大多数系统忽略它所指定的mode,而另外一些系统则要求mode必须是所引用的文件
    在这里是标准输入,初始打开是所使用的打开模式的一个子集。
    上面等效于:fd=dup(0);
    若描述符0先前打开为只读,那么我们也只能对fd进行读操作。即使系统忽略打开模式,
    而且下列调用是成功的:
    fd=open("/dev/fd/0",O_RDWR);
    我们仍然不能对fd进行写操作。
技术不分国界
原文地址:https://www.cnblogs.com/angels-yaoyao/p/12443620.html