每日一句
人的痛苦会把自己折磨到多深呢?
每日一句
You cannot swim for new horizons until you have courage to lose sight of the shore.
除非有勇气离开岸边,否则你永远游不到彼岸。
概念
IO 是主存和外部设备(硬盘、终端和网络等)拷贝数据的过程。IO是操作系统的底层功能实现,底层通过I/O指令进行完成。
以下是5种类Unix下可用的I/O模型
-
阻塞式I/O:Blocking IO
-
非阻塞式I/O:nonblocking IO
-
I/O 复用(Select,poll epoll):IO multiplexing
-
信号驱动式I/O(SIGIO):signal driven IO
-
异步 I/O(posix 的 aio 系列函数):asynchromous IO
Blocking IO
在 Linux 中,默认情况下所有的 socket 都是 Blocking,一个典型的读操作流程大概是这样:
-
通常涉及等待数据从网络到达。当所有等待数据到达时,它被复制到内核中的某个缓冲区
-
把数据从内核缓冲区复制到应用程序缓冲区
当用户进程调用了 recvfrom 这个系统调用, kernel 就开始了 IO 的第一个阶段:准备数据。对于 network IO 来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的 UDP 包)。这个时候 kernel 就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当 kernel 一直等到数据准备好了,它就会将数据从kernel 中拷贝到用户内存,然后kernel返回结果,用户进程才解除 block 的状态,重新运行起来。
所以,Blocking IO 的特点就是在IO执行的两个阶段都被 block了
非阻塞式I/O
Linux 下,可以用过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行操作时,流程如下:
从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。 从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次 发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。
所以,用户进程第一个阶段不是阻塞的,需要不断的主动询问kernel数据好了没有;第二个阶段依然总是阻塞的。
IO 多路复用(NIO)
select、epoll 的好处就在于单个 process 就可以同时处理多个网络链接的 IO。
IO 复用和同步阻塞本质一样,不过利用了新的 Select 系统调用,由内核来负责本来是请求进程该做的轮训操作,看似不非阻塞IO还多了哥系统调用开销,不过因为支持多路IO才算提高了效率
也就是一个可以监听多个。
它的基本原理就是 select、epoll 这个 function会不断的轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程。它的流程如下图:
当用户线程调用select,那么整个进程会被阻塞,而同时,kernel会监视所有select负责的 socket =,当任何一个 socket 中的数据准备好了,select 就会返回,这个时候用户进程会调用 read 操作,将数据 kernel 拷贝到用户进程。
首先开启套接字的信号驱动式IO功能,并且通过sigaction(信号处理程序) 系统调用安装一个信号处理函数 ,该函数调用将立即返回,当前进程没有被阻塞 ,继续工作;当数据报准备好的时候,内核为该进程产生SIGIO 的信号,随后既可以在信号处理函数中调用recvfrom 读取数据报,并且通知主循环数据已经准备好等待处理;也可以直接通知主循环让它读取数据报;(其实就是一个待读取的通知和待处理的通知),基本不会用到。
异步IO(AIO)
多线程和多进程的模型虽然解决了并发的问题,但是系统不能无限的增加线程,由于系统的切换线程的开销恒大,所以,一旦线程数量过多,CPU的时间就花在线程的切换上,正真运行代码的时间就会减少,结果导致性能严重下降
由于我们要解决的问题是CPU高速执行能力和IO设备的龟速严重不匹配,多线程和多进程只是解决这一个问题的一种方法。
另一种解决IO问题的方法是异步IO,当代码需要执行一个耗时的IO操作时,他只发出IO指令,并不等待IO结果然后就去执行其他代码,一段时间后,当IO返回结果是,在通知CPU进行处理我们调用aio_read函数,给内核传递描述符,缓冲区指针,缓冲区大小,和文件偏移量,并且告诉内核当整个操作完成时如何通知我们,该函数调用后,立即返回,不会被阻塞
另一方面:从kernel的角度,当他收到一个aio_read之后,首先它立即返回,所以不会对用户进程产生block,然后kernel会等待数据准备完成,然后将数据拷贝到用户内存(copy由内核完成),当着一切完成后,kernel会给用户进程发送一个singal或者执行下一个基于线程回调函数来完成此次IO处理过程,告诉他read操作完成
美文佳句
岁月应是静好。近来,心神总是不宁,想必是被尘世间的诱惑所困。
我想,要是远方的祥夫先生来到我这“梅知堂”,两人清茶一杯,盘腿而坐,或看他画梅,纸上的梅花仿佛兀自开在飞雪里,如是,那该是一幅怎样的墨梅图?
昨晚,梦里看到那个戴着小黑圆眼镜的祥夫背着黄色的大包正往江南赶路。是又到了梅花开的日子了吗?
面试题
SpringMVC工作原理?
1. 客户端发送请求到前端控制器 DispatcherServlet
2. DispatcherServlet 收到请求后,调用 HandlerMapping 处理器映射器,请求获取 handler
3. 处理器映射器根据 url 找到具体的处理器,生成处理器对象以及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet
4. DispatcherServlet 调用 HandlerAdapter 处理器适配器
5. HandlerAdapter 经过适配器调用 具体处理器(Handler,也叫后端控制器)
6. Handler 执行完成返回 ModelAndView
7. HandlerAdaper 将 Handler 执行结果 ModelAndView 返回给 DispatcherServlet
8. DispatcherServlet 将 ModelAndView 传给 ViewResolver 视图解析器 进行解析
9. ViewResolver 解析后返回具体 View
10. DispatcherServlet 对 view 进行 渲染视图(即将模型数据填充至视图中)
11. DispatcherServlet 响应用户
ArrayList 、LinkedList和Vector的区别?
ArrayList | LinkedList | Vector | |
线程是否安全 | 不保证 | 不保证 | 保证线程安全,但是底层大量使用了synchronized关键字,效率不是很高 |
底层数据结构 | 数组,查询效率高,多用于查询较多的场合 | LinkedList 底层是双向链表,插入和删除非常方便,适用于插入较多的场合 | 数组,查询效率高,多用于查询较多的场合 |
默认大小与扩容 | JDK 1.7 之前默认大小是 10 JDK 1.7 之后是0 每次按照1.5倍扩容 | 底层是链表结构,是不连续的存储空间,没有默认大小的说法 | Vector扩容是2倍 |
@RestController 和 @Controller 有什么区别?
@RestController
注解,在 @Controller
基础上,增加了 @ResponseBody
注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回例如 JSON 数据格式。当然,返回什么样的数据格式,根据客户端的 "ACCEPT"
请求头来决定。
你好,我是yltrcc,日常分享技术点滴,欢迎关注我:ylcoder
网友评论