高级I/O
非阻塞I/O
低速系统调用:可能使得进程永远阻塞的一类系统调用
- 读写管道、网络、终端
- 在某些必要条件未发生之前,打开某些文件
- 对已经上了锁的文件读写
- 某些ioctl操作
- 某些通信函数
读写磁盘文件虽然可能会短时间的阻塞,但也不是低速系统调用
非阻塞I/O可以通过两种方式获得
- 在调用open的时候使用O_NONBLOCK标志
- 可以使用fcntl函数进行设置
不同的设备的阻塞程度不一样
- 我的wmware在书上实验中没产生阻塞返回
- 而在secure CRT中基本上6000字节左右返回一下
书上的实验方式被称作轮询,占用CPU资源较大
- 可以用多线程代替:但是同步又复杂而且还有同步的开销(上🔒)
- 可以采用非阻塞的I/O多路转接
字节范围锁(record locking)
与读写锁的区别
- 锁的粒度更细了
- 读写锁是作用于线程之间的同步
- 字节范围锁是针对于进程之间的同步
- 字节范围锁是在获得文件描述符的一个属性:fcntl函数
- 字节范围锁如果没法获得可以选择 获得失败或则阻塞等待两种状态。
一些实现细节
- 如果进程以已经拥有一把相同位置的锁,如果继续申请相同的位置就会覆盖原来的锁
- 锁的区域可以进行裂开或则合并
- 利用F_GETLK不会报告自己进程拥有的锁,只会报告其它进程的锁
- 系统检测出来一个死锁之后一般都是子进程返回错误
锁的隐含释放和继承
- 一个进程释放的时候,其所建立的锁全部被释放
- 任何时候关闭一个描述符,这进程通过这一个描述符所引用的文件上面的任何一把锁(与该文件、该进程相关的)都会关闭
- 子进程不会继承父进程的锁
I/O多路转接
为啥需要多路转接?
-
我们假想有一个这样的进程,有多个描述符,而且这些通过这些描述符进行数据交流。
-
如果此时利用一个阻塞IO进行读写,而能就永远阻塞在一个描述符上面了
-
可以利用多个进程分别负责这几个描述符,但是程序很变得更加的复杂
-
可以来利用一个进程多个线程,但是增加同步性的,增加程序的复杂性
-
也可利用非阻塞io以轮询进行每个描述符的读写,这样CPU受不了
-
可以使用异步io,但是也有问题:1.并非所有的系统都支持2.我们发送的信号只有有一个,对多个描述符进行异步io,那接收到信号,我们去还是得每个描述符以非阻塞的方式去查询一下。
- 异步io用于终端和网络,select用于所有的描述符
什么是多路转接技术
- 先构造一张有关描述符的列表,然后去调用一个函数,直到有一个描述符中的一个已经准备好进行io了,函数返回我们想要知道的那个描述符
select 和pselect
-
select需要传入的参数和返回
-
我们关心的描述符:三个fd_set结构(读、写和异常)
-
我们愿意等待的时间:一个timeval结构
- 时间设置为null:那么将永久等待(注意这儿等待是全部我们想知道的描述符等待,而不是单独的一个描述符)。直到捕捉到一个信号或者一个描述符准备好了返回
- 时间设置为0:那么相当于轮询
- 时间按设置为一个值:返回有三种可能,信号(-1),描述符准备好了(准备好的数量),时间过了 (0)
-
返回哪些描述符是已经准备好了的
- 返回-1(信号):不修改其中任何一个描述符集
- 返回0(时间过来):所有的描述符集清零
- 返回已经准备好的总描述符数:描述符集只留下准好好的描述符的对应位
-
-
pselect的不同
- 时间结构更细了,用的timespec结构
- pselect的超时值被设置为const,不能够被修改。而select是要被修改的
- 增加了一个信号屏蔽功能,可以在等待期间屏蔽一些信号,在返回的之前恢复信号。
poll
- 和select一样的功能,但是接口不太一样
- 传入的是一个pollfd数组,里面有pollfd有fd,events(读,写还是出错),revents等东西,以代表一个fd的情况
- 这儿与select不一样,当一个fd准备好了,不变events,而变的是revents。
- select、poll这些在Linux系统当中都是不会自动重启的
readv和writev
也被称作散步读和聚集写
writev将从缓冲区散乱的数据,聚集写在一个文件中
readv将从读入的数据的数据,散乱的读在一个缓冲区的各个地方
有两种方法可以代替我们的散布读和聚集写:1.多次调用read和write 2.用write 的时候,先用一个大的缓冲区把散布的缓冲区聚集起来,一次调用write
但是我们写程序的时候最好用较少的系统调用来完成任务。(推介用readv和writev)
存储映射IO
使一个磁盘文件与存储空间中的一个缓冲区进行映射,但从缓冲区中取数据就是在文件中取数据
这个功能由mmap函数实现,其中函数中最重要就是两个参数:prot和flag参数
- prot说明了映射区域被保护的要求:可读、可写、可执行等等。但是不能够超过打开文件open模式的访问权限
- flag参数:控制在缓冲区的头地址、控制是否是存储操作是否改变文件的内容(shared)或则改变一个副本而已。
如果内存页已经被修改了,如何存储到我们的文件中去呢?
- 利用我们的msync函数,相当于fsync函数(安全的,稳定的)
- 或者等待着该页被调度就被写进我们的文件中去了
映射的关系什么被解除?
- 进程终止,或者munmap函数之后映射被解除,munmap并不会将当前修改的页写进我们文件中
- 关闭文件描述符并不会解除映射区