The Select module in Python focuses on I/O multiplexing, provides a select poll Epoll three methods (where the latter two are available in Linux, Windows supports select only), and also provides the Kqueue method (FreeBSD system)
Select method
The process specifies which events of the kernel listens to which file descriptors (up to 1024 FD), and when no file descriptor events occur, the process is blocked, and when one or more file descriptor events occur, the process is awakened.
When we call Select ():
1. The context switch is converted to the kernel state
2. Copy fd from user space to kernel space
3. The kernel iterates through all FD to see if its corresponding event occurred
4, if it does not occur, the process is blocked, when the device driver generates an interrupt or timeout time, wake up the process, and then traverse again
5. Return to FD after traversal
6. Copy fd from kernel space to user space
Fd:file Descriptor File Descriptor
Fd_r_list, fd_w_list, fd_e_list = Select.select (rlist, Wlist, xlist, [timeout])
Parameters: accepts four parameters (the first three must be)
Rlist:wait until ready for reading
Wlist:wait until ready for writing
Xlist:wait for an "exceptional condition"
Timeout: Timeout period
Return value: three lists
The Select method is used to monitor the file descriptor (when a file descriptor condition is not met, select blocks), and when a file descriptor state changes, three lists are returned
1. When the FD in the parameter 1 sequence satisfies the "readable" condition, it gets the changed FD and adds it to the Fd_r_list
2. When the parameter 2 sequence contains FD, all FD in the sequence is added to the fd_w_list
3. When the FD in the parameter 3 sequence has an error, the FD that has the error is added to the fd_e_list
4. When the timeout time is empty, select will block until the handle of the listener changes
When the time-out = N (positive integer), if there is no change in the listening handle, select blocks n seconds, then returns three empty lists, and executes directly if the handle of the listener changes.
Instance:
Using Select to implement a concurrent service side
Import Socketimport Select s = Socket.socket () s.bind ((' 127.0.0.1 ', 8888)) S.listen (5) r_list = [S,]num = 0while true:rl, WL, Error = Select.select (r_list,[],[],10) num+=1 print (' Counts is%s '%num) print ("RL's length is%s"%len (RL)) for FD in rl:< C0/>if FD = = s: conn, addr = fd.accept () r_list.append (conn) msg = CONN.RECV (+) Conn.sendall ( ' First----%s '%conn.fileno ()). Encode ()) else: try: msg = FD.RECV (+) fd.sendall (' second '. Encode () ) except Connectionabortederror: r_list.remove (FD) S.close ()
Import socket flag = 1s = socket.socket () s.connect ((' 127.0.0.1 ', 8888)) While flag:input_msg = input (' input>>> ') if input_msg = = ' 0 ': Break S.sendall (Input_msg.encode ()) msg = S.RECV (1024x768) print (Msg.decode ()) S.close ()
On the server side we can see that we need to keep calling Select, which means:
1 file descriptors are time-consuming to copy in user space and kernel space when there are too many file descriptors
2 kernel Traversal of file descriptors is also a waste of time when there are too many file descriptors
3 Select maximum of only 1024 file descriptors supported
Poll and select are not big, this article does not introduce
Epoll Method:
Epoll has improved the select:
1. The Epoll solution is in the Epoll_ctl function. Each time a new event is registered to the Epoll handle, all FD is copied into the kernel instead of being duplicated at epoll_wait. Epoll guarantees that each FD will be copied only once throughout the process.
2. Epoll will iterate over the specified FD at EPOLL_CTL (which is necessary) and specify a callback function for each FD, which will be called when the device is ready to wake the waiting queue, and this callback function will add the ready FD to a ready list. Epoll_wait's job is actually to see if there's an FD in readiness list.
3. Epoll no additional restrictions on file descriptors
Select.epoll (Sizehint=-1, flags=0) Create Epoll Object
Epoll.close ()
Close the control file descriptor of the Epoll object. To close the files descriptor for Epoll objects
Epoll.closed
True If the Epoll object is closed. Detects if Epoll objects are closed
Epoll.fileno ()
Return the file descriptor number of the control FD. Returns the filename descriptor of the Epoll object
EPOLL.FROMFD (FD)
Create an Epoll object from a given file descriptor. Creates a Epoll object based on the specified FD
Epoll.register (fd[, Eventmask])
Register a FD descriptor with the Epoll object. Register FD and corresponding events to the Epoll object
Epoll.modify (FD, Eventmask)
Modify a registered file descriptor. Modifying an FD Event
Epoll.unregister (FD)
Remove a registered file descriptor from the Epoll object. Unregister
Epoll.poll (Timeout=-1, Maxevents=-1)
Wait for events. Timeout in seconds (float) is blocked until the registered FD event occurs, and a dict is returned, in the format: {(fd1,event1), (Fd2,event2),...... (FDN,EVENTN)}
Event:
Epollin Available for read readable state character 1
Epollout Available for write writable State character 4
EPOLLPRI Urgent data for Read
Epollerr error condition happened on the Assoc. FD occurs with a state character of 8
Epollhup hang up happened on the Assoc. FD Pending Status
Epollet Set Edge Trigger behavior, the default is level Trigger behavior defaults to horizontal trigger, the edge trigger after setting the event
Epolloneshot Set one-shot behavior. After one event was pulled out, the FD is internally disabled
Epollrdnorm equivalent to Epollin
Epollrdband Priority Data Band can be read.
Epollwrnorm equivalent to Epollout
Epollwrband priority data is written.
Epollmsg ignored.
Horizontal and Edge triggering:
Level_triggered (horizontal trigger, sometimes also called conditional trigger): When a read-write event occurs on a monitored file descriptor, epoll.poll () notifies the handler to read and write. If the data is not read and written all at once (such as the read-write buffer is too small), then the next time you call Epoll.poll (), it will also notify you to read and write on the file descriptor to continue reading and writing, of course, if you have not read and write, it will always notify you!!! If the system has a large number of ready file descriptors that you do not need to read and write, and they return each time, this can greatly reduce the efficiency of the handler retrieving the ready file descriptor of its own interest!!! The advantages are obvious: stable and reliable
Edge_triggered (Edge trigger, sometimes called State trigger): When a read-write event occurs on a monitored file descriptor, epoll.poll () notifies the handler to read and write. If you do not read and write all the data this time (such as the read-write buffer is too small), then the next time you call Epoll.poll (), it will not notify you, that is, it will only notify you once, until the file descriptor on the second read-write event will notify you!!! This mode is more efficient than the level trigger, and the system will not be flooded with ready file descriptors that you don't care about!!! Cons: Unreliable under certain conditions
Epoll instances:
Import Socketimport Select s = Socket.socket () s.bind ((' 127.0.0.1 ', 8888)) S.listen (5) Epoll_obj = Select.epoll () Epoll_ Obj.register (S,select. Epollin) connections = {}while true:events = Epoll_obj.poll () for FD, event in events: print (fd,event) if fd = = S. Fileno (): conn, addr = s.accept () Connections[conn.fileno ()] = conn Epoll_obj.register (conn,select. Epollin) msg = CONN.RECV ($) conn.sendall (' OK '. Encode ()) else: try: fd_obj = connections[fd] msg = FD_OBJ.RECV (fd_obj.sendall) (' OK '. Encode ()) except Brokenpipeerror: Epoll_ Obj.unregister (FD) connections[fd].close () del connections[fd] S.close () epoll_obj.close ()
Import socket flag = 1s = socket.socket () s.connect ((' 127.0.0.1 ', 8888)) While flag:input_msg = input (' input>>> ') if input_msg = = ' 0 ': Break S.sendall (Input_msg.encode ()) msg = S.RECV (1024x768) print (Msg.decode ()) S.close ()