一、
1.用户空间和内核空间概念
操作系统为了支持多个应用同时运行,需要保证不同进程之间相对独立(一个进程的崩溃不会影响其他的进程,恶意进程不能直接读取和修改其他进程运行时的代码和数据)。因此操作系统内核需要拥有高于普通进程的权限,以此来调度和管理用户的应用程序。
于是内存空间被划分为两部分,一部分是内核空间,一部分是用户空间。
内核空间:存储的代码和数据具有更高级别的权限。内存访问的相关硬件在程序执行期间会进行访问控制,使得用户空间的程序不能直接读写内核空间的内存
释义:
当一个程序正在执行的过程中,中断或系统调用发生可以使得CPU的控制权从当前进程转移到操作系统内核。操作系统内核负责保存进程i在CPU中的上下文(程序计数器,寄存器)到PCBi(操作系统分配给进程的一个内存块)中。从PCBj取出进程j的CPU上下文,将CPU控制权转移给进程j,开始执行进程j的指令。
对于一个运行着 UNIX 系统的现代 PC 来说, 进程切换通常至少需要花费 300 us 的时间。
二、不同层次的同步/异步、阻塞/非阻塞
2.1 cpu层次
在cpu层次(操作系统进行IO和任务调度的层次),现代操作系统通常使用异步非阻塞方式进行IO(有少部分IO可能会使用同步非阻塞轮询),即发出IO请求之后,并不等待IO操作完成,而是继续执行下面的指令(非阻塞),IO操作和CPU指令互不干扰(异步),最后通过中断的方式来通知IO操作完成结果。
2.2 线程层次
在线程层次(操作系统调度单元的层次),操作系统为了减轻程序员的负担,将底层的异步非阻塞的IO方式进行封装,把相关系统调用(如read、write等)以同步的方式展现出来。然而,同步阻塞的IO会使线程挂起,同步非阻塞的IO会消耗CPU资源在轮询上。为了解决这个问题,出现了:
2.2.1 阻塞式IO(同步阻塞)
2.2.2 非阻塞式IO(同步非阻塞)
2.2.3 IO多路复用select,poll,epoll(同步非阻塞-其实是把阻塞点改了位置)
2.2.4 信号驱动式IO(SIGIO)(同步)
2.2.5 直接暴露出异步的IO接口,如kernel-aio和IOCP(异步非阻塞)
2.3程序员感知层次
在Linux中,上面提到的IO多路复用用的比较广泛,也是比较理想的解决方案,然而,直接使用select之类的接口,依然比较复杂,所以各种库和框架百花齐放,都试图对IO多路复用进行封装。
此时,库和框架提供的API又可以选择是以同步的方式还是异步的方式来展现,如python的asyncio库中,通过协程提供了同步阻塞式的API;如node.js中,就通过回调函数,提供了异步非阻塞的API。因此,按照层次划分来说,node.js在程序员感知层次提供了异步非阻塞的API,但是在Linux下,在线程层次以同步非阻塞的epoll来实现
三、同步异步定义
同步(sync)和异步(async)关注的是消息通信机制(是以同步还是异步的方式回答你返回结果)
同步(sync):调用某个东西时,调用方需要等待这个调用返回结果才能继续往后执行。被调用方自己不会主动的去通知到调用方,而是需要调用方主动的去请求返回结果
异步(async):调用方不会立即得到结果,而是在调用发出后,调用者可以继续执行后续操作;被调用方通过状态来主动通知调用者,或者通过回调函数来处理这个调用
eg1:你去商场买衣服,确定要这件衣服,你得在店里等带导购拿货,不能离开,这叫同步。你在网上买衣服,付完款后你就可以去做其他事件,等货到签收就可以,这叫异步。
eg2:老张把水壶放到火上,坐等水开(同步阻塞);老张把水壶放到火上,去客厅看电视,时不时去厨房看水有没有开(同步非阻塞);老张把水开会响笛的响水壶放到火上,坐等水开(异步阻塞);老张把响水壶放到火上,去客厅看电视,水壶响之前不去看它,响了再去拿壶(异步非阻塞)
这里同步异步只是对于水壶而言,虽然都能干活,但是响水壶可以在自己完工之后,提示老张水开了;但是普通水壶只能让调用者去轮询被调用者(水壶),造成老张效率低下。而阻塞非阻塞只是对于老张(调用方)而言,坐等的老张是阻塞的,看电视的老张是非阻塞的,一般异步配合非阻塞使用,才能发挥异步的效用.
3.1 同步执行
同步方法:调用一旦开始,调用者必须等到方法调用返回结果后,才能继续后续的行为。整个过程是由调用者主动等待这个调用的结果。
image.png
3.2 异步执行
异步方法:调用更像一个消息传递,一旦开始,方法调用就会立即返回(没有返回结果),调用者就可以继续后续操作。同时,异步方法通常会在另外一个线程中,“真实”地在执行着。整个过程不会阻碍调用者的工作。调用发出后,被调用者通过状态、通知来告知调用者,或者通过回调函数处理这个调用。
image.png
四、阻塞非阻塞定义
关注的是程序(调用者)在等待调用结果(消息,返回值)时的状态
阻塞调用:调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。调用方一直等待调用
非阻塞调用:在不能立刻得到结果之前,该调用不会阻塞当前线程
eg:你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果
网友评论