文章目录
begintime:2019-05-02-12点30分
3.1 引言
理解了术语不带缓冲的I/O(unbuffered I/O),指的是每个read和write都调用内核中的一个系统调用。这些不带缓冲的I/O函数不是ISO C的组成部分,但是,它们是POSIX.1和SUS的组成部分。
只要涉及在多个进程间共享资源,原子操作的概念就变得非常重要。通过文件I/O和open函数的参数来讨论此概念。
3.2 文件描述符
明白对于内核而言,所有打开的文件都通过文件描述符引用。
当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
当读、写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传递给read或write。
按照惯例,unix系统shell把文件描述符0与进程的标准输入相关联,文件描述符1与标准输出相关联,文件描述符2与标准错误相关联。这是各种shell以及很多应用程序使用的惯例,与unix内核无关。尽管如此,如果不遵循这种惯例,很多unix系统应用程序就不能正常工作。
在符号POSIX.1的应用程序中,幻数0、1、2虽然已被标准化,但应把它们替换成符号常量STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO以提高可读性。在头文件<unistd.h>中定义。
文件描述符的变化范围是0~OPEN_MAX-1,对于Free BSD 8.0、Linux 3.2.0、Mac OS 10.6.8、Solaris 10以及solaris 10 文件描述符的变化范围几乎是无限的。
3.3 函数open和openat
我只能说,太详细了,随用随查吧。。。
由open和openat函数返回的文件描述符一定是最小的未用描述符数值。
这一点被某些应用程序用来在标准输入、标准输出或标准错误上打开新文件。例如,一个应用程序可以先关闭标准输出(通常是文件描述符1),然后打开另一个文件,执行打开操作就能了解到该文件一定会在文件描述符1上打开。更好的方法是使用dup函数,可以保证在一个给定的描述符上打开一个文件。
fd参数把open和openat函数区分开,共有3种可能性:
(1)
(2)
(3)
openat函数是POSIX.1最新版中新增的一类函数之一,希望解决两个问题:
第一,让线程可以使用相对路径名打开文件目录中的文件,而不再只能打开当前目录。在第11章会看到,同一进程中的所有线程共享相同的当前工作目录,因此很难让同一进程的多个不同线程在同一时间工作在不同的目录中。
第二,可以避免time-of-check-to-time-of-use(TOCTTOU,检查与使用时差)错误。
TOCTTOU错误的基本思想是:如果有两个基于文件的函数调用,其中第二个调用依赖于第一个调用的结果,那么程序是脆弱的。
endtime:13点33分
begintime:2019-05-03-13点18分
3.3.1文件名和路径名截断
如果NAME_MAX是14,而存在一个文件名恰好就是14个字符,那么以路径名作为参数的任一函数(open、stat等)都无法确定该文件的原始名是什么。其原因是这些函数无法判断该文件名是否被阶段过。
在POSIX.1中,常量_POSIX_NO_TRUNC决定是要截断过长的文件名或路径名,还是返回一个出错。正如我们在第2章中已经见过的,根据文件系统类型,此值可以变化。可以使用fpathconf或pathconf来查询目录具体支持何种行为,到底是截断过长的文件名还是返回出错。
若_POSIX_NO_TRUNC有效,则在整个路径名超过PATH_MAX,或路径名中的任一文件名超过NAME_MAX时,出错返回,并将errno设置为ENAMETOOLONG(错误名称过长)。
3.4 函数creat
在open函数提供了O_CREAT和O_TRUNC后不再需要单独地creat函数。当然,想的话,还能使用
3.5 函数close
关闭一个文件时还会释放该进程加在该文件上的所有记录锁(14.3节再学)。
当一个进程关闭时,内核自动关闭它所有的打开文件。很多程序都利用了这一功能而不显示地调用close关闭打开文件。
3.6 函数lseek
每个文件都有一个与其关联的“当前文件偏移量”(current file offect)。它通常是一个非负整数,用以度量从文件开始处计算的字节数。通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。按系统默认的情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0。
可以调用lseek显式地为一个打开文件设置偏移量。
成功返回新的文件偏移量;出错,返回-1
这种方法也可用来确定所涉及的文件是否可以设置偏移量。如果文件描述符指向的是一个管道、FIFO或网络套接字,则lseek返回-1,并将errno 设置为ESPIPE。
比较lseek的返回值时应当谨慎(因为在某些设备中偏移量的值可以为负),不要测试它是否小于0,而要测试它是否等于-1。
lseek使用的偏移量是off_t类型
3.7 函数read
函数类型 ssize_t
如read成功,则返回读到的字节数。如已到达文件的尾端,则返回0。
ISO C标准中,类型void * 用于表示通用指针
3.8 函数write
其返回值通常与参数nbytes的值相同,否则表示出错。write出错的一个常见原因是磁盘已写满,或者超过了一个给定进程的文件长度限制
3.9 I/O效率
如何选取合适的BUFFSIZE值
当BUFFSIZE的值为>=4096时,继续增加缓冲区长度对系统CPU时间几乎没有影响。
大多数文件系统为改善性能都采用某种预读(read ahead)技术。当检测到正进行顺序读取时,系统就试图读入比应用所要求的更多数据,并假想应用很快就会读这些数据。
影响时钟时间。
endtime:2019-05-03-15点38分
begintime:2019-05-04
3.10、文件共享
对文件系统相关部分的i-node(i节点)
对与文件系统无关部分的v-node(v节点)有了清晰的认识
内核定义了三种数据结构表示打开文件:
(1)进程在进程表中有一个记录项
(2)内核为所有打开文件维护一张文件表
(3)每个打开文件(或设备)都有一个v-node结构
3.11、原子操作
以前在学习数据库原理时学习过关系型数据库的ACID特性。
3.12、函数dup和函数dup2
复制一个现有的文件的描述符。
新描述符的执行时关闭(close-on-exec)标志总是由dup函数清除
3.13、函数sync
延迟写(delayed write)
在学习操作系统概念时有了解过。
endtime:2019-05-04-晚
begintime:2019-05-05
3.14、函数fcntl
改变已经打开的文件的属性
write函数只是将数据排入队列,而实际的写磁盘操作可能在以后的某个时刻进行
3.15、函数ioctl
I/O操作的杂物箱。不能使用本章其他函数表示的I/O操作通常都能用ioctl函数表示。
3.16、/dev/fd
其目录项是名为0,1,2等的文件。打开文件/dev/fd/n等效于复制描述符n(假定描述符n是打开的)
主要由shell使用,它允许使用路径名作为调用参数的程序,能用处理其他路径名的相同方式处理标准输入和输出。
3.17、小结
学习了和I/O相关的函数,对unix的文件系统有了较为清晰的认识,v-node、i-node。在linux种实现为通用i-node与i-node。