【APUE】第3章 文件I/O (3) 文件共享、原子操作、函数dup/dup2、函数sync/fsync/fdatasync、函数fcntl、函数ioct1、目录/dev/fd 使用说明
1、文件共享
UNIX系统支持在不同的进程间共享打开文件。为了说明这种共享,以下介绍内核用于所有I/O的数据结构。
内核使用3种数据结构表示打开文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。
(1)每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述符表,可将其视为一个矢量每个描述符占用一项。与每个文件描述符相关联的是:
- 文件描述符标志;
- 指向一个文件表项的指针。
(2)内核为所有打开文件维护一张文件表。每个文件表项包含:
- 文件状态标志(读、写、添加、同步和非阻塞等);
- 当前文件偏移量;
- 指向该文件v节点表项的指针。
(3)每个打开文件(或设备)都有一个v节点(v-node)结构。v节点包含了文件类型和对此文件进行各种操作函数的指针。对于大多数文件,v节点还包含了该文件的i节点(i-node,索引节点)。这些信息都是在打开文件时从磁盘上读入内存的,所以文件的所有相关信息都是随时可用的。例如i节点包含了文件的所有者、文件长度、指向文件实际数据块在磁盘上的位置等。(注意在linux中是没有v节点结构的但是存在i节点结构)
如图3-7 显示了一个进程对应的3张表之间的关系。该进程有两个不同的打开文件;一个文件从标准输入打开(文件描述符为0),另一个从标准输出打开(文件描述符为1)。
如果两个独立的进行各自打开了同一个文件,则有如图3-8所示的关系。
我们假定第一个进程在文件描述符3上打开该文件,而另一个进程在文件描述符4上打开该文件。打开该文件的每个进程都获得各自的一个文件表项,但是对于一个给定的文件只有一个v节点表项。之所以每个进程都获得自己的文件表项,是因为这可以是每个进程都有它自己的对该文件的偏移量。
给出了这些数据结构之后,现在对前面所述的操作做进一步说明:
(1)在完成每个write后,文件表项中的当前文件偏移量即增加所写入的字节数。如果这导致当前文件偏移量超出当前文件长度,则将i节点表项中的当前文件长度设置为当前文件偏移量(也就是该文件加长了)。
(2)如果使用O_APPEND标志打开一个文件,则相应标志也被设置到文件表项的文件状态标志中。每次对这种具有追加标志的文件进行写操作时,文件表项中的当前文件偏移量首先将会被设置为i节点表项中的当前文件长度。
(3)若一个用lseek定位到文件当前的末尾,则文件表项中的当前偏移量被设置为i节点表项中的当前文件长度。
(4)lseek函数只修改文件表项中的当前文件偏移量,不进行任何I/O操作。
可能有多个文件描述符指向同一文件表项,例如fork后,此时父子进程各自的每一个打开文件描述符共享一个文件表项。
上述介绍的一切对于多个进程读取一个文件都能正确工作。每个进程都有自己的文件表项,其中也有它自己的当前文件偏移量。但是当多个进程写同一个文件时,则可能产生预想不到的结果。为了说明如何避免这种情况,需要先理解原子操作的概念。
2、原子操作
考虑一个进程,它将数据追加到一个文件尾端。
假定有两个独立的进程A和B都对同一个文件进行追加写操作。每个进程都已经打开了该文件,但是未使用O_APPEND标志。此时各数据结构之间的关系如图3-8所示,每个进程都有它自己的文件表项,但是共享一个v节点表项。假定进程A调用lseek,它将进程A的该文件当前偏移量设置为1500字节(当前文件尾端处)。然后内核切换进程,进程B运行。进程B执行lseek,也将其对该文件的当前偏移量设置为1500字节(当前文件尾端处)。然后B调用write,它将B文件当前文件偏移量增加至1600.因为该文件长度已经增加了,所以内核将v节点中的当前文件长度更新为1600。然后内核又进行进程切换,使进程A恢复运行。当A调用write时,就从其当前文件偏移量(1500)处开始将数据写入文件。这样也就覆盖了之前进程B写入该文件的数据。
问题出现在逻辑操作“先定位到文件末尾,然后写”,它使用了两个分开的函数调用。解决的方法是使这两个操作对于其他进程而言称为一个原子操作。任何要求多于一个函数的调用操作都不是原子操作,因为在这两个函数调用之间,内核有可能临时挂起进程。
UNIX系统为这样的操作提供了一种原子操作,即在打开文件时设置O_APPEND标志。这样做使得内核每次在写之前,都将进程的当前偏移量设置到该文件的尾端,于是每次写之前就不需要调用lseek。(open函数中的O_APPEND选项实现了lseek函数的部分功能,功能实现了定位和写两个操作成为原子的)
3、函数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_CLOEXEC文件描述符标志就被清除。这样fd2在进程调用exec时是打开状态。
这些函数返回的新文件描述符与参数fd共享一个文件表项,如图3-9所示:
在此图中,我们假定进程启动时执行了:newfd = dup(1);
当此函数开始执行时,假定下一个可用的文件描述符是3。因为两个文件描述符指向同意以文件表项,所以他们共享同一个文件状态标志(读、写、追加等)以及同一当前文件偏移量。
每个文件描述符都有它自己的一套文件描述符标志。复制一个文件描述符的另一种方法是使用fcntl函数。实际上调用dup(fd)等效于fcntl(fd, F_DUPFD, 0)。而调用dup2(fd, fd2)等效于close(fd2); fcntl(fd, F_DUPFD, fd2);而后一种情况下,dup2并不完全等同于close加上fctl。它们之间的区别具体如下:
【APUE】第3章 文件I/O (3) 文件共享、原子操作、函数dup/dup2、函数sync/fsync/fdatasync、函数fcntl、函数ioct1、目录/dev/fd 使用说明的更多相关文章
- 第3章 文件I/O(4)_dup、dup2、fcntl和ioctl函数
5. 其它I/O系统调用 (1)dup和dup2函数 头文件 #include<unistd.h> 函数 int dup(int oldfd); int dup2(int oldfd, i ...
- APUE第4章 文件和目录
4.2 文件函数 #include <sys/stat.h> int stat(const char *restrict pathname, struct stat *restrict b ...
- apue 第4章 文件和目录
获取文件属性 #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(c ...
- 第七篇:使用 fcntl 函数 获取,设置文件的状态标志
前言 当打开一个文件的时候,我们需要指定打开文件的模式( 只读,只写等 ).那么在程序中如何获取,修改这个文件的状态标志呢? 本文将告诉你如何用 fcntl函数 获取指定文件的状态标志. 解决思路 1 ...
- UNIX环境编程学习笔记(5)——文件I/O之fcntl函数访问已打开文件的性质
lienhua342014-08-29 fcntl 函数可以改变已打开的文件的性质. #include <fcntl.h> int fcntl(int filedes, int cmd, ...
- apue学习笔记(第三章 文件I/O)
本章开始讨论UNIX系统,先说明可用的文件I/O函数---打开文件.读写文件等 UNIX系统中的大多数文件I/O只需用到5个函数:open.read.write.lseek以及close open函数 ...
- apue学习笔记(第四章 文件和目录)
本章将描述文件系统的其他特性和文件的性质. 函数stat.fstat.fstatat和lstat #include <sys/stat.h> int stat(const char *re ...
- 《UNIX环境高级编程》(APUE) 笔记第四章 - 文件和目录
4 - 文件和目录 1. 函数 stat.fstat.fstatat 和 lstat #inlcude <sys/stat.h> int stat(const char *restrict ...
- 《UNIX环境高级编程》(APUE) 笔记第三章 - 文件I/O
3 - 文件I/O Github 地址 1. 文件描述符 对于内核而言,所有打开的文件都通过 文件描述符 (file descriptor) 引用.当打开一个现有文件或创建一个新文件时,内核向进程返回 ...
随机推荐
- python之random库的使用以及程序的异常处理
1.random库的使用: random库是使用随机数的Python标准库从概率论角度来说,随机数是随机产生的数据(比如抛硬币),但时计算机是不可能产生随机值,真正的随机数也是在特定条件下产生的确定值 ...
- win10下 安装迅雷精简版
下载链接:https://files-cdn.cnblogs.com/files/del88/ThunderMini_1.5.3.288.zip 他妈的 今天安装迅雷精简版 在win10上 竟然报错, ...
- python中括号知识点
Python语言中括号分为几个类型,常见的三个圆括号是圆括号().中间圆括号[]和大括号.它的函数也不同,代表不同的Python基本内置数据类型. python括号 python()中的括号:表示tu ...
- WinPE基础知识之重定位表
typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; // (重要)需要重定位的位置,这是一个RVA DWORD SizeOfBl ...
- 如何上传HTML5应用到SAP云平台的Cloud Foundry环境下
先使用WebIDE创建一个HTML5应用.New->Project from Template: 从可选模板里选择SAPUI5 Application: 创建一个HTML5 Module,取名为 ...
- 2.2 使用 JAXP 对XML文档进行SAX解析
使用JAXP 对 XML文档进行 SAX解析: public class Demo1 { /** * 使用JAXP对XML文档进行SAX解析 * @throws Exception * @throws ...
- STM32写选项字节(option bytes)的正确姿势
STM32 的 Flash information block 部分,包含有特殊的选项字节,可以用于系统配置等信息, 其中还有两个有效字节(实际四个字节,两个是校验字节)的用户自定义数据字节. 在尝试 ...
- (二十四)Ubuntu16.04配置ADB调试环境
一.安装adb 1.可以通过 apt-get install android-tools-adb 来安装adb sudo add-apt-repository ppa:nilarimogard/web ...
- Linux下安装opencv(踩坑记录帖)
1.首先安装依赖项:sudo apt install build-essential sudo apt install build-essentialsudo apt install cmake gi ...
- [SDOI2008]山贼集团
题目描述 某山贼集团在绿荫村拥有强大的势力,整个绿荫村由\(N\)个连通的小村落组成,并且保证对于每两个小村落有且仅有一条简单路径相连. 小村落用阿拉伯数字编号为\(1,2,3,4, \dots ,n ...