来点八股文(二) 文件IO

APUE是组内的学习任务,平常工作比较忙,只好周末学了QAQ
真的栓Q,不想占用周末时间啊

文章目录

    • 基础知识
    • 不带缓存的IO
      • open函数
      • create函数
      • close函数
      • lseek函数
      • read函数
      • write函数
      • 文件共享
      • fcntl函数
      • dup和dup2
      • ioctl函数
      • /dev/fd目录
    • 文件和目录
      • stat、fstat和lstat
      • access
      • umask/chmod/fchmod
      • chown/fchown/lchown
      • 文件系统
      • symlink和readlink
      • 读目录
      • chdir/fchdir
      • sync/fsync
    • 标准IO库
      • 缓存
      • 流操作

基础知识

  • 文字描述符是一个小的非负整数,内核用以标识一个特定进程正在存访的文件。当内核打开一个现存文件或创建一个新文件时,它就返回一个文件描述符。当读、写文件时,就可使用它。
    我们可以在/proc/sys/fs/file-max中查看它的大小(系统最大描述符号)。
cat /proc/sys/fs/file-max
52626599
  • 文件描述符通常从 0(表示标准输入)、1(表示标准输出)和 2(表示标准错误)开始。可以使用相同的 ><2> 符号来重定向标准输入、输出和标准错误
#include 
int main() {
    int c;

    while ((c = getc(stdin)) != EOF) {
        if (putc(c, stdout) == EOF) {
            perror("Error writing to stdout");
            return 1;
        }
    }

    if (ferror(stdin)) {
        perror("Error reading from stdin");
        return 1;
    }

    return 0;
}
  • 程序(program)是存放在磁盘文件中的可执行文件。使用6个exec函数中的一个由内核将程序读入存储器,并使其执行。

  • 当UNIX函数出错时,往常返回一个负值,而且整型变量errno通常设置为具有特定信息的一个值,errno是一个线程局部存储(ThreadLocalStorage,TLS)的宏或者函数。如果没有出错,则其值不会被一个例程清除。因此,仅当函数的返回值指明出错时,才检验其值。而且任一函数都不会将errno值设置为0,在中定义的所有常数都不为0。
    ● time系统调用会产生三个时间:时钟时间:又称为墙上时钟时间(wallclocktime)。它是进程运行的时间总量,其值与系统中同时运行的进程数有关。用户CPU时间:执行用户指令所用的时间量。系统CPU时间:为该进程执行内核所经历的时间。
    来点八股文(二) 文件IO_第1张图片

不带缓存的IO

文件IO可以分为带缓存的IO和不带缓存的IO。
不带缓存的IO典型的例如:open、read、write、lseek、close。

open函数

来点八股文(二) 文件IO_第2张图片
open函数可以指定只读、只写、读写打开。
并且可以指定是否每次写都追加到文件末尾(使之变为原子操作)、是否创建文件、如果有同名文件时的操作等。
由open函数返回的文件描述符一定是最小的未用描述符。


给出示例
由o p e n返回的文件描述符一定是最小的未用描述符数字。这一点被很多应用程序用来在标
准输入、标准输出或标准出错输出上打开一个新的文件。例如,一个应用程序可以先关闭标准
输出(通常是文件描述符 1 ),然后打开另一个文件,事先就能了解到该文件一定会在文件描述
符1上打开


create函数

来点八股文(二) 文件IO_第3张图片
create函数等价于:

// 只写打开、文件不存在则创建、文件存在则将其长度截断为0
open(pathname,O_WRONL|O_CREAT|O_TRUNC,mode)

close函数

在这里插入图片描述
关闭一个文件时也释放进程所有的记录锁;当进程终止时,所有打开的文件由内核自动关闭。

lseek函数

在这里插入图片描述
用来调整文件偏移量
whence可以设置为相对文件开始、相对当前值、相对文件长度
如果文件偏移量大于当前文件长度,对于文件的下一次写将会延长该文件,并且形成一个空洞,对于文件中没有写过的字节都被读为0。
lseek函数只修改文件表项中的当前文件位移量,没有进行任何I/O操作

read函数

在这里插入图片描述
如果read成功,将会返回得到的字节数,如果到达文件尾部,则返回0。
有多种情况可能导致读到的字节数小于要求字节数。
•读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之
前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0(文件
尾端)。
•当从终端设备读时,通常一次最多读一行(第11章将介绍如何改变这一点)。
•当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
•某些面向记录的设备,例如磁带,一次最多返回一个记录。

write函数

在这里插入图片描述
如果返回值和给定值不同,则表示出错。
出错的可能原因有:
磁盘已写满,或者超过了对一个给定进程的文件长度限制

read和write函数的buffersize值应该设置为大于文件块长(大于此长度后对读写效率无影响)


解释一下


文件共享

每个进程在进程表项中维护一张文件表,记录了打开的文件描述符信息。
内核 为所有文件各维护一张文件表,进程的文件表项指向这张表,这张表包括:文件状态标志(读、写、增写、同步、阻塞)、文件偏移量、指向v节点表的指针
v节点包含了文件类型和对此文件进行操作的函数的指针信息、i节点信息。i节点信息指的是文件的索引节点信息。i节点包含了文件所有者、文件长度、文件设备、文件数据块指针等
来点八股文(二) 文件IO_第4张图片
当两个进程同时打开同一个文件时的情况:
来点八股文(二) 文件IO_第5张图片

  • 在完成每个write后,在文件表项中的当前文件位移量即增加所写的字节数。如果这使当前文件位移量超过了当前文件长度,则在i节点表项中的当前文件长度被设置为当前文件位移量(也就是该文件加长了)。

  • 如果用O_APPEND标志打开了一个文件,则相应标志也被设置到文件表项的文件状态标志中。每次对这种具有添写标志的文件执行写操作时,在文件表项中的当前文件位移量首先被设置为i节点表项中的文件长度。这就使得每次写的数据都添加到文件的当前尾端处。

  • lseek函数只修改文件表项中的当前文件位移量,没有进行任何I/O操作。

  • 若一个文件用lseek被定位到文件当前的尾端,则文件表项中的当前文件位移量被设置为i节点表项中的当前文件长度。

fcntl函数

fcntl可以改变打开的文件的性质。
来点八股文(二) 文件IO_第6张图片

  • 复制一个现存的描述符(cmd=F_DUPFD), 共享同一个文件文件表项。
  • 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)。
  • 获得/设置文件状态标志(cmd=F_GETFL或F_SETFL)。
  • 获得/设置异步I/O有权(cmd=F_GETOWN或F_SETOWN)。
  • 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)。
    来点八股文(二) 文件IO_第7张图片
    openssl用来设置文件描述符属性,nginx用来实现非阻塞io,sqlite用来实现文件锁。

dup和dup2

来点八股文(二) 文件IO_第8张图片
由dup打开的文件描述符共享同一个文件文件表项。

ioctl函数

在这里插入图片描述
来点八股文(二) 文件IO_第9张图片
嵌入式的同学,opencv可能用的比较多。

/dev/fd目录

虚拟的文件路径,每个进程各有一个,打开文件/dev/fd/n等效于复制描述符n(假定描述符n是打开的)。

文件和目录

stat、fstat和lstat

来点八股文(二) 文件IO_第10张图片

stat返回与此命名文件有关的信息结构,fstat获得已在描述符fieldes上打开的文件信息,lstat类似于stat,但是当文件是一个符号链接时,返回该符号链接的有关信息。
来点八股文(二) 文件IO_第11张图片
来点八股文(二) 文件IO_第12张图片

  • st_mode 文件类型 用一组文件类型宏来判断文件类型:

来点八股文(二) 文件IO_第13张图片
来点八股文(二) 文件IO_第14张图片

  • st_uid 所有者
  • st_gid 组所有者
    来点八股文(二) 文件IO_第15张图片
    在执行程序时保存的有效用户ID和有效组ID的副本
  • 文件存取许可权
    有几个规则:
    • 我们用名字打开任一类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应具有执行许可权。如果PATH环境变量指定了一个我们不具有执行许可权的目录,那么shell决不会在该目录下找到可执行文件。
    •为了在一个目录中创建一个新文件,必须对该目录具有写许可权和执行许可权
    •为了删除一个文件,必须对包含该文件的目录具有写许可权和执行许可权。对该文件本身则不需要有读、写许可权。
    •如果用6个exec函数中的任何一个执行某个文件,都必须对该文件具有执行许可权
    来点八股文(二) 文件IO_第16张图片
  • st_size 包含了以字节为单位的文件的长度
  • st_atime st_ctime st_mtime 文件时间(使用utime函数更改)
    来点八股文(二) 文件IO_第17张图片

access

测试文件许可权

在这里插入图片描述
来点八股文(二) 文件IO_第18张图片

umask/chmod/fchmod

umask为进程设置文件方式创建屏蔽字,并且返回以前的值
在这里插入图片描述
fchmod对已打开的文件设立存取许可权
来点八股文(二) 文件IO_第19张图片

chown/fchown/lchown

用于更改文件的用户id和组id。
lchown更改符号连接的所有者而不是该符号连接指向的文件
来点八股文(二) 文件IO_第20张图片

文件系统

来点八股文(二) 文件IO_第21张图片

  • 每个i节点都有一个连接计数
  • ln命令不能跨越文件系统
  • mv文件如果在一个文件系统中,只需要更新文件的目录项即可

symlink和readlink

在这里插入图片描述

symlink函数创建指向acualpath的新目录项sympath

在这里插入图片描述
因为open函数跟随符号连接,所以使用readlink函数打开连接本身。

读目录

对某个目录具有存取许可权的任一用户都可读该目录,但是只有内核才能写目录(防止文件系统发生混乱)。一个目录的写许可权位和执行许可权位决定了在该目录中能否创建新文件以及删除文件,它们并不表示能否写目录本身。

chdir/fchdir

用来更改当前工作目录
来点八股文(二) 文件IO_第22张图片

sync/fsync

来点八股文(二) 文件IO_第23张图片

用来将已修改/指定文件的已修改但还未写入磁盘的文件缓存数据写入磁盘中。

标准IO库

标准io库的操作是围绕流进行的。

缓存

有三种缓存级别:全缓存、行缓存、不带缓存。

ANSI C要求:

  1. 当输入输出不涉及交互作用设备时,才能是全缓存的
  2. 标准错误不会是全缓存的
    来点八股文(二) 文件IO_第24张图片
    使用setbuf打开或关闭缓存机制,可以用setvbuf精确设置缓存类型。
    来点八股文(二) 文件IO_第25张图片

流操作

来点八股文(二) 文件IO_第26张图片
fopen打开路径名上的一个指示的文件
freopen在特定的流上打开一个指定的文件,如果已经打开,则关闭该流
fdopen取一个现存的文件描述符,并使一个标准的io流与该描述符结合。
来点八股文(二) 文件IO_第27张图片
流的读写
一次读一个字符来点八股文(二) 文件IO_第28张图片

getc可能是个宏,但是fgetc一定是个函数,所以&getc是一个函数调用表达式的地址而不是函数地址。
因为getc可以被宏展开,合并系统调用,所以效率更高。
getchar默认从标准输入流中读取,相当于getc(stdin)

因为出错和到达文件尾都返回EOF,所以需要调用ferror或者feof来区分两种情况。调用clearerr清除出错和文件结束标志。
来点八股文(二) 文件IO_第29张图片

每次一行的IO

你可能感兴趣的:(unix)