这一章涉及很多概念和函数,包括:非阻塞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. Idea(二) 解决IDEA卡顿问题及相关基本配置

    一.IDEA太卡顿,设置使用IDEA的内存 在IDEA的安装目录下的bin目录下: 打开设置: 将idea.exe.vmoptions文件内由-server-Xms128m-Xmx512m-XX:Ma ...

  2. ionic2如何升级到最新版本、配置开发环境

         好久没写东西了,去年用了angular2的RC版本和ionic2写了一个项目,因为开发周期和有些版本不稳定,所以一直没有升级,ng2新版本引用Aot打包,听说优化还不错,现在尝试升级ioni ...

  3. Day 4 学习笔记 各种图论

    Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...

  4. [NOI2014]魔法森林 LCT

    题面 [NOI2014]魔法森林 题解 一条路径的代价为路径上的\(max(a[i]) + max(b[i])\),因为一条边同时有$a[i], b[i]$2种权值,直接处理不好同时兼顾到,所以我们考 ...

  5. BZOJ 3110 K大数查询 | 整体二分

    BZOJ 3110 K大数查询 题面 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个 ...

  6. BZOJ2159 Crash 的文明世界 【第二类斯特林数 + 树形dp】

    题目链接 BZOJ2159 题解 显然不能直接做点分之类的,观察式子中存在式子\(n^k\) 可以考虑到 \[n^k = \sum\limits_{i = 0} \begin{Bmatrix} k \ ...

  7. android studio 卡慢的问题(android studio 3.0)

    http://www.jianshu.com/p/0228b7d017bb 想体验一下android studio 3.0的canary版,主要是学习Kotlin.创建项目后,下载相关文件一直不成功. ...

  8. vmware中centos7设置静态IP

    1.vmware—>Edit—>Virtual Network Editor,选中vmnet8-Nat设置,查看网关IP 2.在centos中设置: vi /etc/sysconfig/n ...

  9. 【题解】CF1154

    A Description 有三个正整数 \(a,~b,~c\),现在给定 \(x_1~=~a + b,~x_2~=~a + c, x_3~=~b + c, ~x_4~=~a + b + c\),请求 ...

  10. CSS--overflow和hover

    一.Overflow overflow 属性规定当内容溢出元素框时发生的事情. 当插入的一张图片大小超过了元素本身大小,就会将元素撑大 <!DOCTYPE html> <html l ...