这一章涉及很多概念和函数,包括:非阻塞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的更多相关文章

  1. 《Unix 网络编程》14:高级 I/O 函数

    高级 I/O 函数 ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ...

  2. ASP.NET MVC5 高级编程 第5章 表单和HTML辅助方法

    参考资料<ASP.NET MVC5 高级编程>第5版 第5章 表单和HTML辅助方法 5.1 表单的使用 5.1.1 action 和 method 特性 默认情况下,表单发送的是 HTT ...

  3. ASP.NET MVC5 高级编程 第3章 视图

    参考资料<ASP.NET MVC5 高级编程>第5版 第3章 视图 3.1 视图的作用 视图的职责是向用户提供界面. 不像基于文件的框架,ASP.NET Web Forms 和PHP ,视 ...

  4. ASP.NET MVC5 高级编程 第2章 控制器

    参考资料<ASP.NET MVC5 高级编程>第5版 第2章 控制器 控制器:响应用户的HTTP 请求,并将处理的信息返回给浏览器. 2.1 ASP.NET MVC 简介 MVC 模式中的 ...

  5. UNIX环境高级编程 第8章 进程控制

    本章是UNIX系统中进程控制原语,包括进程创建.执行新程序.进程终止,另外还会对进程的属性加以说明,包括进程ID.实际/有效用户ID. 进程标识 每个进程某一时刻在系统中都是独一无二的,它们之间是用一 ...

  6. UNIX环境高级编程 第7章 进程环境

    本章涉及C/C++程序中main函数是如何被调用的.命令行参数如何传递给main函数.程序的内存空间布局.程序如何使用环境变量.程序如何终止退出. main函数 C程序或C++程序总是从main函数开 ...

  7. UNIX环境高级编程 第6章 系统数据文件和信息

    UNIX系统的正常运作需要用到大量与系统有关的数据文件,例如系统用户账号.用户密码.用户组等文件.出于历史原因,这些数据文件都是ASCII文本文件,并且使用标准I/O库函数来读取. 口令文件 /etc ...

  8. UNIX环境高级编程 第13章 守护进程

    守护进程daemon是一种生存周期很长的进程.它们通常在系统引导时启动,在系统关闭时终止.守护进程是没有终端的,它们一直在后台运行. 守护进程的特征 在Linux系统中,可以通过命令 ps -efj ...

  9. UNIX环境高级编程 第9章 进程关系

    在第8章学习了进程的控制原语,通过各种进程原语可以对进程进行控制,包括新建进程.执行新程序.终止进程等.在使用fork( )产生新进程后,就出现了进程父子进程的概念,这是进程间的关系.本章更加详细地说 ...

随机推荐

  1. Linux中Apache+Tomcat+JK实现负载均衡和群集的完整过程

    人原创,一个字一个字地码的,尊重版权,转载请注明出处! http://blog.csdn.net/chaijunkun/article/details/6987443 最近在开发的项目需要承受很高的并 ...

  2. UVA 12633 Super Rooks on Chessboard(FFT)

    题意: 给你一个R*C的棋盘,棋盘上的棋子会攻击,一个棋子会覆盖它所在的行,它所在的列,和它所在的从左上到右下的对角线,那么问这个棋盘上没有被覆盖的棋盘格子数.数据范围R,C,N<=50000 ...

  3. C++模式学习------适配器模式

    适配器模式: 适配器模式属于结构型的设计模式,是将一个类的接口转换成使用方希望的另外一个接口,这样使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适配器模式有两种: 1.类的适配器:继承不 ...

  4. 【刷题】BZOJ 4530 [Bjoi2014]大融合

    Description 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它 ...

  5. Atcoder Yahoo Programming Contest 2019 简要题解

    A-C 直接放代码吧. A int n,k; int main() { n=read();k=read(); puts(k<=(n+1)/2?"YES":"NO&q ...

  6. BZOJ5058 期望逆序对 【矩乘 + 组合数学 + 树状数组】

    题目链接 BZOJ5058 题解 可以发现任意两个位置\(A,B\)最终位置关系的概率是相等的 如果数列是这样: CCCCACCCCBCCCC 那么最终有\(7\)种位置关系 \((A,B)\) \( ...

  7. BZOJ3834 [Poi2014]Solar Panels 【数论】

    题目链接 BZOJ3834 题解 容易想到对于\(gcd(x,y) = D\),\(d\)的倍数一定存在于两个区间中 换言之 \[\lfloor \frac{a - 1}{D} \rfloor < ...

  8. Nginx web服务优化 (一)

    1.Nginx基本安全优化 a.更改配置文件参数隐藏版本 编辑nginx.conf配置文件增加参数,实现隐藏Nginx版本号的方式如下.在nginx配置文件nginx.conf中的http标签段内加入 ...

  9. bzoj1027【JSOI2007】合金

    题目描述 某公司加工一种由铁.铝.锡组成的合金.他们的工作很简单.首先进口一些铁铝锡合金原材料,不同种类的原材料中铁铝锡的比重不同.然后,将每种原材料取出一定量,经过融解.混合,得到新的合金.新的合金 ...

  10. NSLineBreakByWordWrapping和NSLineBreakByCharWrapping

    在iOS开发过程中,在文本的lineBreakMode中有以下几个功能: NSLineBreakByWordWrapping = 0 //以空格为界,保留整个单词. NSLineBreakByChar ...