In the past, server programs were written into multiple threads, without considering other methods or the best method;
A few days ago, some people said that using ports for Windows and epoll for Linux is highly efficient.
In other environments, select is used. It seems that many people do not want to adopt the multi-thread method, maybe it is scared by the legendary thread synchronization;
I personally prefer multithreading, which not only Monitors multiple ports, but also isolates the business logic to facilitate debugging and maintenance.
Bytes ----------------------------------------------------------------------------------------------------
Understanding these things requires an environment to describe the problem;
That is, my program needs to process two or more file descriptors at the same time;
All these methods can be solved. In addition to multithreading, non-blocking I/O is required;
FD = open (filename, o_rdonly | o_nonblock ); |
The Round-Robin method can be used as a joke. No one can use this method.
# Define blksize 1024 Int nbytes; Char Buf [blksize]; Int sign_done = 0; While (! Sign_done ){ Nbytes = read (FD, Buf, blksize ); If (nbytes <0) & (errno! = Eagain) & (errno! = Eintr) |! Nbytes) Do_something_process_error (); Else Do_something_process_data (); // If shocould end loop. Set sign_done to 1; } |
FD is non-blocking. If you cannot read anything, it will always read and occupy CPU resources. There is no value in addition to waste;
The second method is to use the asynchronous I/O of the sigpoll signal;
Sigpoll is the signal of Sistem V, and sigio is used in BSD;
When the system knows that something needs to be read, it sends a sigpoll signal to notify you;
Int sigpoll_received = 0; Static void poll_handler (INT signo) {// This is the signal processing function of sigpoll. Sigpoll_received = 1; } |
Int fd1, fd2; Int fd1_done = 0, fd2_done = 0; Sigset_t oldmask, newmask, zeromask; Struct sigaction newact; Fd1 = open (filename1, o_rdonly | o_nonblock ); Fd2 = open (filename2, o_rdonly | o_nonblock ); Sigemptyset (& newmask ); Sigaddset (& newmask, sigpoll ); Sigprocmask (sig_block, & newmask, & oldmask); // block the sigpoll signal before actually reading Newact. sa_handler = poll_handler; Sigemptyset (& newact. sa_mask ); Newact. sa_flags = 0; Sigaction (sigpoll, & newact, null); // install a processor for the sigpoll Signal IOCTL (fd1, I _setsig, s_input | s_hangup); // sets the sigpoll signal when something is read. IOCTL (fd2, I _setsig, s_input | s_hangup ); Sigemptyset (& zeromask ); While (! Fd1_done |! Fd2_done ){ If (! Fd1_done) Deal_with_fd1_and_set_fd1done_sign; If (! Fd12done) Deal_with_fd2_and_set_fd1done_sign; While (! Sigpoll_received &&(! Fd1_done |! Fd2_done )) Sigsuspend (& zeromask); // when there is nothing to read, blocking is here until the sigpoll signal comes Sigpoll_received = 0; } |
The third method is to use the Select system call;
Select is a BSD system, but most systems support it, probably because of spec1170;
# Include # Include Int select (INT NFDs, fd_set * readfds, fd_set * writefds, fd_set * limit TFDs, struct timeval * timeout ); |
The first parameter, NFDs, is the number of mask bits to be detected in the file descriptor set, because the previously descriptor set was implemented as an integer shielding code;
That is, each bit represents a descriptor, but that method cannot process more than 32 Descriptors (originally, when foreigners do not need to think about problems );
Currently, descriptors are generally expressed in the bitfield of an integer array. The NFDs value is greater than the actual number of descriptors to be detected, why do you want to see the source code of the system call );
The next three (fd_set *) types are the actual descriptor sets, and the three separate sets are read monitoring, write monitoring, and exception monitoring;
Finally, the timeout time is returned by the time function. Many people say this can be used as a timer, which is better than alarm;
-1 is returned when the select is interrupted and errno is set to eintr;
Fd_set readset; Int maxfd, fd1, fd2; Maxfd = fd1; If (fd2> maxfd) maxfd = fd2; // find the largest Descriptor While (1 ){ Fd_zero (& readset);/* because no data in the descriptor set is cleared when the SELECT statement is returned, the descriptor set must be reset before each select operation */ Fd_set (fd1, & readset ); Fd_set (fd2, & readset ); If (select (maxfd + 1, & readset, null, null) =-1) & (errno! = Eintr )) /* Deal with Error */ Else { If (fd_isset (fd1, & readset)/* fd_isset (2) check whether the specified descriptor is set */ /* Get and process data * // * the problem here is that when processing the data, the process will be blocked here, and there is no way to overlap the processing of data on other ports */ If (fd_isset (fd2, & readset )) /* Get and process */ } } |
Poll is something of svr4, almost the same as select, just in a different way for descriptors;
# Include # Include Int poll (struct pollfd * FDS, size_t NFDs, int timeout ); Struct pollfd { Int FD;/* file descriptor */ Short events;/* Waiting events */ Short revents;/* events actually occurred */ };
|
Poll uses the pollfd structure array to provide the descriptor and uses the message mask of the input and output respectively, so that it does not need to be set once before each call as in the SELECT statement.
Struct pollfd * FDS; Short errmsk; Int idx; Errmsk = pollerr | pollhup; FDS = (void *) calloc (num_fds, sizeof (struct pollfd )); For (idx = 0; idx (FDS + idx)-> FD = *****;/* Set descriptor */ (FDS + idx)-> events = pollrdnorm;/* set the event to be monitored */ (FDS + idx)-> revents = 0; } While (Situation ){ If (num_ret = poll (FDS, num_fds, inftim) =-1) & (errno! = Eintr )) Break; For (idx = 0; idx <num_fds & num_ret> 0; idx ++ ){ If (FDS + idx)-> events & (FDS + idx)-> revents) {/* a message exists and an event occurs instead of an error */ If (FDS + idx)-> revents & errmsk ){ /* Error */ (FDS + idx)-> revents = 0;/* clear */ } Else if (FDS + idx)-> revents & pollrdnorm ){ /* Process data normally */ } } } } |