IO模型
在进行解释之前,首先说明几个概念:
1.用户空间和内核空间
2.进程切换
3.进程的阻塞
4.文件描述符
5.换成I/O
用户控件与内核空间
现在操作系统都是系统虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件的所有权限,为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间,为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0x00000000和0xFFFFFFFF),供内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各进程使用,称为用户空间。
进程切换
为了控制进程的执行,内核必须有能力挂起正在CPU上运行进程,并恢复以前挂起的某个进程的执行,这种行为被称为进程切换,这种切换是由操作系统完成的,因此可以说,任何进程都是在操作系统内核的支持下运行,是与内核紧密相关的。
从一个进程的运行转到另一个进程上运行,这个过程中仅过了下面这些变化;
保存处理机上下文,包括程序计数器和其他寄存器
更新PCB信息
把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
选择另一个进程执行,并更新其PCB。
更新内存管理的数据结构。
恢复处理机上下文。
注:总而言之就是很耗资源的
进程阻塞
正在执行的进程,由于期待的某些事件来发生,如请求系统资源失败,、等待某种操作的完成、新数据商未到达或新工作做等,侧由系统自动执行阻塞原语(Block),使自己由运行状态变成阻塞状态。科见,进程的阻塞是进程自身的一种主动行为,也因此只有处理运行态的进程(获得CPU),才可以将其转换为阻塞状态,当进程进入阻塞状态,是不占CPU资源的。
缓存I/O
缓存I/O又被称作标准I/O,大多数文件系统的默认I/O操作都是缓存I/O。在Linux缓存I/O机制中,操作系统会将I/O的数据缓存在文件系统的页缓存(page cache)中,也就是说,数据会先被拷贝到操作系统内核缓冲区中,然后才会从操作系统内核的缓冲区拷贝道应用程序的地址空间。用户空间没法直接访问内核空间的,内核态道用户态的数据拷贝
缓存I/O的缺点:
数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的CPU以及开销是非常大的。
同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking) IO分别是什么,到底有什么区别?这个问题其实不同的人给出的答案都可能不同,比如wifi,就认为asynchronous IO和non-blocking IO是一个东西。这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(context)也不相同。所以,为了更好的回答这个问题,我先限定一下本文的上下文。
本文讨论的背景是Linux环境下的network IO。
Stevens在文章中一共比较了五种IO Model:
blocking IO
nonblocking IO
IO multiplexing
signal driven IO
asynchronous IO
由于signal driven IO在实际中并不常用,所以这里只提及剩下四种IO Model。
再说一下IO发生时涉及的对象和步骤。
对于一个network IO(这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process(or thread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:
1 等待数据准备(waiting for the data to be ready)
2 将数据从内核拷贝道进程中(Copying the data from the kernel to the process)
记住这两点很重要,因为这些IO Mode的区别就是在两个阶段上各有不同的情况。
#!/usr/bin/env python3.8
# -*- coding: UTF-8 -*-
# __author: smoke
# file: IO_test
# time: 2021/06/01
import socket
print(socket.socket()) #打印文件描述符
/home/smoke/文档/DocumentFile/PycharmProjects/选课系统Demo/Jaime/venv/bin/python /home/smoke/文档/DocumentFile/PycharmProjects/pythonProject/join/IO_test.py
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
Process finished with exit code 0
浙公网安备 33010602011771号