Linux I/O multiplexing detailed and example _linux

Source: Internet
Author: User
Tags epoll prepare sleep socket

Linux I/O multiplexing

Linux everything is file, whether we are stored on the disk of character files, executable files or our access to the computer's I/O devices are abstracted into a file, such as the standard input device defaults to the keyboard, we operate in the standard input device, In fact, the operation is the default open a file descriptor is 0 of the file, and all software operation hardware need to go through the OS, and OS operation all hardware needs the corresponding driver, this driver is configured in this hardware and the corresponding configuration and use methods. Linux I/O is divided into blocking I/O, non-blocking i/o,i/o multiplexing, signal-driven I/O four kinds. For drivers of I/O devices, there are generally two types of configurations for blocking and non-blocking. One of our most common I/O devices-the keyboard (standard input device) driver is blocked by default.

Multiplexing is designed to enable a process to get the data it wants from multiple blocking I/O and continue to perform the next task. The main idea is to monitor multiple file descriptors at the same time, if the set state of the file descriptor is triggered, continue to execute the process, if no one file descriptor set state is triggered, the process into sleep

One of the main uses of multiplexing is to implement "I/O multiplexing concurrent Servers", which is less expensive and more suitable for Web servers than multithreading concurrency or multiprogramming.

Blocking I/O

Blocking I/O is when the process attempts to access the I/O device and the device is not ready, and the device driver passes through the kernel to allow the process that is trying to access to sleep. One benefit of blocking I/O is that it can greatly save CPU time, because once a process attempts to access a blocked I/O that is not ready, it enters the sleep state, and the process into the sleep state is not in the kernel's process scheduling list until the target i/ O is ready to wake up and join the scheduling list, which saves CPU time. Of course, blocking I/O also has its inherent disadvantage, if the process attempts to access a blocking I/O, but whether the access is successful and does not have a decisive impact on the next task, then the direct access to sleep state will obviously delay the completion of its task.
Typical default blocking IO has standard input devices, socket devices, plumbing devices, and so on, when we use gets (), scanf (), read () and so on to request these IO sometimes IO and no data inflow, will cause the process of sleep.

Suppose a process wants to read data from any of the three pipes and display the pseudocode as follows

Read (pipe_0,buf,sizeof (BUF));    Sleep
print buf;
Read (pipe_1,buf,sizeof (BUF));
Print buf;
Read (pipe_2,buf,sizeof (BUF));
Print buf;

Because the pipeline is blocking I/O, if the pipe_0 does not have data inflow, the process enters the sleep state at the first read () and is not read even if Pipe_1 and pipe_2 have data inflows.
If we reset the blocking properties of the pipe using the following code, obviously, if three pipelines do not have data inflow, then the process will not be able to get the requested data to continue execution, if the data is important (so we want to use blocking I/O), the result will be very bad, instead of polling but a lot of CPU time.

int fl = FCNTL (PIPE_FD, F_GETFL);
Fcntl (PIPE_FD, F_SETFL, FL | O_nonblock);

How to let a process monitor three of pipelines simultaneously, one of which has data to continue without sleep, and if there is no data inflow and sleep, is the problem that multiplexing technology needs to solve.

Non-blocking I/O

Non-blocking I/O is when a process tries to access an I/O device, and the data that gets the request from it returns and continues to perform the next task. , but it's great for the request to be successful in the I/O request that will not affect the next task. However, if you access a non-blocking I/O, but if the request fails to have a fatal effect on the next task of the process, the most outrageous is to use while (1) {read ()} for polling. Obviously, this approach takes up a lot of CPU time.

Select mechanism

Select is a very "old" synchronous I/O interface, but it provides a good way of I/O multiplexing

Model

Fd_set   //Create Fd_set object in the future to increase or decrease the FD
Fd_zero ()/  //Empty Fd_set Object Fd_set ()// 
/To add an FD to the Fd_set object Select ()  //monitor the file descriptor in the Fd_set object
pselect ()  ////Set the signal mask before monitoring
fd_isset ()//test if FD belongs to Fd_set object
fd_ CLR ()  //Remove FD from the Fd_set object

Note:

The first parameter Nfds of select refers to the largest file descriptor +1 in the collection, because the Select will traverse the entire file descriptor chart without discrimination until the target is found, and the file descriptor starts at 0, so it is the largest file descriptor + 1 times in the collection.

The previous article led to inefficiencies in this mechanism, and if you need to monitor a file descriptor of 0 and 100, then iterate through 101 times each time

Select () modifies fd_set each time it is returned, and if you want to cycle select (), you need to first prepare the initial fd_set

Example _I/O multiplex Concurrent Server

For the programming model of the server itself, see TCP/IP Protocol server model and UDP/IP protocol server model This is only part of the model that uses select to implement pseudo parallelism

#define BUFSIZE #define MAXNFD 1024 int main () {/*********** Server LISTENFD is ready for **************/fd_set;
  Fd_set Writefds;
  Fd_zero (&readfds);
  Fd_zero (&writefds);

  Fd_set (LISTENFD, &readfds);
  Fd_set Temprfds = Readfds;
  Fd_set Tempwfds = Writefds;


  int maxfd = LISTENFD;
  int nready;
  Char Buf[maxnfd][bufsize] = {0};
    while (1) {Temprfds = Readfds;

    Tempwfds = Writefds; Nready = Select (Maxfd+1, &temprfds, &tempwfds, NULL, NULL) if (Fd_isset (LISTENFD, &temprfds)) {/
      
      /If the supervisor hears the LISTENFD on the Accept int sockfd = accept (LISTENFD, struct sockaddr*) &clientaddr, &len);
      Adding the SCOKFD of the new accept to the listening set and keeping maxfd as the maximum FD fd_set (SOCKFD, &readfds);
      
      MAXFD = maxfd>sockfd?maxfd:sockfd;
    If the opinion examines the Nready FD, there is no need to wait any longer, directly to the next loop if (--nready==0) continue;
    int fd = 0; Traverses the file descriptor, processing the received message for (;fd<=maxfd; fd++) {if (fd = = LISTENFD) continuE
        if (Fd_isset (FD, &temprfds)) {int ret = read (FD, BUF[FD], sizeof buf[0]);
          if (0 = ret) {//Client link has disconnected close (FD);
          FD_CLR (FD, &readfds);
          if (MAXFD==FD)--maxfd;
        Continue 
      ////Add FD to the Listener writable set Fd_set (FD, &writefds);
        //found the FD of the socket that received the message, and then added it to the monitor write Fd_set//will monitor if (Fd_isset (FD, &tempwfds)) in the next while () loop.
        int ret = write (FD, BUF[FD], sizeof buf[0]);
        printf ("Ret%d:%d\n", FD, ret);
      FD_CLR (FD, &writefds);
}} close (LISTENFD);

 }

Poll mechanism

Poll is a select based improvement mechanism proposed by System V, which is redesigned for many of the obvious flaws in select, including only traversal of several file descriptors, no backup fd_set, etc.

Model

struct POLLFD  FDS   //Create an array of POLLFD types
fds[0].fd//        to fds[0] put the FD
fds[0].events      //To fds[to be monitored 0] Put the triggering event of the FD to be monitored
  pollin       //i/o have input
  pollpri       //have emergency data need to read
  pollout       //i/o
  writable Pollrdhup      //streaming socket connection disconnect or socket in semi-shutdown state
  Pollerr       //Error condition (output only)
  Pollhup       //suspend (for output only)
  Pollnval      //Invalid request: FD is not turned on (output only)

Example _I/O multiplex Concurrent Server

/* ... * */int main () {/* * ... */struct POLLFD myfds[maxnfd] = {0};
  MYFDS[0].FD = LISTENFD;
  Myfds[0].events = Pollin;
  
  int maxnum = 1;
  int nready;
  Prepare two-dimensional array buf, each FD using a BUF row, data jamming char buf[maxnfd][bufsize] = {0};
    while (1) {//poll directly returns the number of FD triggered by Event Nready = Poll (Myfds, Maxnum,-1) int i = 0; for (;i<maxnum; i++) {//poll indicates that the corresponding binary position is set///If the following conditions are true, the Pollin bit in revent[i] is already 1 if (Myfds[i].reve NTS & Pollin) {if (myfds[i].fd = = listenfd) {int sockfd = accept (LISTENFD, (struct sockaddr*) &cli
          ENTADDR, &len);
          Adding the SCOKFD of the new accept to the listening set myfds[maxnum].fd = SOCKFD;
          Myfds[maxnum].events = Pollin;
          
          maxnum++;
        If the opinion examines the Nready FD, direct the Next loop if (--nready==0) continue;
          else{int ret = read (MYFDS[I].FD, buf[myfds[i].fd], sizeof buf[0]);
            
    if (0 = ret) {//If the connection is disconnected close (MYFDS[I].FD);         Initializes all file descriptors for the file descriptor to 1//close The file descriptor is also marked as-1//opens a new description characters searches the table for the first -1//open ( 
            This is the way to implement always use the smallest FD//here to demonstrate and not use this mechanism MYFDS[I].FD =-1;
          Continue
        } myfds[i].events = Pollout; } else if (Myfds[i].revents & pollout) {int ret = write (MYFDS[I].FD, buf[myfds[i].fd), sizeof BUF
        [0]);
      Myfds[i].events = Pollin;
}} close (LISTENFD);

 }

Epoll

Epoll, a more robust interface based on poll, is also the multiplex technology used by the mainstream Web server, Epoll a major feature is the support of Epollet (Edge Trigger) and epolllt (horizontal trigger), which means that if there is data in the buffer after reading, So as long as the end of the reading, the rest of the data will be discarded, the latter said the data will not be discarded, the next time you read it, the default is Epolllt

Model

Epoll_create ()     ///Create Epoll object
struct epoll_event   //Prepare event structure body and event structure body array
  event.events
  event.data.fd ...
Epoll_ctl ()       ///Configure Epoll Object
epoll_wait ()      //Monitor FD in Epoll object and its corresponding event

Example _I/O multiplex Concurrent Server

/* ... */int main () {/*////* Create Epoll Object */int epoll_fd = epoll_create (1024);
  Prepares an event structure body struct Epoll_event event = {0};
  Event.events = Epollin;  EVENT.DATA.FD = LISTENFD;
  Data is a common body, in addition to the FD can return other information//ctl is to monitor LISTENFD whether an event is triggered///If it happens, bring the event through wait.
  
  So, if the event does not indicate FD, we will not know which FD Epoll_ctl (EPOLL_FD, Epoll_ctl_add, LISTENFD, &event) in the future;
  struct Epoll_event revents[maxnfd] = {0};
  int nready;
  Char Buf[maxnfd][bufsize] = {0};
    while (1) {//wait Returns the number of occurrences of the waiting event//and places the corresponding event in an array of event types Nready = epoll_wait (epoll_fd, Revents, MAXNFD,-1)
    int i = 0; for (;i<nready; i++) {//wait Indicates the occurrence of the corresponding event by setting the appropriate bit in events//If input is available, the following result should be true if (revents[i].events &am P Epollin) {//If LISTENFD has data entry if (REVENTS[I].DATA.FD = = listenfd) {int sockfd = accept (LISTENFD,
          struct sockaddr*) &clientaddr, &len);
          struct Epoll_event event = {0};
          Event.events = Epollin; EvenT.DATA.FD = SOCKFD;
        Epoll_ctl (EPOLL_FD, Epoll_ctl_add, SOCKFD, &event);
          else{int ret = read (REVENTS[I].DATA.FD, buf[revents[i].data.fd], sizeof buf[0]);
            if (0 = ret) {close (REVENTS[I].DATA.FD);
          Epoll_ctl (EPOLL_FD, Epoll_ctl_del, REVENTS[I].DATA.FD, &revents[i]);
          } revents[i].events = Epollout;
        Epoll_ctl (EPOLL_FD, Epoll_ctl_mod, REVENTS[I].DATA.FD, &revents[i]); } else if (Revents[i].events & epollout) {int ret = write (REVENTS[I].DATA.FD, buf[revents[i].data
        . FD], sizeof buf[0]);
        Revents[i].events = Epollin;
      Epoll_ctl (EPOLL_FD, Epoll_ctl_mod, REVENTS[I].DATA.FD, &revents[i]);
}} close (LISTENFD);



 }

Thank you for reading, I hope to help you, thank you for your support for this site!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.