文件io(二)--unix环境高级编程笔记
在linux中,打开的文件组织结构如下:
与打开的文件相关的有三个数据结构,就是上图中的三部分。
在linux中,有一个进程表,每一个进程在进程表中有一个表项。每一个进程表项中都维护着一张打开文件的描述符表,每一个文件描述符占用了一个表项。只要文件被打开,就都会在这张表中存在一个文件描述符,而不管该描述符是否是该进程打开的。与文件描述符相关联的是文件描述符标志和文件表项指针。文件表项指针指向文件表项,什么是文件表项呢?内核为每一个打开的文件都维护了一个文件表项。这个文件表项记录了以下信息:文件打开的状态(只读,只写或者可读可写),当前文件的偏移量(用来记录文件的下次要读或者写文件的位置)以及文件的v节点指针。内核为每一个打开的文件还维护了一个v节点,这个节点包括了文件的类型以及各种操作文件的指针。另外它还包括i节点,i节点包括文件的所有者以及文件的长度等信息,这些信息是在打开文件时从磁盘上读进来的,因此可以看到在程序中获取打开文件的相关信息是十分快速的。
每次向一个文件中写入数据后,文件表项的文件位移会增加相应的字节数。当文件的位移超过i节点中当前文件的长度时,当前文件的长度会设置为当前文件的位移。如果文件以O_APPEND的方式打开时,每次向文件中写入数据前,都会将当前文件指针设置为i节点中的当前文件长度。
如果有两个进程打开了同一文件,则内核为这两个进程分别维护一个文件表项,这样做的理由是两个进程可以有独立的文件位移,这种情况,图示如下:
虽然内核为每一个进程都维护了一个文件表项,但是内核对每一个文件只维护了一个v节点。在这种情况下,多个进程同时对一个文件进行读操作是完全没有问题的,但是如果多个进程同时对一个文件进行写操作,就有可能造成最终的文件并不是我们所想要的结果。这个问题就引出了原子操作的概念。
原子操作是由一系列操作构成的一个集合,这个集合中的操作,要么全部执行,要么全不执行,绝不可能只执行了这个指令集合的一个真子集。考虑这样一个进程,它每次将数据添加到文件的末尾,在早期的linux中没有O_APPEND操作,所以程序会写成如下的形式:
if( lseek(fd,0L,SEEK_END)<0 )
{printf("lseek error!\n");return -1;
} if( write(fd,buff,100)!=100 )
{printf("write file error!\n");return -2;
}
当有两个进程需要同时对同一个文件进行操作时,我们假设为进程A和进程B。当进程A执行lseek后将文件指针移动到1600字节处(文件末尾),这时候切换到进程B执行,进程B执行lseek将它的文件表项的位移设置到1600个字节处,然后执行写操作,此时,i节点中的当前文件长度为1700。这时侯,切换到进程A继续执行,对应进程A的文件表项中的文件偏移仍然为1500,所以执行写操作后会将B进程写入文件的数据覆盖,造成我们不想要的结果。使用O_APPEND则不会出现这种情况,因为每次执行写操作之前,都会将文件偏移量移动到当前文件的末尾,可以看出移动文件偏移量到文件末尾和写文件操作是一个原子操作。
dup函数和dup2函数用来复制一个文件描述符,它们的函数原型是:
int dup(int filedes);
int dup2(int filedes,int filedes2);
如果函数执行成功,则返回新的文件描述符,否则返回-1。dup函数的参数是所要复制的文件描述符,返回系统中尚未使用的最小的文件描述符。dup2的作用是将第一个参数指定的文件描述符复制到第二个参数制定的文件描述符上,如果第二个参数已经使用,则先将其关闭。由dup函数复制后得到的文件描述符与原文件描述符指向同一个文件表项,如下图:
fcntl函数用来改变打开文件的性质,它的函数原型是:
int fcntl(int filedes,int cmd,...);
如果函数执行成功返回值依赖于cmd,如果函数执行失败则返回-1。cmd的取值有以下几种情况:
F_DUPFD:复制文件描述符,相当于dup和dup2的功能
F_GETFD / F_SETFD:获取/设置文件描述符标志
F_GETFL / F_SETFD:获取/设置文件状态标志
F_GETOWN / FSETOWN:获取设置异步IO所有权
F_GETTLK / F_SETTLK / F_SETTLKW:获得设置记录锁
其中,最常使用的是前面三个选项。
ioctl函数是io操作的杂物箱,可以进行各种乱七八糟的io操作。
系统中存在/dev/fd/n,这个目录,n的值为0,1,2等,打开文件/dev/fd/n等于复制文件描述符n。
文件io(二)--unix环境高级编程笔记相关推荐
- UNIX环境高级编程笔记之文件I/O
一.总结 在写之前,先唠几句,<UNIX环境高级编程>,简称APUE,这本书简直是本神书,像我这种小白,基本上每看完一章都是"哇"这种很吃惊的表情.其实大概三年前,那会 ...
- Unix环境高级编程 笔记
Unix环境高级编程(第二版)学习笔记 这是一次较长时间的整理,然而跳跃了一些章节和很多知识点,仍然是很不完善很不全面的. 前言 操作系统某些问题 严格意义上,可将操作系统定义为一种软件,它控制计算机 ...
- UNIX环境高级编程笔记
1.setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, len); SO_REUSEADDR套接口选项允许为以下四个不同的目的提供服务: ...
- 标准IO库--unix环境高级编程读书笔记
标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...
- UNIX环境高级编程笔记(2)- STDIN_FILENO、STDOUT_FILENO和stdin、stdout的区别
目录 前言 一.STDIN_FILENO.STDOUT_FILENO介绍 二.stdin.stdout介绍 三.代码例程 1.文件描述符的使用 2.流的使用 3.代码标记 总结 前言 本章主要通过UN ...
- Unix环境高级编程笔记:12、高级IO
2019独角兽企业重金招聘Python工程师标准>>> 1.非阻塞IO 系统调用分成"低速"系统调用和其他系统调用.低速系统调用是可能会使进程永远阻塞的一类系统调 ...
- linux系统数据文件和信息--unix环境高级编程读书笔记
linux系统中的数据文件有很多,在这一章里介绍的主要内容是和系统有关的一系列文件,包括passwd,shadow,group,utmp,wtmp以及一些系统的相关信息和时间的相关操作. 1.pass ...
- UNIX环境高级编程笔记(14)- 函数sigsuspend 实现父进程子进程同步
前言 本章主要介绍sigsuspend函数以及实现父进程子进程通过信号的同步. 一.函数sigsuspend #include<signal.h> int sigsuspend(const ...
- UNIX环境高级编程笔记之进程控制
本章重点介绍了进程控制的几个函数:fork.exec族._exit.wait和waitpid等,主要需要掌握的是父进程和子进程之间的运行机制,怎么处理进程的正常和异常终止.以及怎么让进程执行不同的程序 ...
最新文章
- 关于supervisor 挂载woker和worker linke worker 的同样的结束等待
- thinkphp-join
- MYSQL实现分组排序并取组内第一条数据
- 【HDU6662】Acesrc and Travel【树形DP】
- 解决MySQL事务未提交导致死锁报错 避免死锁的方法
- Zuul:Pre和Post过滤器(下)
- 曹大带我学 Go(8)—— 一个打点引发的事故
- 关于editor网页编辑器ueditor.config.js 配置图片上传
- .NET MVC异步调用中的Session问题
- 数据结构与算法之七归并排序
- Python自然语言处理学习笔记(60):7.1 信息抽取
- Problem D: 平面划分
- Easyrecovery激活码生成器下载地址?
- 北京周边骑行路线总结
- 解决问题:UnicodeDecodeError utf-8 codec cant decode byte 0xb5 in position 116:
- 《原则》读后感(一)
- Leecode 刷题记录 1217 玩筹码
- packet tracer 用命令配置路由器
- anaconda创建虚拟环境并安装tensorflow
- bootdo监控后台上传文件定期失效