APUE读书笔记之二——文件I/O

第三章 文件I/O

1,文件描述符

   对于内核而言,所有打开的文件都用文件描述符引用。文件描述符是一个非负整数,当打开或者创建一个文件时,内核向进程返回一个文件描述符,支持POSIX的应用程序中,0,1,2被替换成标准输入STDIN_FILENO,标准输出STDOUT_FILENO,标准错误STDERR_FILENO,这些常量被定义在<unistd.h>Linux2.4对于每个进程的文件描述符数量的硬限制是10485761M)。

2Open()函数

int open(const char* pathname, int oflag, ...  /*  mode_t mode  */);

   若成功则返回文件描述符,失败则返回-1

   参数说明:1pathname是路径名;2aflag说明此函数的多个选项(O_RDONLY只读打开,O_WRONLY只写打开,O_RDWR读写打开,多数实现分别为0,1,2),选项可进行或运算。这三个常量必须选择至少一个。另外的选项还有O_APPEND每次写时追加到文件末尾;O_CREAT若文件不存在,则创建它;O_EXCL测试文件是否存在,如不存在就创建它,测试与创建为原子操作,不能与O_CREAT同时使用;O_TRUNC如果文件存在,且为只写或读写打开,则将其长度截短为0O_NOCTTY如果pathname指的是终端设备,则不将该设备分配作为此进程的控制端;O_NONBLOCK如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则将此选项为文件的本次打开操作和后续的I/O操作设置为非阻塞模式;O_DSYNC使每次write等待物理I/O操作完成,如果写操作不影响读取刚写的数据,则不等待文件属性被更新;O_RSYNC使每一个以文件描述符作为参数的read操作等待,直至任何对文件同一部分进行的未决写操作都完成;O_SYNC使每次write都等到物理I/O操作完成,包括write操作引起的文件属性更新所需的I/OO_DSYNCO_SYNC区别在于一个不需要等待文件属性(如写文件引起的文件大小更新)完成,另一个则需要。

   文件名如果字符数超过了限制,系统会自动将其截短,此时如果一个文件名其长度刚好是系统的限制,就无法判断该文件名是否被截短过。常量_POSIX_NO_TRUNC决定了是要截短过长的文件名或路径名,还是返回一个出错。

3creat()函数

int creat(const char* pathname, mode_t mode);

   成功则返回文件描述符,失败则返回-1.此函数等效于int open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode)Creat的一个不足之处是它只以只写方式打开创建文件。

4close()函数

int close(int filedes);<span style="white-space:pre">	</span>

   若成功则返回0,失败则返回-1。关闭一个文件是还会释放该进程加在该文件上的所有记录锁。当一个进程终止时,内核会自动关闭它打开的所有文件。

5lseek()函数

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

   若成功则返回新的文件偏移量,失败则返回-1.

Offset是文件开始处计算的字节数,系统默认情况下,若非以O_APPEND方式打开文件,偏移量offset0,参数whence取值有SEEK_SET将该文件的偏移量设置为距文件开始处offset个字节;SEEK_CUR将该文件的偏移量设置为其当前值加offsetoffset可以为正为负;SEEK_END将该文件的偏移量设置为文件长度加offsetoffset可以为正为负。

   可以利用SEEK_CUR,偏移量+0方式返回的偏移量确定当前偏移量是多少。也可以用来确定涉及的文件是否可以设置偏移量,如果文件描述符引用的是一个管道、FIFO或网络套接字,则lseek返回-1,并将errno设置为ESPIPE。因为返回的偏移量可能小于0,所以不要测试是否小于0,而是测试是否等于-1.

   偏移量off_t是带符号数据类型,所以文件最大长度会减少一半,如果off_t32位整型,则文件最大长度是(2^31)-1字节,即2GB,如果支持64位偏移量,但能否创建大于2GB的文件需要看底层文件系统的类型。

   文件空洞。偏移量可以大于文件大小,这会在文件形成一个空洞,空洞处的字节都为0,空洞不要求占用存储区,超出空洞新写的数据需要分配磁盘块,原文件末尾与新写的数据块之间的境地不需要分配磁盘块,这也需要看具体系统的实现。

6read()函数

ssize_t read(int filedes, void *buf, size_t nbytes);

   若成功返回读到的字节数,若已到文件末尾则返回0,若失败则返回-1.

   有多种情况可使读到的字节数少于要求读的字节数。1,读普通文件时,要求读100字节,但读到30字节就已经到了文件末尾,返回30,下一次再调用read时返回02,当从终端设备读时,通常一次最多读一行;3,当从网络读时,网络中的缓冲机构可能造成返回值小于所要求的字节数;4,当从管道或FIFO读时,若管道的字节数少于要求的字节数;5,当从面向记录的设备(磁带)读时,一次最多返回一个记录;6,当某一信号造成中断,而此时已经读了部分数据量时。

7write()函数

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

   若成功返回已写的字节数,失败则返回-1;若以O_APPEND方式打开文件,成功写后偏移量等于原偏移量加上写的字节数。

8I/O的效率

   对于UNIX内核而言,文本文件和二进制文件并无区别。

   在Linux ext2文件系统中,buff块大小在4KB时效率最高;文件系统会有预读技术,提前读取更多字节缓存在内存中以便下次读取使用。

9,文件共享

UNIX支持不同进程间共享打开的文件。内核使用三种数据结构表示打开的文件,分别是进程表项,文件表,文件V节点表。其中进程表项是每个进程都有一个记录项,记录项中包含一张打开文件描述符表,每项是一个打开的文件描述符标志和一个文件表项的指针;内核为所有打开的文件维持一张文件表,每个文件表项有文件状态标识(读、写、添写、同步和非阻塞),当前文件偏移量,文件V节点指针,每个打开的文件都有一个V节点结构,V节点包含了文件类型和对此文件进行各种操作的函数的指针,该文件的i节点(索引节点)和当前文件长度;

   可能有多个文件描述符项指向同一文件表项,比如使用dup()函数,比如fork()函数,都可以使多个文件描述符指向同一文件表项。在此时,多个进程打开同一文件进行读取就没有问题,但是如果多进程写文件就会出现问题。

10,原子操作

    在多进程添写同一个文件时,如果每个进程先使用lseek(同一偏移量),后使用write进行写,则会有冲突,后一进程写的内容会覆盖前一进程刚写的内容。解决方案是取消lseekwrite的分开调用,采用两者结合的原子操作,有pread()原子读和pwrite()原子写调用。在使用原子调用时,无法中断其定位和读写操作,不更新文件指针。

    当多进程创建一个文件时,即同时使用open()函数的O_CREATO_EXCL选项,某个进程创建了一个文件,并且写入了数据,然后切换另一个进程,此进程又创建此文件,将刚写的数据擦除。

11dup()和dup2()函数

int dup(int filedes);
int dup2(int filedes, int filedes2);

    两个函数都可以用来复制一个现存的文件描述符。区别是 dup()返回的是当前可用的文件描述符中的最小数值,dup2()可以指定新描述符的数值,如果filedes2已经打开,则先关闭filedes2,如果filedes等于filedes2,则返回filedes2Fcntl()函数可以复制一个文件描述符,等效于这两个函数,但dup2()是一个原子操作,fcntl()和close()之间可能被插入执行其他函数。

12sync(),fsync()和fdatasync()函数

   传统的UNIX实现在内核中设有缓冲区高速缓存或页面高速缓存,大多数磁盘I/O通过缓冲进行。当将数据写入文件时,内核通常先将该数据复制到其中一个缓冲区中,如果该缓冲区没有写满,则不将其排入输出队列,而是等待其写满或者党内和需要重用该缓冲区以便存放其他磁盘块时,再将数据排入输出队列,然后待其排到队首位置时,才将进行实际的I/O操作,这种输出方式称为延迟写(delayed write)。延迟写减少了磁盘读写次数,但是却降低了文件内容的更新速度,使得想写到磁盘的数据有一段时间没有写到磁盘上,当系统发生故障时,内存中还没写到磁盘的数据可能会丢失。为了保证磁盘上文件系统和缓冲区高速缓存的内容一致性,UNIX系统提供了sync,fsync,fdatasync系统调用:

void sync(void);
int fsync(int filedes);
int fdatasync(int filedes);

        Sync函数只是将修改过的块缓冲区排入写队列,然后就返回,并不等写磁盘完成。update的系统守护进程会周期性地调用sync函数,保证了定期冲洗内核的缓冲区,命令sync1)页调用sync函数;fsync函数只对有文件描述符filedes指定的单一文件起作用,并且等待写操作结束,然后再返回,fsync函数可适用于数据库这样的软件,同步写磁盘,保证内存与磁盘的一致性;fdatasync类似于fsync。但它只影响文件的数据部分,fsync还有同步更新文件的属性。

13fcntl()函数

int fcntl(int filedes, int cmd, ...  /*  int arg  */);

       fcntl可以改变已打开的文件的性质。该函数有5种功能,分别是1,复制一个现有的描述符(cmd=F_DUPFD);2,获得/设置文件描述符标记(cmd=F_GETFDF_SETFD);3,获得/设置文件状态标志(cmd=F_GETFLF_SETFL);4,获得/设置异步I/O所有权(cmd=F_GETOWNF_SETOWN);5,获得/设置记录锁(cmd=F_GETLKF_SETLKF_SETLKW)。

14ioctl()函数

int ioctl(int filedes, int request, ...);

         Ioctl函数I/O操作的杂物箱,不能使用其他函数表示的I/O操作通常都能用ioctl表示。终端I/Oioctl的最大使用方面。

15/dev/fd

   该目录的目录项是名为01,2等的文件,打开的文件(文件描述符)等效于一个目录项。可以使用open“/dev/fd/0”, mode)打开那个文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值