UNIX环境高级编程 第14章 高级I/O
这一章涉及很多概念和函数,包括:非阻塞I/O、记录锁、I/O复用、异步I/O、readv和writev函数以及内存映射。
非阻塞I/O
在Unix中,可以将系统调用分为两种,一种是“低速”系统调用,另一种是其他系统调用。前一种是可能导致主调进程永久阻塞的一种系统调用,比如管道,当另一端没有准备好时,一端对其读或写可能会永久阻塞。
一旦一个进程可能被永久阻塞这就表明程序有可能在某点彻底瘫痪,为了预防这样的情况发生,可以使用非阻塞I/O来避免。非阻塞I/O能够使得进程不会陷入永久阻塞的陷阱,当操作不能立即完成时,非阻塞I/O不会阻塞,其将会立即以出错的形式返回,表示操作继续执行就会阻塞。
有两种办法来将一个给定的文件描述符指定为非阻塞I/O:
(1) 在调用open( )函数获得文件描述符时,通过O_NONBLOCK标志来设置;
(2) 对于已存在的文件描述符,通过fcntl函数来设置O_NONBLOCK标志。
记录锁
Unix提供了用于支持单独文件读写保证的服务,那就是记录锁。实际上,记录锁并不能保证是在单独写一个文件,这和普通锁是一样的道理,你必须先持有锁,然后再修改文件,关键点是所有参与修改的进程先设置锁,但是有的进程压根就不上锁,因此也就谈不上保证单独读写。事实上,这里有个前提,那就是参与读写同一文件的进程必须都先加记录锁,然后才能通过记录锁提供保证。
对打开的文件加持记录锁是通过fcntl( )函数来完成的。其头文件及函数原型如下:
#include <fcntl.h> int fcntl(int fd, int cmd, ... /* struct flock *flockptr */ );
第一个参数为已打开文件的文件描述符。
第二个参数取F_GETLK(获取锁)、F_SETLK(设置/清除锁、排斥时返回)、F_SETLKW(设置锁、排斥时阻塞)。
第三个参数是一个具体的锁指针,可以用来设置锁的具体条件,比如从何处开始加锁、加锁多少字节,加持读锁、写锁还是解锁。
对于加锁起始位置可以在文件尾端开始,或者尾端后面开始,但不能在文件起始位置之前。
如果一把锁的 l_start 和 l_whence 都指向文件开始位置,并且 l_len 为0,那么表示该文件全部加锁,包括后续追加的内容也是在锁范围。
同一个进程新加锁会替换旧锁,比如进程768第一次在16-32字节处加了读锁,则第二次在16-32字节处加读锁或者写锁时,第一次的锁会被清除,第二个锁会生效,也即一个进程只能对同一个文件的同一区间加一把锁。同样的,我们不能使用F_GETLK来测试自己的进程是否持有锁,因为自己不会对自己排斥,一定会用自己的新锁去替换旧锁。
对于F_SETLKW需要注意的是防止导致死锁,因为F_SETLKW在锁不能满足的时候是阻塞而不是出错返回,两个进程各持有一把锁,同时两个进程都试图获得对方进程的锁时也会死锁。
记录锁的继承和释放
记录锁的继承和释放有三个规则:
1. 锁与进程和文件两个同时有关:也即,当一个进程结束后,锁自动释放,当一个文件关闭后,锁也自动释放,二者必须同时有效,锁才正常,只要任意一个无效,锁就释放。对于文件关闭来说,文件的重复打开或者文件描述符的复制等同于原文件描述符,具体如下:
fd1 = open(pathname, ...); //打开文件
read_lock(fd1, ...); //加锁
fd2 = dup(fd1); //复制文件描述符
close(fd2); //关闭复制的文件描述符 fd1 = open(pathname, ...); //打开文件
read_lock(fd1, ...); //加锁
fd2 = open(pathname, ...) //重复打开文件
close(fd2); //关闭复制的文件描述符
对于上面的重复打开文件或者文件描述符的复制操作,当关闭fd2之后,锁也失效,因为锁和文件有关,文件被关闭了一次,锁就失效了。
2. 对于fork产生的子进程不继承父进程的锁,因为锁与进程有关,子进程是一个新进程,与原锁无关。
3. 对于执行exec之后的新程序会继承原锁,因为锁与进程有关,新程序只是替换了进程内部数据,进程本身不变,锁依然有效,当然,如果进程打开的文件描述符设置了close_on_exec那么该文件描述符会在exec时被关闭,由于锁与文件也有关,因此文件描述符被关闭后,锁就会释放。
记录锁与进程有关这是没有任何疑问的,而记录锁与文件描述符无关与文件有关有点令人困惑,这是因为内核底层是通过v节点表项中的记录锁指针来记录记录锁,并不能知道具体是哪个文件描述符设置的,所有的文件描述符都指向同一个v节点表项,因此只要有一个文件描述符关闭了,内核就会去释放该v节点表项中的记录锁。
建议性锁和强制性锁
建议性锁和强制性锁不是真实存在的两种锁,它们是两种机制。如前所述,记录锁并不能保证是在单独写一个文件,所有参与修改同一文件的进程,如果其中存在未使用记录锁的进程,那么记录锁则等同虚设。为了能够使得记录锁发挥作用,应该在所有修改进程中进行记录锁的检查与设置,这就是建议性锁,也即所有的进程在读写文件之前先设置记录锁,所有这些进程合计称为合作进程。建议性锁只是一个规则,并不是真实存在的锁,如果不遵守该项机制,那么建议性锁并不能保证单独写一个文件。
和建议性锁相对的是强制性锁,强制性锁会使内核检查每个文件读写行为,即使某些进程并未使用记录锁。强制性锁也不是一种锁,它也是一种机制,该规则是在一个文件的设置组ID位打开,且组执行位关闭时触发生效。
UNIX环境高级编程 第14章 高级I/O的更多相关文章
- 《Unix 网络编程》14:高级 I/O 函数
高级 I/O 函数 ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ...
- ASP.NET MVC5 高级编程 第5章 表单和HTML辅助方法
参考资料<ASP.NET MVC5 高级编程>第5版 第5章 表单和HTML辅助方法 5.1 表单的使用 5.1.1 action 和 method 特性 默认情况下,表单发送的是 HTT ...
- ASP.NET MVC5 高级编程 第3章 视图
参考资料<ASP.NET MVC5 高级编程>第5版 第3章 视图 3.1 视图的作用 视图的职责是向用户提供界面. 不像基于文件的框架,ASP.NET Web Forms 和PHP ,视 ...
- ASP.NET MVC5 高级编程 第2章 控制器
参考资料<ASP.NET MVC5 高级编程>第5版 第2章 控制器 控制器:响应用户的HTTP 请求,并将处理的信息返回给浏览器. 2.1 ASP.NET MVC 简介 MVC 模式中的 ...
- UNIX环境高级编程 第8章 进程控制
本章是UNIX系统中进程控制原语,包括进程创建.执行新程序.进程终止,另外还会对进程的属性加以说明,包括进程ID.实际/有效用户ID. 进程标识 每个进程某一时刻在系统中都是独一无二的,它们之间是用一 ...
- UNIX环境高级编程 第7章 进程环境
本章涉及C/C++程序中main函数是如何被调用的.命令行参数如何传递给main函数.程序的内存空间布局.程序如何使用环境变量.程序如何终止退出. main函数 C程序或C++程序总是从main函数开 ...
- UNIX环境高级编程 第6章 系统数据文件和信息
UNIX系统的正常运作需要用到大量与系统有关的数据文件,例如系统用户账号.用户密码.用户组等文件.出于历史原因,这些数据文件都是ASCII文本文件,并且使用标准I/O库函数来读取. 口令文件 /etc ...
- UNIX环境高级编程 第13章 守护进程
守护进程daemon是一种生存周期很长的进程.它们通常在系统引导时启动,在系统关闭时终止.守护进程是没有终端的,它们一直在后台运行. 守护进程的特征 在Linux系统中,可以通过命令 ps -efj ...
- UNIX环境高级编程 第9章 进程关系
在第8章学习了进程的控制原语,通过各种进程原语可以对进程进行控制,包括新建进程.执行新程序.终止进程等.在使用fork( )产生新进程后,就出现了进程父子进程的概念,这是进程间的关系.本章更加详细地说 ...
随机推荐
- java中的==操作符和equals函数
基本规则 “==”操作符的使用需要分成两种情况 判值类型相等 这一点很好理解,两个值类型代表的数值相等,则“==”表达式返回true “==”可以用与不同值类型的比较,语言会自动进行类型转换 判引用类 ...
- ASP.NET MVC中在 @RenderBody() 或者 @Html.Partial()中需要使用引入外部js,css
今天想在后台封装一下bootstraptree这个插件,引入jquery.js bootstrap.js bootstrap.css bootstrap-tree.js后,我在页面查看脚本错误就连最简 ...
- js 添加事件兼容性
var tools = { //添加事件 addHandle: function (e, type, handle) { if (e.addEventListener) { e.addEventLis ...
- 【uoj#213】[UNR #1]争夺圣杯 单调栈+差分
题目描述 给出一个长度为 $n$ 的序列,对于 $1\sim n$ 的每一个数 $i$ ,求这个序列所有长度为 $i$ 的子区间的最大值之和,输出每一个 $i$ 的答案模 $998244353$ 后异 ...
- Python学习---日期时间
在Python里面日期时间的功能主要由几个模块提供:time,calendar,datetime,date等 time主要用到的功能函数: #!/usr/bin/python3 # coding:ut ...
- BZOJ3155:Preprefix sum——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3155 最朴素的想法是两棵树状数组,一个记录前缀和,一个记录前缀前缀和,但是第二个我们非常不好修改 ...
- 一步步创建第一个Docker App —— 3. 创建一个集群Swarm
原文:https://docs.docker.com/engine/getstarted-voting-app/create-swarm/ 初始化集群 Swarm 1. 使用 ssh 命令登录 man ...
- 解题:POI 2012 Well
题面 比较明显地能看出二分来,但是检查函数很难写.对于二分出的一个$mid$,我们要让它满足在$m$次操作内令序列中存在一个为零的位置,同时使得任意相邻的两项之差不超过$mid$ 第二项的检查比较好做 ...
- loj6436【PKUSC2018】神仙的游戏
$|S| \le 5 \times 10^5$ 题解 这题直接用通配符匹配的套路会错,因为重复部分的$?$可能同时被当做了$0$和$1$ 有长度为$i$的公共前缀后缀等价于有长度为$n-i$的循环节: ...
- cloudera manager安装hive注意事项,提示连不上数据库,没有user目录权限
1.提示连不上数据库,password:null 解决方法:拷贝数据库驱动到hive的lib目录,数据库要使用安装hive机器的本地数据库,远程的可能连不上 2.没有/user目录权限 解决方法:因为 ...