day10-阻塞IO,非阻塞IO,同步IO,异步IO介绍

FAQ?

前面介绍了gevent实现了协程,同时在遇到I/O操作时实现协程的自动切换,协程之所以能够处理大并发,其实就是把I/O操作给挤掉了,最后整个程序就变成了只有CPU的运算,由于CPU的速度很快,所以就感觉实现并发的效果。那么它和我们所说的事件驱动有什么关系吗?几个任务切换执行,当程序遇到I/O操作进行切换时,注册一个回调函数到事件循环中,然后当I/O操作完成后再切换回来继续执行,那么这个I/O操作为什么不阻塞?

答:因为I/O的操作是用操作系统完成的,我们使用系统上的软件读取一个文件,以为是使用该软件打开文件和读取文件,其实不是的,是通过操作系统的文件调度接口打开这个文件读取文件的。这么说来其实是操作系统负责I/O的控制,所以一遇到I/O操作就进行协程的切换之前,要确保操作系统I/O操作完成之后,我还能够切换回来,那么如何保证我能够切换回来呢?

答:加一个回调函数,在协程切换之前调用操作系统I/O接口的时候告诉操作系统处理完之后调用回调函数,那么这时候,回调函数就会通知协程,代表I/O已经完成,协程就可以切换回来了拿到I/O。所以这就是通过事件驱动的方式。

接下来我们来看协程是如何实现的?如何去实现事件驱动情况下I/O阻塞的自动切换?

I/O多路复用

概念说明

在进行解释之前,首先要说明几个概念:
- 用户空间和内核空间
- 进程切换
- 进程的阻塞
- 文件描述符
- 缓存 I/O

用户空间与内核空间

现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。

进程切换

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:
1. 保存处理机上下文,包括程序计数器和其他寄存器。
2. 更新PCB信息。

3. 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
4. 选择另一个进程执行,并更新其PCB。
5. 更新内存管理的数据结构。
6. 恢复处理机上下文。

总而言之就是很耗资源,具体的可以参考这篇文章:进程切换

注:进程控制块(Processing Control Block),是操作系统核心中一种数据结构,主要表示进程状态。其作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位或与其它进程并发执行的进程。或者说,OS是根据PCB来对并发执行的进程进行控制和管理的。 PCB通常是系统内存占用区中的一个连续存区,它存放着操作系统用于描述进程情况及控制进程运行所需的全部信息

进程阻塞

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的

文件描述符fd 

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

缓存I/O

缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

缓存 I/O 的缺点:

数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

小结

在socket中进行收发数据时,当我们手动send强制缓冲区超时发送,因为连续调用2次send的动作(紧挨着),在这种情况下,I/O缓冲区就会把大小和内容数据合并成一条数据发送给客户端(把2次的手动强制缓冲区超时当做1次),这就导致在网络开发过程中粘包的问题。那么为什么把2次数据合并成一条数据呢?

答:因为它要减少从内核态到用户态的数据的来回拷贝,就像如果你打开一个文件读到内存里,你以为是它直接读到用户的内存里,其实是先读到内核的缓存里,然后再由内核将数据拷贝到用户空间的内存里,所以就是因为存在着这样的拷贝,它也是消耗系统资源,所以为了避免这么来回拷贝,将好几次的拷贝变成1次拷贝,提高了效率。这样的话,我们就理解了客户端或服务端发送数据不是直接发送到socket用户的程序里,而是先发送到操作系统的I/O接口(内核空间)里,然后再把数据拷贝到用户的内存空间里。

 

posted @ 2017-12-14 23:58  Mr.hu  阅读(105)  评论(0编辑  收藏  举报