io multiplexing
Let's take a look at the server programming model first, the client sends a request server to produce a process to service it, each time a customer request produces a process to serve, but the process is not infinite system generation, so in order to solve a large number of client access problems, the introduction of IO multiplexing technology.
that is, a process can service multiple customer requests at the same time.
That is, the "media" for IO reuse is a process (the exact reuse is select and poll, because processes are also implemented by calling Select and poll), multiplexing a process (select and poll) to service multiple IO, Although the IO that the client sends is concurrent, the read and write data required for IO is mostly not ready, thus, a function (select and poll) can be used to listen for the state of the data required by IO, and the process will serve such IO once the IO has data to read and write.
Io multiplexing refers to a kernel that notifies the process if one or more IO conditions specified by the process are ready to be read.
IO multiplexing is suitable for the following occasions:
1. When a client processes multiple descriptors (typically interactive input and network socket interfaces), I/O multiplexing must be used.
2. When a client handles multiple sets of interfaces at the same time, this is possible, but rarely occurs.
3. If a TCP server handles both the listener socket and the connected socket interface, I/O multiplexing is generally used.
4. If a server is to handle TCP, but also to handle UDP, generally use I/O multiplexing.
5. If a server is to handle multiple services or multiple protocols, I/O multiplexing is generally used.
Compared with multiple processes and multithreading technology, I/O multiplexing technology has the greatest advantage of low system overhead, the system does not have to create processes/threads, and do not have to maintain these processes/threads, thereby greatly reducing the overhead of the system. Select function
This function allows the process to instruct the kernel to wait for any one of the events to occur and to wake him up only after one or more times have occurred or after a specified amount of time.
#include <sys/select.h>
#include >sys/time.h>
int Select (int maxfdp1,fd_set *readset,fd_set * Writeset,fd_set *exceptset,const struct timeval *timeout);
return Value:
If there is a ready descriptor to return its number, if the timeout is 0, if the error is-1 parameter first parameter--int maxfdp1
The first parameter MAXFDP1 specifies the number of descriptive words to be tested .
Its value is the maximum descriptor to be tested plus 1 (so that the parameter is named MAXFDP1), the description of the word 0, 1, 2...maxfdp1-1 will be tested.
Because the file descriptor starts at 0. fd_set *readset fd_set *writeset fd_set *exceptset
The middle three parameters Readset, Writeset, and Exceptset Specify the descriptor we want the kernel to test for read, write, and exception conditions.
If you are not interested in one of the conditions, you can set it to a null pointer. struct Fd_set can be understood as a collection that holds a file descriptor that can be set through the following four macros:
void Fd_zero (Fd_set *fdset);
Empties
the set void fd_set (int FD, fd_set *fdset);
Adds a given file descriptor to the set
void fd_clr (int FD, fd_set *fdset);
Deletes a given file descriptor from the collection
int fd_isset (int FD, fd_set *fdset);
const struct Timeval *timeout
Timeout tells the kernel how long it will take to wait for any one of the specified descriptors to be ready. Its timeval structure is used to specify the number of seconds and microseconds for this period of time.
struct timeval{
long tv_sec; Seconds
long tv_usec; Microseconds
};
This parameter has three possible possibilities:
1. Always wait: Returns only if a descriptive word is ready for I/O. To do this, set the parameter to null pointer.
2. Wait a fixed time: Returns when a descriptor is ready for I/O, but does not exceed the number of seconds and microseconds specified in the TIMEVAL structure to which the parameter points.
3. Do not wait at all: return immediately after checking the descriptor, which is called polling. To do this, the parameter must point to a timeval structure, and the timer value must be 0. Call procedure for select function
(1) using Copy_from_user to copy fd_set from user space to kernel space
(2) Registration callback function __pollwait
(3) traversing all FD
Call its corresponding poll method (for the socket, this poll method is sock_poll,sock_poll called to Tcp_poll,udp_poll or Datagram_poll according to the situation)
(4) Taking Tcp_poll as an example, its core implementation is __pollwait, which is the callback function registered above.
(5) The main task of __pollwait is to hang current (currently process) in the waiting queue of the device, different devices have different waiting queues, for Tcp_poll, the waiting queue is sk->sk_ Sleep (note that suspending a process to the wait queue does not mean that the process is asleep). When the device receives a message (a network device) or fills out the file data (a disk device), it wakes up the device waiting for the process to sleep on the queue, and then the current is awakened.
(6) The poll method returns a mask mask that describes whether a read-write operation is ready, and assigns a value to fd_set based on the mask mask.
(7) If all the FD has been traversed and no mask mask has been returned, then Schedule_timeout is invoked to call the Select's process (that is, current) to sleep. When a device driver takes its own resource to read and write, it wakes up its process of waiting for the queue to sleep. If there is more than a certain timeout (schedule_timeout specified), or no one wakes, the process that invokes the select will be awakened to the CPU again, and then iterate through the FD to determine if there is a ready fd.
(8) copy fd_set from kernel space to user space. Select sleep and wake process
Select cleverly utilizes the wait queue mechanism to allow user processes to sleep properly without resources to read/write, and to wake up when resources are readable/writable. Select the sleep process
The select loops through the poll function of the driver for all file descriptors in the Fd_ set that it monitors.
The poll function provided by the driver inserts the user process that invokes the select into the waiting queue for the device-driven corresponding resource (such as the read/write wait queue), and then returns a bitmask telling the select Current resource which is available.
When the Select Loop traverses the poll function for the file descriptor specified in all Fd_set, if none of the resources are available (that is, there is no file to operate on), the select lets the process sleep, waits until the resource is available, and the process is awakened (or timeout) Continue to follow down. Select Wakeup Process
The process that wakes up the process is typically implemented within the device driver of the monitored file.
The driver maintains a wait queue that reads and writes to its own resources. This resource waits for the process on the queue when the device driver discovers that its resources become writable and that there is a process sleep on the waiting queue for that resource. The disadvantage of select
1. Every time you call Select, you need to copy the FD collection from the user state to the kernel state, which can be very large in the case of FD.
2. At the same time, each call to select will need to traverse the kernel to pass through all the FD, this cost in the FD is very large
3.select The number of file descriptors supported is too small, the default is 1024 the server code written using the Select function is as follows:
int fds[128];
const int len = 128;
int startup (char *ip,int port) {int listen_sock = socket (af_inet,sock_stream,0);
struct SOCKADDR_IN local;
local.sin_family = af_inet;
LOCAL.SIN_ADDR.S_ADDR = inet_addr (IP);
Local.sin_port = port;
Bind (&listen_sock, (struct sockaddr*) &local,sizeof (local));
if (Listen (listen_sock,5) < 0) {perror ("listen");
return listen_sock;
int main () {if (argc!= 3) {usage (argv[0]);
Exit (1);
int i = 0;
for (; i < len;i++) {Fds[i] =-1;
int listen_sock = Startup (Argv[1],atoi (argv[2));
Fd_set RfDs;
Fds[0] = Listen_sock;
int done;
while (!done) {int max_fd =-1;
Fd_zero (&rfds); for (int i = 0;i < len;i++) {if (Fds[i]!=-1) {Fd_set (Fds[i],&rfds
); } MAX_FD = Fd[i] > max_fd?
FD[I]:MAX_FD; } struct Timeval timeout = {5,0};
Switch (select (max_fd+1,&rfds,null,null,null)) {case 0:printf ("timeout\n");
Break
Case-1: Perror ("select");
Break Default: {if (Fd_isset (Listen_sock,&rfds)) {struct SO
Ckaddr_in peer;
int len = sizeof (peer);
int new_fd = accept (listen_sock,\ (struct sockaddr*) &peer,&len);
if (new_fd > 0) {printf ("Get a new client->%s:%d\n", \
Inet_addr (PEER.SIN_ADDR), Ntohs (Peer.sin_port));
for (int i = 0; i < len;i++) {if (fds[i] = = 1)
{Fds[i] = NEW_FD; Break } if (i = = len) {CLO
SE (NEW_FD);
}} else {char buf[1024]; for (int i = 0; i < len;i++) {if (i!= 0 && fd_isset
(fds[i],\ &rfds))
{ssize_t _s = read (fds[i],buf,sizeof (BUF)) if (_s > 0)
{buf[_s] = ' I ';
printf ("client:%s\n", buf); else if (_s = 0) {printf ("Clien
T:%d is close\n ", fds[i]);
Close (Fds[i]); Fds[i] =-1;
else perror ("read");
Break in}}}; }
}
}