sellect、poll、epoll
http://www.cnblogs.com/alex3714/p/4372426.html
select
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。
另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。
poll
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
epoll
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()、create、regester、unregest获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
Python select
Python的select()方法直接调用操作系统的IO接口,它监控sockets,open files, and pipes(所有带fileno()方法的文件句柄)何时变成readable 和writeable, 或者通信错误,select()使得同时监控多个连接变的简单,并且这比写一个长循环来等待和监控多客户端连接要高效,因为select直接通过操作系统提供的C的网络接口进行操作,而不是通过Python的解释器。
注意:Using Python’s file objects with select() works for Unix, supported under Windows yet.
接下来通过echo server例子以了解select 是如何通过单进程实现同时处理多个非阻塞的socket连接的
select_server:
#!/usr/bin/env python
# -*- coding:utf-8 -*- # 导入模块
import select
import socket
import sys
import queue # 创建TCP/IP socket套接字实例server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置 socket为非阻塞
server.setblocking(False) ## 绑定server socket地址(ip、端口)
server_address = ('localhost', 10000) #设置服务端地址(即IP和端口)
print(sys.stderr, 'starting up on %s port %s' % server_address) # 打印启动信息(ip、端口)
server.bind(server_address) # server socket实例绑定地址 ## 监听客户端连接,最多允许5个并发连接
server.listen(5)
'''
select()方法接收并监控3个通信列表, 第一个是所有的输入的data,就是指外部发过来的数据,第2个是监控和接收所有要发出去的data(outgoing data),第3个监控错误信息,接下来我们需要创建2个列表来包含输入和输出信息来传给select().
'''
'''
select.select()
select(rlist, wlist, xlist, timeout=None)
select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
作用:等到一个或多个文件描述符准备好某种I/O
select() 方法中接收并监控三个通信列表,
rlist可读列表,所有输入的数据,监控外部发起的连接及发过来的数据;
wlist可写列表,监控并接收所有要发出的数据
xlist错误信息列表,监控错误信息 '''
# Sockets from which we expect to read定义可读列表,保存server socket及已建立的socket连接,首先把server socket放进去
inputs = [server] # Sockets to which we expect to write定义可写列表,保存需要发送数据的socket连接
outputs = [] '''
所有客户端的进来的连接和数据将会被server的主循环程序放在上面的list中处理,我们现在的server端需要等待连接可写(writable)之后才能过来,然后接收数据并返回(因此不是在接收到数据之后就立刻返回),因为每个连接要把输入或输出的数据先缓存到queue里,然后再由select取出来再发出去。 Connections are added to and removed from these lists by the server main loop. Since this version of the server is going to wait for a socket to become writable before sending any data (instead of immediately sending the reply), each output connection needs a queue to act as a buffer for the data to be sent through it.
'''
# 定义发送消息缓冲区(字典类型,每个连接与数据为其中一个key:value对,如socket_connect:'recv_data'),服务端收到的数据不是即收即发的,收到的数据先放到缓冲区,等到扫描可写时再从缓冲区的队列中读取并发送
message_queues = {} '''
The main portion of the server program loops, calling select() to block and wait for network activity. 下面是此程序的主循环,调用select()时会阻塞和等待直到新的连接和数据进来
''' ## 程序主循环
while inputs: # 循环可读列表 # Wait for at least one of the sockets to be ready for processing
print( '\nwaiting for the next event') # 打印提示信息
'''
当你把inputs,outputs,exceptional(这里跟inputs共用)传给select()后,它返回3个新的list,我们上面将他们分别赋值为readable,writable,exceptional, 所有在readable list中的socket连接代表有数据可接收(recv),所有在writable list中的存放着你可以对其进行发送(send)操作的socket连接,当连接通信出现error时会把error写到exceptional列表中。 select() returns three new lists, containing subsets of the contents of the lists passed in. All of the sockets in the readable list have incoming data buffered and available to be read. All of the sockets in the writable list have free space in their buffer and can be written to. The sockets returned in exceptional have had an error (the actual definition of “exceptional condition” depends on the platform).
'''
'''把 inputs、outputs、exceptional(与inputs共用)列表传给select(),
用readable, writable, exceptional来承接select()返回的3个新列表,
readable 列表中的server表示接收新socket连接,其他的socket连接表示有数据可接受
writable列表中的socket连接表示有数据要发送
exceptional列表的socket表示在通信过程中出现有错误
'''
readable, writable, exceptional = select.select(inputs, outputs, inputs, 2) #设置2秒超时,默认阻塞的 '''
Readable list 中的socket 可以有3种可能状态,第一种是如果这个socket是main "server" socket,它负责监听客户端的连接,如果这个main server socket出现在readable里,那代表这是server端已经ready来接收一个新的连接进来了,为了让这个main server能同时处理多个连接,在下面的代码里,我们把这个main server的socket设置为非阻塞模式。 The “readable” sockets represent three possible cases. If the socket is the main “server” socket, the one being used to listen for connections, then the “readable” condition means it is ready to accept another incoming connection. In addition to adding the new connection to the list of inputs to monitor, this section sets the client socket to not block.
''' # Handle inputs处理可读列表
'''
可读列表中有三种状态:
1、server socket监听 新的socket连接
2、已建立的socket连接有数据发过来
3、客户端断开连接(这时候recv()的数据为空)
'''
for s in readable: if s is server: # socket连接为main "server" socket,表示有新的socket要连接进来
# A "readable" server socket is ready to accept a connection
connection, client_address = s.accept() #接收外部连接
print('new connection from', client_address) #打印客户地址(ip,port)
connection.setblocking(False) #设置新连接的socket为非阻塞
inputs.append(connection) #把新连接的socket追加到可读列表 # Give the connection a queue for data we want to send 在数据缓存区中为新连接的socket分配列队,以便缓存需要发送给该socket的数据
message_queues[connection] = queue.Queue()
else: #非server socket,即外部已经建立的socket连接,这时候就接收数据
data = s.recv(1024)
'''
第二种情况是这个socket是已经建立了的连接,它把数据发了过来,这个时候你就可以通过recv()来接收它发过来的数据,然后把接收到的数据放到queue里,这样你就可以把接收到的数据再传回给客户端了。 The next case is an established connection with a client that has sent data. The data is read with recv(), then placed on the queue so it can be sent through the socket and back to the client.
'''
# 这里做一个简单的处理,就是客户发什么数据过来,服务端就再发什么数据给客户。
# 如果是WEB服务器则,需要对外部发来的data进行解析,如外部请求是读取一个图片,那么服务端就需要把图片内容放到该socket连接在缓冲区中对应的对象中
if data: # 有数据,数据为非空
# A readable client socket has data
print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()) ) #打印接收到的数据及地址信息
message_queues[s].put(data) #把接收到的数据放到缓冲区中该连接的队列中
# Add output channel for response
# 把该socket连接添加到可写列表中,等到遍历可写列表时再发送上面存放的数据
if s not in outputs:
outputs.append(s)
else: # 客户端已经断开连接,所以 recv()接收数据就为空,服务端需要把该客户的连接关闭(这里还需要删除可读、可写列表中的该socket连接,及缓冲区中的数据)
'''
第三种情况就是这个客户端已经断开了,所以你再通过recv()接收到的数据就为空了,所以这个时候你就可以把这个跟客户端的连接关闭了
A readable socket without data available is from a client that has disconnected, and the stream is ready to be closed.
'''
# Interpret empty result as closed connection
print('closing', client_address, 'after reading no data') #打印客户端地址信息
# Stop listening for input on the connection
if s in outputs: #如果可写列表中有该socket连接,
outputs.remove(s) # 可写列表中删除该socket连接
inputs.remove(s) # 可读列表中删除该socket连接
s.close() # 关闭该socket连接 # Remove message queue
del message_queues[s] # 消息缓冲区中删除给该socket连接分区的queue队列 '''
对于writable list中的socket,也有2种状态,如果这个客户端连接在跟它对应的queue里有数据,就把这个数据取出来再发回给这个客户端,否则就把这个连接从output list中移除,这样下一次循环select()调用时检测到outputs list中没有这个连接,那就会认为这个连接还处于非活动状态 There are fewer cases for the writable connections. If there is data in the queue for a connection, the next message is sent. Otherwise, the connection is removed from the list of output connections so that the next time through the loop select() does not indicate that the socket is ready to send data.
'''
# Handle outputs处理可写列表
'''
在可写列表中的socke连接有两种状态:
1、有数据要发送
2、无数据要发送(客户端为非活跃状态)
'''
for s in writable:
try:
next_msg = message_queues[s].get_nowait() # 获取发送数据
except queue.Empty: # 无数据要发送
# No messages waiting so stop checking for writability.
print('output queue for', s.getpeername(), 'is empty') #打印客户地址等提示信息
outputs.remove(s) # 可写列表中删除该socket连接,这样做也能让可写列表不变得那么大,即不保存非活跃的socket连接
else: # 有数据要发送
print( 'sending "%s" to %s' % (next_msg, s.getpeername())) #打印发送数据及地址信息
s.send(next_msg) # 发送数据 '''
最后,如果在跟某个socket连接通信过程中出了错误,就把这个连接对象在inputs\outputs\message_queue中都删除,再把连接关闭掉
'''
# Handle "exceptional conditions"处理错误列表
# 停止监听错误列表中的socket连接
for s in exceptional:
print('handling exceptional condition for', s.getpeername() ) #打印地址等提示信息
inputs.remove(s) # 可读列表中删除该socket连接
if s in outputs: # 可写列表中删除该socket连接
outputs.remove(s)
s.close() # 闭关该socket连接 # 消息缓存区中删除该socket对应的queue队列
del message_queues[s]
server运行结果:
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> starting up on localhost port 10000
waiting for the next event
waiting for the next event
new connection from ('127.0.0.1', 60486)
waiting for the next event
new connection from ('127.0.0.1', 60487)
waiting for the next event
new connection from ('127.0.0.1', 60488)
waiting for the next event
new connection from ('127.0.0.1', 60489)
waiting for the next event
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'This is the message.'" from ('127.0.0.1', 60486)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'This is the message.'" from ('127.0.0.1', 60487)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'This is the message.'" from ('127.0.0.1', 60488)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'This is the message.'" from ('127.0.0.1', 60489)
waiting for the next event
sending "b'This is the message.'" to ('127.0.0.1', 60486)
sending "b'This is the message.'" to ('127.0.0.1', 60487)
sending "b'This is the message.'" to ('127.0.0.1', 60488)
sending "b'This is the message.'" to ('127.0.0.1', 60489)
waiting for the next event
output queue for ('127.0.0.1', 60486) is empty
output queue for ('127.0.0.1', 60487) is empty
output queue for ('127.0.0.1', 60488) is empty
output queue for ('127.0.0.1', 60489) is empty
waiting for the next event
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'It whill be send.'" from ('127.0.0.1', 60486)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'It whill be send.'" from ('127.0.0.1', 60487)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'It whill be send.'" from ('127.0.0.1', 60488)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'It whill be send.'" from ('127.0.0.1', 60489)
waiting for the next event
sending "b'It whill be send.'" to ('127.0.0.1', 60486)
sending "b'It whill be send.'" to ('127.0.0.1', 60487)
sending "b'It whill be send.'" to ('127.0.0.1', 60488)
sending "b'It whill be send.'" to ('127.0.0.1', 60489)
waiting for the next event
output queue for ('127.0.0.1', 60486) is empty
output queue for ('127.0.0.1', 60487) is empty
output queue for ('127.0.0.1', 60488) is empty
output queue for ('127.0.0.1', 60489) is empty
waiting for the next event
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'in parts.'" from ('127.0.0.1', 60486)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'in parts.'" from ('127.0.0.1', 60487)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'in parts.'" from ('127.0.0.1', 60488)
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> received "b'in parts.'" from ('127.0.0.1', 60489)
waiting for the next event
sending "b'in parts.'" to ('127.0.0.1', 60486)
sending "b'in parts.'" to ('127.0.0.1', 60487)
sending "b'in parts.'" to ('127.0.0.1', 60488)
sending "b'in parts.'" to ('127.0.0.1', 60489)
waiting for the next event
output queue for ('127.0.0.1', 60486) is empty
output queue for ('127.0.0.1', 60487) is empty
output queue for ('127.0.0.1', 60488) is empty
output queue for ('127.0.0.1', 60489) is empty
waiting for the next event
closing ('127.0.0.1', 60489) after reading no data
waiting for the next event
closing ('127.0.0.1', 60489) after reading no data
waiting for the next event
closing ('127.0.0.1', 60489) after reading no data
waiting for the next event
closing ('127.0.0.1', 60489) after reading no data
waiting for the next event
waiting for the next event
client运行结果:
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> connecting to localhost port 10000
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60503): sending "This is the message."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60504): sending "This is the message."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60505): sending "This is the message."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60506): sending "This is the message."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60503) received "b'This is the message.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60504) received "b'This is the message.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60505) received "b'This is the message.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60506) received "b'This is the message.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60503): sending "It whill be send."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60504): sending "It whill be send."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60505): sending "It whill be send."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60506): sending "It whill be send."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60503) received "b'It whill be send.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60504) received "b'It whill be send.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60505) received "b'It whill be send.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60506) received "b'It whill be send.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60503): sending "in parts."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60504): sending "in parts."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60505): sending "in parts."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60506): sending "in parts."
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60503) received "b'in parts.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60504) received "b'in parts.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60505) received "b'in parts.'"
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'> ('127.0.0.1', 60506) received "b'in parts.'"
epoll_socket_server:
#!/use/local/env python
# -*- coding:utf-8 -*- '''
http://scotdoyle.com/python-epoll-howto.html
''' import socket, select EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!' serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('0.0.0.0', 8080))
serversocket.listen(1)
serversocket.setblocking(0)
serversocket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) epoll = select.epoll()
epoll.register(serversocket.fileno(), select.EPOLLIN) try:
connections = {}; requests = {}; responses = {}
while True:
events = epoll.poll(1)
for fileno, event in events:
if fileno == serversocket.fileno():
connection, address = serversocket.accept()
connection.setblocking(0)
epoll.register(connection.fileno(), select.EPOLLIN)
connections[connection.fileno()] = connection
requests[connection.fileno()] = b''
responses[connection.fileno()] = response
elif event & select.EPOLLIN:
requests[fileno] += connections[fileno].recv(1024)
if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
epoll.modify(fileno, select.EPOLLOUT)
print('-'*40 + '\n' + requests[fileno].decode()[:-2])
elif event & select.EPOLLOUT:
byteswritten = connections[fileno].send(responses[fileno])
responses[fileno] = responses[fileno][byteswritten:]
if len(responses[fileno]) == 0:
epoll.modify(fileno, 0)
connections[fileno].shutdown(socket.SHUT_RDWR)
elif event & select.EPOLLHUP:
epoll.unregister(fileno)
connections[fileno].close()
del connections[fileno]
finally:
epoll.unregister(serversocket.fileno())
epoll.close()
serversocket.close()
sellect、poll、epoll的更多相关文章
- Select、Poll、Epoll、 异步IO 介绍
一.概念相关介绍 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的net ...
- 【python】-- IO多路复用(select、poll、epoll)介绍及实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- IO多路复用(select、poll、epoll)介绍及select、epoll的实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- 同步IO与一部IO、IO多路复用(番外篇)select、poll、epoll三者的区别;blocking和non-blocking的区别 synchronous IO和asynchronous IO的区别
Python之路,Day9 , IO多路复用(番外篇) 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. ...
- select、poll、epoll之间的区别总结
select.poll.epoll之间的区别总结 05/05. 2014 select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪 ...
- (转载) Linux IO模式及 select、poll、epoll详解
注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案 ...
- select、poll、epoll区别总结
1 本质上都是同步I/O 三者都是I/O复用,本质上都属于同步I/O.因为三者只是负责通知应用程序什么时候数据准备好了,实际的I/O操作还是在由应用程序处理:如果是异步I/O的话,实际I/O由内核处理 ...
- 聊聊IO多路复用之select、poll、epoll详解
本文转载自: http://mp.weixin.qq.com/s?__biz=MzAxODI5ODMwOA==&mid=2666538922&idx=1&sn=e6b436ef ...
- select、poll、epoll之间的区别
select.poll.epoll之间的区别总结[整理] select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就 ...
- (转)Linux IO模式及 select、poll、epoll详解
本文为转载,并作了部门调整.修改. [原文出处:https://segmentfault.com/a/1190000003063859] 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么 ...
随机推荐
- namenode 和datanode无法启动,错误:FSNamesystem initialization failed. datanode.DataNode: Incompatible namespaceIDs
问题一: namenode无法启动,查看日志,错误信息如下: org.apache.hadoop.hdfs.server.namenode.FSNamesystem: FSNamesystem ini ...
- Swift 实现iOS Animation动画教程
这是一篇翻译文章.原文出处:http://www.raywenderlich.com/95910/uiview-animation-swift-tutorial 动画( animation)是iOS用 ...
- Oracle数据库中序列用法讲解
序列(SEQUENCE)是序列号生成器,可以为表中的行自动生成序列号,产生一组等间隔的数值(类型为数字).其主要的用途是生成表的主键值,可以在插入语句中引用,也可以通过查询检查当前值,或使序列增至下一 ...
- 2016 - 1- 14 UI阶段学习补充 transform属性详解
UIView的transform属性 transform是view的一个重要属性,它在矩阵层面上改变view的显⽰状态,能实现view的缩放.旋转.平移等功能.transform是CGAffineTr ...
- D - Mysterious Present
这个题和求最长递增序列的题类似,为了能输出一组可行的数据,我还用了一点儿链表的知识. Description Peter decided to wish happy birthday to his f ...
- Hello Qt
版本:Qt 5.5.1 Windows 参考: C++ GUI Programming with Qt 4 Second Edition 1. 打开 Qt Creator,File -> New ...
- ios学习之 关于Certificate、Provisioning Profile、App ID的介绍及其之间的关系
刚接触iOS开发的人难免会对苹果的各种证书.配置文件等不甚了解,可能你按照网上的教程一步一步的成功申请了真机调试,但是还是对其中的缘由一知半解.这篇文章就对Certificate.Provisioni ...
- 20145210 《Java程序设计》第07周学习总结
教材学习内容总结 第十二章 Lambda 12.1 认识Lambda语法 •Lambda 教材的引入循序渐近.深入浅出 •如果使用JDK8的话,可以使用Lambda特性去除重复的信息,例: Compa ...
- HDU 4407
http://acm.hdu.edu.cn/showproblem.php?pid=4407 把修改和询问分成两部分解决 询问求区间内与p不互素的和,和求个数一样,用容斥原理解决,只不过做容斥的时候把 ...
- css中的width,height,属性与盒模型的关系
这段话很容易记住盒模型: css中盒子模型包含属性margin.border.padding.content,他们可以把它转移到我们日常生活中的盒子(箱 子)上来理解,日常生活中所见的盒子也具有这些属 ...