FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
JavaTechnologyStack/network4 at main · SpartaHao/JavaTechnologyStack · GitHub
SpartaHao
/
JavaTechnologyStack
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
Wiki
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security and quality
Insights
Expand file tree
Breadcrumbs
JavaTechnologyStack
/
network4
Copy path
More file actions
More file actions
Latest commit
History
History
History
134 lines (106 loc) · 19.9 KB
Breadcrumbs
JavaTechnologyStack
/
network4
Copy path
File metadata and controls
134 lines (106 loc) · 19.9 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
16. 简单介绍下同步、异步、阻塞、非阻塞?
Ø 同步:所谓同步就是当一个服务调用另一个服务时,在没有得到结果之前,该调用就不返回东西,一旦有结果,就得到返回值了。
异步:所谓异步同同步相反,当一个服务调用另一个服务时,调用直接得到返回,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。
举个通俗的例子:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
Ø 阻塞与非阻塞:
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
还是上面的例子:
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书的结果。如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
Ø 下面还有一个简单的例子:
老张爱喝茶,废话不说,煮开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)老张觉得自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
17. IO的模型有哪几种?Reactor和preactor线程模型有了解?
阻塞IO
非阻塞IO
IO多路复用
信号驱动IO
异步IO:
同步IO vs 异步IO 1. 同步IO指的是程序会一直阻塞到IO操作如read、write完成 2. 异步IO指的是IO操作不会阻塞当前程序的继续执行
所以根据这个定义,上面阻塞IO当然算是同步的IO,非阻塞IO也是同步IO,因为当文件操作符可用时我们还是需要阻塞的读或写,同理IO多路复用和信号驱动IO也是同步IO,只有异步IO是完全完成了数据的拷贝之后才通知程序进行处理,没有阻塞的数据读写过程。
https://zhuanlan.zhihu.com/p/115220699
IO是分为两个部分的分别是用户态和内核态
(1)数据通过用户态到达内核态
(2)内核态转备好数据的时候,从内核态写入用户态
同步: 第二步数据从内核态写入用户态是用户的线程自行读取数据,处理数据的
异步: 第二步数据是内核态负责写入用户态的,他只是告诉用户我的数据已经准备好了,并且读取完成,这时候直接调用用户态的线程就可以取到数据,内核已经帮我们完成了数据的准备,以及将数据从内核态写入到用户态。
我们可以这么理解,阻塞是说明当数据没有准备好的时候我什么事情都不做,一直等待数据,直到数据准备完成后才去执行下面的步骤;而非阻塞是我们数据没来,我们做自己的事情,有一个进程不停的帮我们去轮训检测数据是否准备完毕,直到数据来了,我们才会接到通知,继续执行。
https://blog.csdn.net/u014437791/article/details/89073761
同步阻塞(BIO: Blocking IO)
• 服务端采用单线程,当accept一个请求后,在recv或send调用阻塞时,将无法accept其他请求(必须等上一个请求处recv或send完),无法处理并发
同步非阻塞(NIO)
• 服务器端当accept一个请求后,加入fds集合,每次轮询一遍fds集合recv(非阻塞)数据,没有数据则立即返回错误,每次轮询所有fd(包括没有发生读写事件的fd)会很浪费cpu
非阻塞的 read,指的是在数据到达前,即数据还未到达网卡,或者到达网卡但还没有拷贝到内核缓冲区之前,这个阶段是非阻塞的。当数据已到达内核缓冲区,此时调用 read 函数仍然是阻塞的,需要等待数据从内核缓冲区拷贝到用户缓冲区,才能返回。
IO多路复用(现在的做法)
• 服务器端采用单线程通过select/epoll等系统调用获取fd列表,遍历有事件的fd进行accept/recv/send,使其能支持更多的并发连接请求
IO复用模型是指的是Linux提供了select/poll的操作,进程通过将一个或者多个fd传递给select或者poll系统调用,阻塞在select操作上,这样子select/poll可以帮我们检测是否有一个或者多个fd是就绪状态,是就绪状态表示的是内核态的数据已经准备完成。当检测出是就绪状态的时候,立刻调用回调函数。这时候应用层再向内核态请求数据的时候,数据已经是准备好的,因此也无需进行阻塞,但是数据从内核复制到用户空间的时候还是需要进行短暂阻塞的。
poll和epoll的区别
poll支持的fd数量是有限的,且select/poll是顺序扫描fd是否就绪
epoll是使用基于事件驱动方式代替的顺序扫描,因此性能高,且epoll的fd数量远大于poll。
信号驱动IO模型
我们上面讲的IO多路复用,可以看到select/poll的时候应用程序一直是阻塞的,直到内核态的数据准备完成之后,我们才会调用其回调函数进行下面的操作,那我们的信号驱动IO模型则更进一步的优化。
信号驱动IO模型,首先开启套接口信号驱动IO功能,并且通过系统调用sigaction执行一个信号处理函数(此系统调用之后立即返回,进程继续工作,他是非阻塞的)。当数据准备就绪的时候,就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,处理数据,同样的内核态的数据复制到用户态的时候依然是阻塞的。
异步IO
异步IO解决的最大的问题就是内核态数据复制到用户态的时候,我们也是非阻塞的,这个是怎么实现的呢?
异步IO,告知内核启动某个操作,并让内核在整个操作完成之后(包括将数据从内核态复制到用户态)通知我们。这种模型与信号驱动模型的主要区别是:信号驱动IO由内核通知我们什么时候开始一个IO操作 ;异步IO模型由内核通知我们IO的操作何时完成。
https://blog.csdn.net/u014437791/article/details/89073761
18. 什么是IO多路复用?
Ø IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程。
Ø IO事件的特点是当不满足某些条件时会阻塞,当程序运行碰到阻塞IO时,会等待阻塞结束再继续运行。如果有很多IO事件,当前事件是阻塞状态,但是第二个IO事件是非阻塞状态可以直接执行的,程序还是会等第一个IO事件运行结束再执行第二个IO事件。要是此时第二个IO事件又变成阻塞状态了,程序又要等待它阻塞结束,这样是很浪费时间的,明明可以先执行不阻塞的IO事件效率最高。
Ø IO多路复用有效地解决了这一尴尬的难题,其基本原理是应用程序把所有待执行的IO事件交给操作系统内核,然后由内核轮询这些IO事件,碰到准备就绪的再返给应用程序执行。多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,变成了一次系统调用 + 内核层遍历这些文件描述符。通常使用select,poll和epoll这三种方法实现IO多路复用,其中select方法跨平台性最强,Windows、Unix、Linux都能使用。poll方法在Unix、Linux都能使用。epoll只能在Linux环境下使用。
Ø 其中epoll效率是最高的,因为select和poll都是把待执行IO事件先放在应用程序的一个列表里,再一起映射给内核,内核轮询完以后,返回给应用的也是整个列表,而epoll是直接把待执行IO事件交给内核处理,内核处理完返回的只有其中准备就绪的IO事件,所以效率极高。而且epoll是支持边缘触发的,而其它两种方法只支持水平触发。边缘触发指的是当内核把准备就绪的IO事件返给应用时,如果下次轮询到此事件时该事件没有发生任何的变化(说明应用还没有做任何处理),即使该事件还是准备就绪也不会返回给应用。而水平触发是内核每次轮询到准备就绪的IO事件都会返给应用,无论应用是否处理过(也许来不及处理,也许不想处理)。
19. nginx/redis 所使用的IO模型是什么?
Ø epoll
20. NIO的三大件分别是什么?Jdk 中Buffer和netty中buff的区别是什么?
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
21. Channel和stream有什么不同?
Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
Stream是面向字节流的,channel是面向缓冲区的
22. Select,poll,epoll的区别?
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
1、poll本质上和select基本没有区别,唯一区别它没有最大连接数的限制,原因是它是基于链表来存储的
2、select单个进程所打开的FD是有限制的,通过FD_SETSIZE设置,默认1024;而epoll没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
3、epoll效率是最高的,因为select和poll都是把待执行IO事件先放在应用程序的一个列表里,再一起映射给内核,内核轮询完以后,返回给应用的也是整个列表。它仅仅知道了,有I/O事件发生了,却并不知道是哪几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作,同时处理的流越多,无差别轮询时间就越长。
而epoll是直接把待执行IO事件交给内核处理,内核处理完返回的只有其中准备就绪的IO事件(只需关注活跃的链接),所以效率极高。epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
4、select/poll 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的。而epoll是在内核中保存一份文件描述符集合,无需用户每次都重新传入,只需告诉内核修改的部分即可;
5、相比于select/poll,epoll支持边缘触发,效率更高。如果采用EPOLLLT模式的话,系统中一旦有大量你不需要读写的就绪文件描述符,它们每次调用epoll_wait都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率.。而采用EPOLLET这种边沿触发模式的话,当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符
6、内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
7、epoll只能在linux下运行,poll 可以在linux、unix下运行,而select 是跨平台性最好, windows、unix、linux都可以运行
整个 select 的流程图如下。
1. select 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的。(可优化为不复制)
2. select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态,是个同步过程,只不过无系统调用切换上下文的开销。(内核层可优化为异步事件通知)
3. select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历。(可优化为只返回给用户就绪的文件描述符,无需用户做无效的遍历)
通过select的方式,既做到了一个线程处理多个客户端连接(文件描述符),又减少了系统调用的开销(多个文件描述符只有一次 select 的系统调用 + n 次就绪状态的文件描述符的 read 系统调用)。
epoll 主要就是针对select的三个缺点进行了改进。
1. 内核中保存一份文件描述符集合,无需用户每次都重新传入,只需告诉内核修改的部分即可。
2. 内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒。
3. 内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合。
来自 <https://blog.csdn.net/coderising/article/details/115258181>
select/poll/epoll之间的区别
select poll epoll
数据结构 bitmap 数组 红黑树
最大连接数 1024 无上限 无上限
fd拷贝 每次调用select拷贝 每次调用poll拷贝 fd首次调用epoll_ctl拷贝,每次调用epoll_wait不拷贝
工作效率 轮询:O(n) 轮询:O(n) 回调:O(1)
来自 <https://cloud.tencent.com/developer/article/1681177>
一幅图总结一下 epoll 的整个工作路程。
select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait:
epoll_create是创建一个epoll句柄;
epoll_ctl:向内核添加、修改或删除要监控的文件描述符。
epoll_wait则是等待事件的产生;
在实践中,只要活儿足够的多,epoll_wait 根本都不会让进程阻塞。用户进程会一直干活,一直干活,直到 epoll_wait 里实在没活儿可干的时候才主动让出 CPU。这就是 epoll 高效的地方所在!
23. epoll 水平触发(LT)与 边缘触发(ET)的区别?在什么场景下使用?
• epoll有EPOLLLT和EPOLLET两种触发模式,LT是默认的模式,ET是“高速”模式。
• LT(Level_triggered)模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作;当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!
• ET(Edge_triggered)模式下,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你()!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!所以在ET模式下,read一个fd的时候一定要把它的buffer读完,或者遇到EAGAIN错误;
举例说明:一个管道收到了1kb的数据,epoll会立即返回,此时读了512字节数据,然后再次调用epoll.这时如果是水平触发的,epoll会立即返回,因为有数据准备好了.如果是边缘触发的不会立即返回,因为此时虽然有数据可读但是已经触发了一次通知,在这次通知到现在还没有新的数据到来,直到有新的数据到来epoll才会返回,此时老的数据和新的数据都可以读取到(当然是需要这次你尽可能的多读取).所以当我们写epoll网络模型时,如果我们用水平触发不用担心数据有没有读完,因为下次epoll返回时,没有读完的socket依然会被返回,但是要注意这种模式下的写事件,因为是水平触发,每次socket可写时epoll都会返回,当我们写的数据包过大时,一次写不完,要多次才能写完或者每次socket写都写一个很小的数据包时,每次写都会被epoll检测到,因此长期关注socket写事件会无故cpu消耗过大甚至导致cpu跑满,所以在水平触发模式下我们一般不关注socket可写事件而是通过调用socket write或者send api函数来写socket,说到这我们可以看到这种模式在效率上是没有边缘触发高的,因为每个socket读或者写可能被返回两次甚至多次,所以有时候我们也会用到边缘触发,但是这种模式下在读数据的时候一定要注意,因为如果一次可写事件我们没有把数据读完,如果没有读完,在socket没有新的数据可读时epoll就不回返回了,只有在新的数据到来时,我们才能读取到上次没有读完的数据。
Ø select(),poll()模型都是水平触发模式,信号驱动IO是边缘触发模式,epoll()模型即支持水平触发,也支持边缘触发,默认是水平触发。
https://blog.csdn.net/qq_34793133/article/details/82055915
来自 <https://cloud.tencent.com/developer/article/1681177>
Back
|
FazBrowse Home
|
New Git URL