The difference between select, poll and Epoll
Select
Select was first seen in the 4.2BSD in 1983, and it was used by a select () system to monitor multiple file descriptor arrays (everything in Linux is file, block device, socket connection, etc.). ), when Select () returns, the ready file descriptor in the array is changed by the kernel to the flag bit Allows the process to obtain these file descriptors for subsequent read and write operations (select will constantly monitor the network interface in a directory where the number of file descriptors into the ready state "in the network interface, a connection will create a ' file '", after becoming ready state, Select to manipulate the file descriptor).
"Socketserver is multi-threaded to handle multiple requests, each connected to allocate a thread to handle, but the select is a single process, a process execution code is definitely serial, but now it is necessary to implement the concurrency through a process, a process with only one main thread, Just talk about using a thread to implement the concurrency effect. Why use a process to achieve multiple concurrency without multithreading?
========== A: Because a process is more concurrent than multithreading is the efficiency of multi-concurrency is also high, because the start of multithreading will have a lot of overhead, and the CPU to constantly check the state of each thread, determine which thread can be executed. This is also a pressure on the system, the single process can avoid this overhead and the pressure on the system,
So how does a single process achieve multiple concurrency???
======== A: Very ingenious use of the producer and consumer model (async), producers and consumers can implement non-blocking, a socketserver through select to receive multiple connections (before the socket a process can only receive a connection, When the new connection is received when the blocking, because the socket process to communicate with the client, the two are waiting for each other "client sent a message, the server received, the client waiting to return .... The server is waiting to receive ... "has been blocking, this time if another connection, to wait until the previous connection broken, this can be connected. -----------means that multiple processes are blocked with a basic socket. In order to solve this problem, each connection generated by a thread, is not blocked, but when the number of threads is too high, for the CPU overhead and pressure is relatively large. For a single socket, most of the time when blocking is waiting for IO operations (network operations also belong to IO operations). To avoid this, an asynchronous ============= client initiates a connection, registers a file handle on the server, and the server continually polls the list of these file handles, the main process and the client establish a connection without starting the thread, and this time the main process interacts with the client. Other clients are unable to connect to the main process, in order to realize that the main process can both send and receive messages to the connected clients, but also to establish a connection with the new client, the polling is very fast (dead loop) to brush the client connected to the list of file handles, as long as the client sends a message, the server read the message, There is another list to receive the message returned by the client, but also constantly to brush the list, brush it back to the client, so that the communication with the client is completed, but the connection with the client has not broken, but entered the next polling. 】
Select advantages
Select is currently supported on almost all platforms and is well-cross-platform in nature.
Select disadvantage
Each time a select is called, the FD collection needs to be copied from the user state to the kernel state, which can be very expensive when FD is large.
The maximum number of FD that a single process can monitor is the default of 1024 on Linux (this limit can be improved by modifying the macro definition or recompiling the kernel)
And because the FD of select is placed in the array, and each time the entire array is linearly traversed, when there are many FD, the overhead is very high
Python Select
The function that calls Select is Readable,writable,exceptional = Select.select (rlist, wlist, xlist[, timeout]), and the first three parameters are three lists, respectively. The objects in the array are waitable object: Both the file descriptor of the integer or the object that owns the Fileno () that returns the file descriptor method (descriptor).
Rlist: Waiting for read-ready list
Wlist: Waiting for a list to be written ready
Errlist: Waiting for "exception" list
The Select method is used to monitor the file descriptor, and if the file descriptor is changed, gets the descriptor.
1. These three lists can be an empty list, but receiving 3 empty lists is dependent on the system (acceptable on Linux, but not on Windows).
2. When the descriptors in the rlist sequence are readable (ACCETP and read), get the changed descriptors and add them to the readable sequence
3. When a wlist sequence contains a descriptor, all the descriptors in that sequence are added to the writable sequence
4. When a handle in the errlist sequence has an error, the handle to the error is added to the exceptional sequence
5. When the timeout period is not set, select will block until the listener descriptor changes
When the time-out = 1 o'clock, if there is no change in the listening handle, select blocks 1 seconds, then returns three empty lists, and executes directly if the Listener descriptor (FD) changes.
6. The file object (such as Sys.stdin, or object returned by open () and Os.open () can be accepted in the list Ptython, and the socket object will return Socket.socket (). You can also customize the class as long as there is a suitable method for Fileno () (you need to actually return a file descriptor instead of a random integer).
Select Example:
The Python Select () method directly invokes the operating system's IO interface, which monitors sockets,open files, and pipes (all file handles with Fileno () methods) become readable and writeable, or communication errors, Select () makes it easy to monitor multiple connections at the same time, and this is more efficient than writing a long loop to wait and monitor a multi-client connection, because select operates directly from the C network interface provided by the operating system, rather than through the Python interpreter
#coding: Utf8import selectimport socketimport sysimport queue# Create a TCP/IP Process Server = Socket.socket (socket.af_inet, Socket. SOCK_STREAM) server.setblocking (0) #连接地址和端口server_address = (' localhost ', 10000) print >>sys.stderr, ' starting up ' On%s prot%s '% Server_addressserver.bind (server_address) #最大允许链接数server. Listen (5) inputs = [Server]outputs = []message_ Queues = {}while inputs:print >>sys.stderr, ' \nwaiting for the next event ' readable,writable,exceptional = Sel Ect.select (inputs,outputs,inputs) # Handle inputs for s on Readable:if S is server: # A "Rea Dable "Server socket is ready to accept a connection connection, client_address = S.accept () Print & Gt;>sys.stderr, ' New connection from ', Client_address #connection. setblocking (0) inputs.append (CO nnection) # Give The connection a queue for data we want to send message_queues[connection] = Q Ueue. Queue () Else: data = S.RECV (1024x768) if data: # A Readable client socket has data print >>sys.stderr, ' received '%s ' from%s '% (data, s.getpeername ()) message_queues[s].put (data) #这个s相当于c Onnection # ADD Output channel for response if s not in outputs:outputs. Append (s) Else: # Interpret empty result as closed connection print >>SYS.S Tderr, ' closing ', client_address, ' After reading no data ' # Stop listening for input on the connection If s in Outputs:outputs.remove (s) #既然客户端都断开了, I don't have to return the data to it, so this time if the client's connection object is still in the outputs list, It deletes Inputs.remove (s) #inputs中也删除掉 s.close () #把这个连接关闭掉 # Remove Message Queue del Message_queues[s] # Handle outputs for s in WRITABLE:TR Y:next_msg = Message_queues[s].get_nowait () except Queue.empty: # No messages waiting so stop checking for Writabili Ty. Print >>sys.stderr, ' output queue for ', S.getpeername (), ' is empty ' outputs.remove (s) Else: Print >>sys.stderr, ' sending '%s ' to%s '% (next_msg, S.getpeername ()) S.send (Next_msg.upper ()) # Handle "exceptional conditions" for s in Exceptional:print >>sys.stderr, ' handling exceptional condition For ', S.getpeername () # Stop listening for input on the connection inputs.remove (s) if s in outputs: Outputs.remove (s) s.close () # Remove Message Queue del Message_queues[s] Server
Code parsing:
The Select () method receives and monitors 3 communication lists, the first of which is all input data, which refers to the externally sent information, and the 2nd one is to monitor and receive all of the information to be sent out (outgoing data), the 3rd monitoring error message, Next we need to create 2 lists to contain input and output information to pass to select ().
# Sockets from which we expect to readinputs = [Server]# Sockets to which we expect to writeoutputs = []
All incoming connections and data for all clients will be processed by the server's main loop program in the list above, and we now have to wait for the connection to be writable (writable) before we can come back and then receive the data and return it (so it is not returned immediately after receiving the data). Because each connection is going to cache the input or output data first into the queue, then the select is removed and sent out again.
# Outgoing message queues (socket:queue) message_queues = {}
The main portion of the server program loops, calling Select () to block and wait for network activity.
The following is the main loop for this program, which blocks and waits until new connections and data come in when the Select () is called
While inputs: # Wait In least one of the sockets to is ready for processing print >>sys.stderr, ' \nwaiting for T He next event ' readable, writable, exceptional = select.select (inputs, outputs, inputs)
When you pass inputs,outputs,exceptional (here with inputs) to select (), it returns 3 new lists, which we assign to readable,writable,exceptional, respectively, All socket connections in the readable list have data to receive (RECV), all the socket connections in the writable list that you can send to, Error is written to the exceptional list when the connection communication error occurs.
Readable list socket can have 3 possible states, the first is if the socket is the main "server" socket, it is responsible for listening to the client connection, if the main server socket appears in the readable, That means this is the server side already ready to receive a new connection, in order for this main server to be able to handle multiple connections at the same time, in the following code, we set the socket of the main server to non-blocking mode.
The second case is that the socket is an already established connection, it sends the data, and this time you can use RECV () to receive the data it sent, and then put the received data into the queue, so that you can transfer the received data back to the client.
The third case is that the client has been disconnected, so you will be free to receive the data through recv (), so you can close the connection to the client at this time.
For the socket in the writable list, there are several states, if the client is connected to its corresponding queue in the data, the data will be taken out and sent back to the client, otherwise the connection is removed from the output list, so the next cycle of select () call detects that the connection is not in the outputs list, and that the connection is not active
Finally, if an error occurred during the communication with a socket connection, the connection object is deleted in the Inputs\outputs\message_queue and the connection is closed.
#coding: utf8import socketimport sys messages = [' This is the message. ', ' It'll be sent ', ' in parts. ',]server_address = (' localhost ', 10003) # Create A TCP/IP socketsocks = [Socket.socket (socket.af_inet, socket. SOCK_STREAM), Socket.socket (socket.af_inet, socket. Sock_stream),] # Connect the socket to the port where the server is Listeningprint >>sys.stderr, ' connecti Ng to%s port%s '% server_addressfor s in Socks:s.connect (server_address) for message in messages: # Send messages On both sockets for s in Socks:print >>sys.stderr, '%s:sending '%s '% (S.getsockname (), message) S.send (Message) # Read responses on both sockets for s in Socks:data = S.recv (1024x768) Print >> Sys.stderr, '%s:received '%s '% (S.getsockname (), data) if not data:print >>sys.stderr, ' Closin G socket ', S.getsockname () client
The
Client program demonstrates how to manage the socket through select () and interact with multiple connections simultaneously, sending and receiving data to the server through a loop through each socket connection.
Server:starting up in localhost prot 10000waiting for the next eventnew connection from (' 127.0.0.1 ', 54812) waiting for th E Next eventnew connection from (' 127.0.0.1 ', 54813) received "This is the message." From (' 127.0.0.1 ', 54812) waiting for The next eventreceived "the" is the message. "From (' 127.0.0.1 ', 54813) sending" the "and" to (' 127.0.0.1 ', 5 4812) waiting for the next eventoutput queue for (' 127.0.0.1 ', 54812) are emptysending "This is the message." To (' 127.0.0. 1 ', 54813) waiting for the next eventoutput queue for (' 127.0.0.1 ', 54813) are emptywaiting for the next eventreceived "It W Ill is sent "from (' 127.0.0.1 ', 54812) received" It'll be Sent "from (' 127.0.0.1 ', 54813) waiting for the next eventsend ing "it'll be sent" to (' 127.0.0.1 ', 54812) sending "It'll be a sent" to (' 127.0.0.1 ', 54813) waiting for the next event Output queue for (' 127.0.0.1 ', 54812) are emptyoutput queue for (' 127.0.0.1 ', 54813) are emptywaiting for the next eventrece Ived "in parts." From ('127.0.0.1 ', 54812) received "in parts." From (' 127.0.0.1 ', 54813) waiting for the next eventsending "in parts." To (' 127.0.0 .1 ', 54812) sending "in parts." To (' 127.0.0.1 ', 54813) waiting for the next eventoutput queue for (' 127.0.0.1 ', 54812) is E Mptyoutput queue for (' 127.0.0.1 ', 54813) are emptywaiting for the next eventclosing (' 127.0.0.1 ', 54813) after reading no Dataclosing (' 127.0.0.1 ', 54813) after reading no datawaiting for the next eventclient:connecting to localhost port 10000 ( ' 127.0.0.1 ', 54812): Sending "This is the message." (' 127.0.0.1 ', 54813): Sending "This is the message." (' 127.0.0.1 ', 54812): Received "This is the MESSAGE." (' 127.0.0.1 ', 54813): Received "This is the MESSAGE." (' 127.0.0.1 ', 54812): Sending "It'll be Sent" (' 127.0.0.1 ', 54813): Sending "It'll be Sent" (' 127.0.0.1 ', 54812): rece Ived "It'll be SENT" (' 127.0.0.1 ', 54813): Received "It'll be a SENT" (' 127.0.0.1 ', 54812): Sending "in parts." (' 127.0.0.1 ', 54813): Sending "in parts." (' 127.0.0.1 ', 54812): REceived "in PARTS." (' 127.0.0.1 ', 54813): Received "in PARTS." Run results
Poll
Poll was born in 1986 in System V Release 3, and it does not differ substantially from select in nature, but poll does not have a limit on the maximum number of file descriptors.
The disadvantage of poll and select is that an array containing a large number of file descriptors is copied in between the user state and the kernel's address space, regardless of whether the file descriptor is ready, and its overhead increases linearly as the number of file descriptors increases.
In addition, when select () and poll () file descriptors are ready to tell the process, if the process does not have IO operations on it, the next time you invoke select () and poll (), the file descriptors are reported again, so they generally do not lose the ready message. This approach is called horizontal trigger (level triggered).
Calling poll in Python
Select.poll (), returns an poll object that supports registering and unregistering file descriptors.
Poll.register (fd[, Eventmask]) registers a file descriptor that, after registration, can be checked by the poll () method to see if a corresponding I/O event occurs. FD can be an I integer, or a Fileno () method object that returns an integer. If the file object implements Fileno (), it can also be used as a parameter.
Eventmask is a type of event you want to check, which can be a combination of constant Pollin, Pollpri, and Pollout. By default, all 3 event types are checked.
Event constant meaning
Pollin has data read
POLLPRT has data emergency read
Pollout ready output: output does not block
Pollerr some error conditions appear
Pollhup hangs
Pollnval Invalid Request: Description could not be opened
Poll.modify (FD, Eventmask) modifies an already existing FD, which has the same effect as Poll.register (FD, Eventmask). If you try to modify an unregistered FD, it will cause a errno to enoent ioerror.
Poll.unregister (FD) unregisters an FD from the poll object. Attempts to unregister an unregistered FD can cause keyerror.
Poll.poll ([timeout]) to detect a file descriptor that has already been registered. Returns a two-tuple (FD, event) in a potentially empty list,list. FD is a file descriptor, which is the event that corresponds to a file descriptor. If an empty list is returned, the description timed out and no file description have event occurred. The unit of timeout is milliseconds, and if timeout is set, the system waits for the corresponding time. If timeout is default or none, this method will block until an event occurs for the corresponding poll object.
#coding: Utf-8 Import Select, Socketresponse = B "Hello world" ServerSocket = Socket.socket (socket.af_inet, socket. SOCK_STREAM) serversocket.setsockopt (socket. Sol_socket, SOCKET. SO_REUSEADDR, 1) serversocket.bind ((' localhost ', 10000)) Serversocket.listen (1) serversocket.setblocking (0) #poll = Select.poll () Poll.register (Serversocket.fileno (), select. Pollin) connections = {}while true:for fd, event in Poll.poll (): if event = = Select. Pollin:if FD = = Serversocket.fileno (): con, addr = serversocket.accept () poll.re Gister (Con.fileno (), select. Pollin) Connections[con.fileno ()] = con Else:con = connections[fd] data = CON.RECV (1024x768) if Data:poll.modify (Con.fileno (), select. Pollout) Elif Event = = Select. Pollout:con = CONNECTIONS[FD] Con.send (response) Poll.unregister (Con.fileno ()) Con.close ()
Epoll
It was not until Linux2.6 that the kernel directly supported the implementation method, that is, Epoll, which has almost all the advantages of the previous said, is recognized as the best performance of the Linux2.6 multi-channel I/O readiness notification method.
Epoll can support both horizontal and edge triggering (edge triggered, which only tells the process which file descriptor has just become ready, it only says it again, and if we do not take action then it will not be told again, this way is called edge triggering), The performance of edge triggering is theoretically higher, but the code implementation is quite complex.
Epoll also only informs those file descriptors that are ready, and when we call Epoll_wait () to get the ready file descriptor, the return is not the actual descriptor, but a value representing the number of ready descriptors, You just have to go to the Epoll specified array to get the appropriate number of file descriptors, and memory mapping (MMAP) technology is used, which completely eliminates the overhead of copying these file descriptors on system calls.
Another essential improvement is the epoll adoption of event-based readiness notification methods. In Select/poll, the kernel scans all monitored file descriptors only after a certain method is called, and Epoll registers a file descriptor beforehand with Epoll_ctl (), once it is ready based on a file descriptor, The kernel uses a callback mechanism like callback to quickly activate the file descriptor and be notified when the process calls Epoll_wait ().
Calling Epoll in Python
Select.epoll ([sizehint=-1]) returns a Epoll object.
Eventmask
Event constant meaning
Epollin Read Ready
Epollout Write Ready
EPOLLPRI has data emergency read
Epollerrassoc. FD has an error condition occurred
Epollhupassoc. FD occurs pending
EPOLLRT Set Edge Trigger (ET) (default is horizontal trigger)
Epolloneshot is set to One-short behavior, an event is pulled out and the corresponding FD is internally disabled
Epollrdnorm and Epollin are equal
Epollrdband first-read Data band
Epollwrnorm and Epollout are equal
Epollwrband Priority Data Band
Epollmsg neglect
Epoll.close () Closes the file descriptor of the Epoll object.
Epoll.fileno returns the file descriptor number for control FD.
EPOLL.FROMFD (FD) uses the given FD to create a Epoll object.
Epoll.register (fd[, Eventmask]) registers a file descriptor in the Epoll object. (If the file descriptor already exists, it will cause a ioerror)
Epoll.modify (FD, Eventmask) modifies a file descriptor that has already been registered.
Epoll.unregister (FD) unregisters a file descriptor.
Epoll.poll (timeout=-1[, Maxevnets=-1]) waits for an event, the unit of timeout (float) is seconds (second).
#coding: utf8import socket, selectEOL1 = B ' \ n ' EOL2 = B ' \n\r\n ' response = B ' http/1.0 Ok\r\ndate:mon, 1 Jan 1996 01:0 1: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 ((' localhost ', 10000)) Serversocket.listen (1) serversocket.setblocking (0) 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 = = Serversock Et.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 (1024x768) if EOL1 in Requests[fileno] or EOL2 in R Equests[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[f Ileno][byteswritten:] If Len (Responses[fileno]) = = 0:epoll.modify (Fileno, 0) Conne Ctions[fileno].shutdown (socket. SHUT_RDWR) elif Event & Select. EPOLLHUP:epoll.unregister (Fileno) connections[fileno].close () del Connections[fileno]fin Ally:epoll.unregister (Serversocket.fileno ()) Epoll.close () Serversocket.close ()