zoukankan      html  css  js  c++  java
  • Apue.2e学习笔记chapter3

    本章学习文件I/O。

    不带缓冲指的是每个读写操作都会调用内核中的一个系统调用。

    File descriptor

        相当于win下的文件句柄,一般0:stdin,1:stdout,2:stderr,在依从POSIX标准的系统中,使用宏STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO来确定,最好不用硬编码。

        文件描述符的取值范围是0~OPEN_MAX。

    open function

        #include <fcntl.h>

        int open(const char *pathname, into flag, … /*mode_t mode */)

    flag包括O_RDONLY, O_WRONLY和O_RDWR(以上3选1),和O_APPEND, O_CREAT, O_EXCL(测试是否存在),O_TRUNC, O_NOCITY(终端相关),O_NONBLOCK(非阻塞,对FIFO、块文件或字符设备文件有效),SUS扩展选项:O_DYSNC(每次write等待物理I/O完成,属性除外),O_RSYNC(读同步),O_SYNC(写操作,包括属性,在不同系统上实现不太一样)。

        open操作返回的文件描述符一定是未用的最小文件描述符。

        如果文件名长度大于NAME_MAX有的系统会直接截断,有的会返回出错。_POSIX_NO_TRUNC确定了遵从哪种行为,如果=1,那么出错,设置errno;否则直接截断。

    creat function

        令人蛋碎的create函数,别问为啥没有最后的e,这个问题去问ken大神。

        int creat(const char *pathname, mode_t mode)

        这个函数现在一般不用了,等价于open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode),之所以存在,是对旧系统的兼容。

    close function

        #include <unistd.h>

        int close(int filedes),同上面的一样,失败返回-1

    lseek function

        off_t lseek(int filedes, off_t offset, int whence)

        通过whence参数描述具体的行为,可以是SEEK_SET, SEEK_CUR, SEEK_END,具体意思和ISO C中一致。如果是确定当前偏移量,可以用lseek(fd,0,SEEK_CUR)

        off_t的实现根据平台有所变化,一般有32位和64位两种。可以使用sysconf确认,这里标准有些混乱。

        注意:某些设备的偏移量可能是负值,因此对结果,确认失败与-1比较是否相等比确认负值更为妥当。

        如果写入内容超出文件末端,文件大小会被扩大,但是两块内容之间的空洞是否占用空间是由具体实现决定的。

    read function

        sszie_t read(int filedes, void *buf, size_t nbytes);//posix.1

        int read(int filedes, char *buf, unsigned nbytes);//经典

        返回读到的字节数(或-1),读到EOF则返回0

    write function

        sszie_t write(int filedes, const void *buf, size_t nbytes)

        类似read,失败的原因多半是磁盘空间已经写满,或文件大小超限。

    对于Unix系统,字符和二进制没有区别…换句话说,一切皆字符。

    buf大小的选择,一般与文件系统的block长度相关,可以通过实验得出恰当值。

    文件共享

        系统有一张进程表,每个进程在其中有一个记录项,记录项包含一张该进程打开的所有文件的描述符表,这个表格主要包含两项内容:文件描述符标志和对应的文件表项指针;后者包含文件状态标志、文件偏移量和指向该文件v节点表项的指针。v节点包含了文件类型和对此文件进行各种操作的函数的指针,此外还可能包含了文件的i节点(索引节点)。i节点是文件的固有属性,包含文件的所有者、文件长度、文件所在的设备和指向文件实际数据块在磁盘所在位置的指针。

        以上描述根据实现不同而有所不同,Linux没有v节点,采用了一个独立于文件系统的i节点和一个依赖于文件系统的i节点。

        一个给定的文件只有一个v节点表项,但是打开该文件的每个独立进程都有自己的文件表项(以维持各自的偏移量)。当写入的偏移量超出文件长度,i节点中的对应信息也会更新为文件偏移量。

        可能有多个文件描述符项指向同一个文件表项;文件状态标志会影响所有指向该文件表项的任何进程中的描述符。

    原子操作

        所谓原子操作,意思就是不能被打断的操作,要么执行,要么不执行,不能被暂停的操作,主要用于多线程同步。

        SUS的扩展原子I/O,包括:

        ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset)

        ssize_t pwrite(int filedes, const void *buf, size_t nbytes, off_t offset)

        注意这里直接将偏移量做为参数,因此相当于将lseek和read/write合并为一个原子(当然,不更新文件偏移量指针)

        open(fd, O_CREAT | O_EXCL)也可以作为一个原子,意思是如果不存在就创建文件,否则返回-1,O_TRUNC也有类似的效果。

    dup和dup2

        复制文件描述符的函数(命名方式非常糟糕)。

        int dup(int filedes);

        int dup(int fiedes1, int filedes2)

        前者一定返回可用的最小文件描述符,后者可以通过filedes2指定(如果filedes2已经打开,需要先将其关闭,如果两个参数相等就不用关闭了)。

        dup后的描述符共享文件表项,所以需要使用原子函数读写。

        另外,可以使用万用函数fcntl进行复制,dup1: fcntl( fd, P_DUPFD, 0); dup2: close(fd2); fcntl(fd1, F_DUPFD,fd2),注意后者不是原子操作。

    sysc, fsync和fdatasync

        用于高速缓存的读写同步。

        sync最简单,会将所有修改过的块缓冲区排入写队列,然后返回(作用于全局),并不等待写完成。系统守护进程会周期地调用该函数维护系统。

        fsync(fd),作用于单一文件描述符,等待写操作结束。

        fdatasync类似于fsync,但是只更新数据,不更新属性。(并非所有系统都支持)

    fctnl function

        字面意思就是文件操作函数,万用函数。主要用来改变已经打开文件的性质。

        #include <fctnl.h>

        int fctnl(int filedes, int cmd, … /* int arg */ )

        行为依赖于cmd,包括:

    • F_DUPFD,复制文件描述符,前面有相关描述,从略;
    • F_GETFD, F_SETFD,获得/设置文件描述符标记,现在只有一个:FD_CLOEXEC(执行时是否关闭);
    • F_GETFL, F_SETFL,获得/设置文件状态标志,即O_xxx一系列open函数使用的标志;
    • F_GETOWN, F_SETOWN,获得/设置异步I/O所有权,见14章;
    • F_GETLK, F_SETLK, F_SETLKW,获得/设置记录锁;

    对于F_GETFL/F_SETFL,有个比较蛋碎的地方,它们共占一位,如果需要取得具体的,先要把结果与O_ACCMODE相与,然后再和3个读取方式逐一比较;其他的就直接位与就行了,=1说明对应标志置位。

    ioctl function

        字面意思是I/O控制函数,万用函数。

        #include <unistd.h> /*system V*/

        #include <sys/ioctl.h> /*BSD and Linux*/

        #include <stropts.h> /*XSI STREAMS*/

        int ioctl(int filedes, int request, …)

        这货是SUS的一个扩展,功能非常多。通常最后一个参数是指向一个变量或结构的指针。除了这个头文件之外,还需要其操作设备的头文件。每个设备驱动都可以自定义自身的ioctl命令,因此它的用法非常多。

    /dev/fd

        非POSIX.1特性。打开/dev/fd/n,相当于复制描述符n(假设n是打开的),主要用于shell。

        有些系统可能直接提供/dev/stdin, /dev/stdout和/dev/stderr来表述标准流。

        有些bash shell中,可以使用 '-'来表述标准输入,但是如果有/dev/fd/0或/dev/stdin,显然使用后者更清晰。

  • 相关阅读:
    array_diff()和array_diff_assoc()
    React出现错误:Uncaught TypeError: this.setState is not a function
    predis操作大全
    MacOS下出现-bash: 命令: command not found的解决方法
    OnCreateClient学习总结
    MFC之CSingleDocTemplate构造函数
    CString 的成员函数详解
    MFC 中Invalidate的使用
    CFileFind类的使用总结(转)
    MFC CSplitterWnd的用法
  • 原文地址:https://www.cnblogs.com/livewithnorest/p/2860257.html
Copyright © 2011-2022 走看看