技术标签: 杂
首先介绍阻塞与非阻塞: 阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做);那么你可以去睡觉了,因为你知道快递把货送来时一定会给你打个电话(假定一定能叫醒你)。 非阻塞忙轮询。接着上面等快递的例子,如果用忙轮询的方法,那么你需要知道快递员的手机号,然后每分钟给他挂个电话:“你到了没?”
很明显一般人不会用第二种做法,不仅显很无脑,浪费话费不说,还占用了快递员大量的时间。
大部分程序也不会用第二种做法,因为第一种方法经济而简单,经济是指消耗很少的CPU时间,如果线程睡眠了,就掉出了系统的调度队列,暂时不会去瓜分CPU宝贵的时间片了。
为了了解阻塞是如何进行的,我们来讨论缓冲区,以及内核缓冲区,最终把I/O事件解释清楚。缓冲区的引入是为了减少频繁I/O操作而引起频繁的系统调用,当你操作一个流时,更多的是以缓冲区为单位进行操作,这是相对于用户空间而言。对于内核来说,也需要缓冲区。
假设有一个管道,进程A为管道的写入方,B为管道的读出方。
这四个情形涵盖了四个I/O事件,缓冲区满,缓冲区空,缓冲区非空,缓冲区非满(说的内核缓冲区)。这四个I/O事件是进行阻塞同步的根本。
阻塞I/O模式下,一个线程只能处理一个流的I/O事件。如果想要同时处理多个流,要么多进程(fork),要么多线程(pthread_create),很不幸这两种方法效率都不高。
于是再来考虑非阻塞忙轮询的I/O方式,我们发现我们可以同时处理多个流了:
while true { for i in stream[]; { if i has data read until unavailable } }
我们只要不停的把所有流从头到尾问一遍,又从头开始。这样就可以处理多个流了,但这样的做法显然不好,因为如果所有的流都没有数据,那么只会白白浪费CPU。阻塞模式下,内核对于I/O事件的处理是阻塞或者唤醒,而非阻塞模式下则把I/O事件交给其他对象。为了避免CPU空转,可以引进了一个代理(select)。这个代理可以同时观察许多流的I/O事件,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中醒来,于是我们的程序就会轮询一遍所有的流。
while true { select(streams[]) for i in streams[] { if i has data read until unavailable } }
如果没有I/O事件产生,我们的程序就会阻塞在select处。但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,但却并不知道是那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。
但是使用select,我们有O(n)的无差别轮询复杂度,同时处理的流越多,每一次无差别轮询时间就越长。
select/poll是通过轮询的方法来获得就绪的状态,调用select/poll后就阻塞住,直到有就绪的文件描述符,或者超时,或者被中断。返回值是就绪的文件描述符的个数,需要遍历作为参数传入的文件描述符的位域或数组获得哪个文件描述符。
所以引入epoll:
epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll之会把哪个流发生了怎样的I/O事件通知我们。此时我们对这些流的操作都是有意义的。(复杂度降低到了O(k),k为产生I/O事件的流的个数,也有认为O(1))
epoll是通过后台中断的方式来获得就绪的状态,调用epoll_create创建实例,调用epoll_ctl添加或删除监控的文件描述符,调用epoll_wait阻塞住,直到有就绪的文件描述符,通过epoll_event参数返回就绪状态的文件描述符和事件
(注:当对一个非阻塞流的读写发生缓冲区满或缓冲区空,write/read会返回-1,并设置errno=EAGAIN。而epoll只关心缓冲区非满和缓冲区非空事件)。
一个epoll模式的代码如:
while true { active_stream[] = epoll_wait(epollfd) for i in active_stream[] { read or write till unavailable } }
epoll的原理就是:
你把要监控读写的文件交给内核(epoll_add)
设置你关心的事件(epoll_ctl),比如读事件
然后等(epoll_wait),此时,如果没有哪个文件有你关心的事件,则休眠,直到有事件,被唤醒
然后返回那些事件实现并发,还需要配合非阻塞的读写。这样就可以一下搜集一大把文件(套接字),然后一下读写一大把文件(不会因为某个文件慢而阻塞),这样来实现并发。
epoll的优势在于,由接收数据的OS来负责通知你有数据可以操作,因为OS是知道什么时候有数据的。
仍然用快递例子来说,ePoll的优势就是,你可以随便做其他的事情,当有快递来的时候,他给你打电话让你来拿,你空了的时候下来拿就好了。
不像阻塞那样需要一直在窗边看着快递来没来,也不需要像select那样不停地打电话问快递来没来。尤其是在快递比较多的时候,select需要问快递你没有你的快递,快递说有的时候,你还需要逐个问某一个包裹到没到;ePoll会直接告诉你,你的哪个包裹的快递到了。
kqueue与epoll非常相似,最初是2000年Jonathan Lemon在FreeBSD系统上开发的一个高性能的事件通知接口。注册一批socket描述符到 kqueue 以后,当其中的描述符状态发生变化时,kqueue 将一次性通知应用程序哪些描述符可读、可写或出错了。
kqueue的接口包括 kqueue()、kevent() 两个系统调用和 struct kevent 结构:
struct kevent { uintptr_t ident; /* 事件 ID */ short filter; /* 事件过滤器 */ u_short flags; /* 行为标识 */ u_int fflags; /* 过滤器标识值 */ intptr_t data; /* 过滤器数据 */ void *udata; /* 应用透传数据 */ };
在一个 kqueue 中,{ident, filter} 确定一个唯一的事件:
事件的 id,一般设置为文件描述符。
可以将 kqueue filter 看作事件。内核检测 ident 上注册的 filter 的状态,状态发生了变化,就通知应用程序。kqueue 定义了较多的 filter:
与socket读写相关的filter:
行为标志flags:
过滤器标识值:
注册事件到 kqueue
bool Register(int kq, int fd) { struct kevent changes[1]; EV_SET(&changes[0], fd, EVFILT_READ, EV_ADD, 0, 0, NULL); int ret = kevent(kq, changes, 1, NULL, 0, NULL); return true; }
Register 将 fd 注册到 kq 中。注册的方法是通过 kevent() 将 eventlist 和 neventlist 置成 NULL 和 0 来达到的。
人们一般将 socket IO 设置成非阻塞模式,以提高读写性能的同时,避免 IO 读写不小心被锁定。为了达到某种目的,有人会通过 getsocketopt 来偷看 socket 读缓冲区的数据大小或写缓区可用空间的大小。在 kevent 返回时,将读写缓冲区的可读字节数或可写空间大小告诉应用程序。基于这个特性,使用 kqueue 的应用一般不使用非阻塞 IO。每次读时,根据 kevent 返回的可读字节大小,将接收缓冲区中的数据一次性读完;而发送数据时,也根据 kevent 返回的写缓冲区可写空间的大小,一次只发可写空间大小的数据。
文章浏览阅读281次。模拟电子技术习题-宁波广播电视大学宁波广播电视大学计算机科学与技术(本科)补修课程《模拟电子技术基础》练习题一、用大于号(>)、小于号(1、在本征半导体中,电子浓度 空穴浓度;2、在P型半导体中,电子浓度 空穴浓度;3、在N型半导体中,电子浓度 空穴浓度。二、选择括号中的答案填空。1、在杂质半导体中多数载流子的浓度取决于 ,而小..._电大机考模拟电子
文章浏览阅读1.7k次。程序员必备的国外IT网站本文介绍了几个IT行业的国外的网站,都是相当好的网站。英文好的朋友们可以去看看,会有很大收获的。1.TheServerSidewww.theserverside.com/这是一个老牌的IT信息网站,从名称上你就能看出,它是关注服务器端编程的,以Java和Java周边信息为主,不过最近它也有向客户端和 微软产品扩展的趋势_海外程序员下载网址
文章浏览阅读3.9k次。图表联动: 方式一: echarts.init对象.group='x'; echarts.init对象2.group='x'; echarts.connect('x'); 方式二: echarts.connect([echarts.init对象1,echarts.init对象2])事件: 原生鼠标事件: echarts.init对象.on('click',fn); echarts.init对象.on('click','响应的组件属性',fn) .on('_echarts原生事件对象
文章浏览阅读2.4k次,点赞4次,收藏7次。一、安装指定版本 docker二、安装kubectl三、安装minikube四、验证minikube是否可用 minikube顾名思义即迷你型Kubernetes,非常适合快速学习k8s的各个组件的作用及yml的编写。实验环境:虚拟机:virtualbox 或 VMwareCentos 7运行内存2G,磁盘内存40G在线环境一、安装指定版本 docker指定版本Docker安..._kubectl run minikube
文章浏览阅读5.3k次,点赞3次,收藏36次。在Java开发中,若单次展示的数据量太大,会造成程序响应缓慢,就需要用到分页功能,每一页展示一定量的数据,分多次展示...那么在List集合中,如何实现分页功能呢?本文将以3种方式,分别是。......_java list 分页
文章浏览阅读501次,点赞9次,收藏8次。杭电计网实验10(因为是提前修的,所以可能有一些考虑的不够全面,求求别骂)_数据流拓扑图 球球
文章浏览阅读346次。整理时,多尝试这些技巧,引导学生探索更多技巧。 五、课堂练习:利用具体的操作题目引导学生进行文件整理练习,通过练习巩固本课所学,掌握操作技巧。可以采取比赛形式,以小组为单位开展,激励学生尽力投入练习。建议练习后先进行组内评价,再进行班级范围内的交流评价,相互取长补短,并将分类整理的目的和方法回家讲给父母听。 六、课堂小结:总结本课学习和掌握的文件分类整理技巧,引导学生谈一谈在活动过程中的新发现,探..._泰山版小学信息技术第二册全册教学设计
文章浏览阅读153次。据悉,听人说的,无法提供规则引用,通俗说就是忘了从哪儿悉的,windows日后将继承所有linux命令,然后发布在一个新版本的windows操作系统中。既然忘了从哪儿悉的,自然时间表也就无从谈起。活在裆下,自然要受到裆的骚气,能够凑活过下去。当我们遇到必须使用windows和linux两个操作系统的情况怎么办?1、买两台电脑,组合使用。2、安装vm,装多个操作系统。以上架构势必带来程序的复杂度,而..._windows mingw 可以运行linux程序吗
文章浏览阅读270次。首先是在这个是针对字段的,所以给cols新增一个配置项,来决定哪些列需要有过滤的功能,一般来说只有那些数据可以归纳成几种分布的列才有做这个过滤的需求,比如例子中的性别列还有职业列,id列这些的意义不大,当然也不是绝对的,有的就是需要呢。所以新增了一个配置项filter: true/false/null然后在表格渲染完毕之后给表头对应的字段添加上过滤的那个图标和事件这个是主要的代码篇幅太长没办法全显..._layui cols加标签
文章浏览阅读4.3k次。【协议】 数据包可以想象成是在网络上传输的电子信封。在它的数据头中有地址和其他相关信息,然后才发出真正的数据帧。 网络数据传输,有着很多不同的协议或规则来定义数据包以什么格式以及为了发送必须 做些什么。 网络游戏现在通常会让游戏逻辑使用两种协议之一:TCP、UDP。有的游戏会使用第三种协议,ICMP,常用于一些非游戏逻辑的功能。【IP】 全称是Internet Protocol(网际网络协议),要通过网络发送数据,这是需要遵守的最基本的协议。不管是ICMP、TCP还是UDP,都必须附加I..._游戏更新系统时是使用tcp协议还是udp协议
文章浏览阅读80次。lufylegend库 LGraphics <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>lufylegend</title> <script typ_lufylend怎样循环画图片
文章浏览阅读76次。以下是转载别人的日志,以方便自己以后使用,仅供自己学习之用。package com.jackey.topic;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;//循..._java map批处理数据