这一章涉及很多概念和函数,包括:非阻塞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. STM32标准外设库、 HAL库、LL库

    工作以来一直使用ST的STM32系列芯片,ST为开发者提供了非常方便的开发库.到目前为止,有标准外设库(STD库).HAL库.LL库 三种.前两者都是常用的库,后面的LL库是ST最近才添加,目前支持的 ...

  2. python的N个小功能(图片预处理:打开图片,滤波器,增强,灰度图转换,去噪,二值化,切割,保存)

    ############################################################################################# ###### ...

  3. BZOJ5125 小Q的书架(决策单调性+动态规划+分治+树状数组)

    设f[i][j]为前i个划成j段的最小代价,枚举上个划分点转移.容易想到这个dp有决策单调性,感性证明一下比较显然.如果用单调栈维护决策就不太能快速的求出逆序对个数了,改为使用分治,移动端点时树状数组 ...

  4. P3293 [SCOI2016]美味

    题目描述 一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 ai(1<=i<=n).有 m 位顾客,第 i 位顾客的期望值为 bi,而他的偏好值为 xi .因此,第 ...

  5. JAVA ACM 基础

    java ACM Java做ACM-ICPC的特点: (1) 在一般比赛中,Java程序会有额外的时间和空间,而实际上经过实验,在执行计算密集任务的时候Java并不比C/C++慢多少,只是IO操作较慢 ...

  6. BZOJ 3282: Tree

    3282: Tree Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1714  Solved: 765[Submit][Status][Discuss ...

  7. 洛谷 P1878 舞蹈课 解题报告

    P1878 舞蹈课 题目描述 有\(n\)个人参加一个舞蹈课.每个人的舞蹈技术由整数来决定.在舞蹈课的开始,他们从左到右站成一排.当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始 ...

  8. 使用SUID二进制文件进行Linux权限升级技巧

      0x00 基础知识 众所周知,在Linux中一切都以文件存在,包括具有允许或限制三个执行操作(即读/写/执行)权限的目录和设备.因此,当给任何文件设置权限时,应该需要了解允许的Linux用户或限制 ...

  9. Android源码批量下载及导入到Eclipse

    1.去http://code.google.com/p/msysgit/downloads/list  下载Git,进行安装 2.生成批量下载脚本文件  下载批量工具CreatAutoGetSh(工具 ...

  10. 布隆过滤器 Bloom Filter

    使用普通集合来判断一个元素是否已存在于集合中,需要占用比较大的空间.而使用Bloom Filter 可有效节省空间. Bloom Filter 以较少的内存占用及较小的误判率达到判断元素是否存已经加入 ...