1. 阻塞IO

最简单直接的IO模型。

调用过程:

进程调用recvfrom系统调用,进程阻塞;等待数据,数据准备完成,

将数据从内核拷贝到用户空间,系统调用返回;进程继续处理。
在这里插入图片描述
优点是简单直接,缺点是进程阻塞,浪费资源。

2. 非阻塞IO

非阻塞IO采用轮询的方式隔一段时间发起系统调用看数据是否就绪,

与阻塞IO不同的是它会马上返回结果。

如果数据准备就绪,就将数据拷贝到用户空间,返回成功。

否则kernal立即返回EWOUDBLOCK这个错误。

因此在准备数据阶段,不会阻塞进程。

优点:进程发起I/O操作时,不会因为数据未就绪而阻塞。

缺点:会多次调用系统调用,消耗CPU。

在这里插入图片描述

3. I/O多路复用

I/O多路复用可以理解为,单线程监听多个文件描述符。

I/O多路利用向kernel发起系统调用(select、poll、epoll)

传入文件描述符和相应的动作read、writekernel去监测,当

其中有文件描述有相应的动作发生,就返回结果。进程再

发起真正的I/O操作。

在这里插入图片描述

4. 信号驱动IO

通过信号来通知数据是否到来,

因此在等待数据到来这个阶段进程不会阻塞。

在信号触发后,调用相应的信号处理函数,开始真正的I/O操作:

将数据从内核拷贝到用户空间,这时候进程会阻塞。

在这里插入图片描述

下面是找得一段例子,sigaction对信号绑定相应的处理函数,相应的处理

函数就是sa.sa_handler

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>

#include <linux/if_tun.h>

extern int errno;

int bridge_term = 0;
int f1, f2;

void sig_io(int sig) 
{
   static char buf[1600];
   register int r;

   while( (r=read(f1, buf, sizeof(buf))) > 0 )
      write(f2, buf, r);
   if( r < 0 && (errno != EAGAIN && errno != EINTR) ) {
      bridge_term = 1;
      return;
   }

   while( (r=read(f2, buf, sizeof(buf))) > 0 )
      write(f1, buf, r);
   if( r < 0 && (errno != EAGAIN && errno != EINTR) ) {
      bridge_term = 1;
      return;
   }
}

int main(int argc, char *argv[])
{
   struct sigaction sa;
   char buf[20];

   if(argc < 2) {
      printf("Usage: bridge tap|tun\n");
      exit(1);
   }

   sprintf(buf,"/dev/%s%d",argv[1],0);
   f1 = open(buf, O_RDWR);

   sprintf(buf,"/dev/%s%d",argv[1],1);
   f2 = open(buf, O_RDWR);

   ioctl(f1, TUNSETNOCSUM, 1);
   ioctl(f2, TUNSETNOCSUM, 1);

   fcntl(f1, F_SETFL, O_NONBLOCK | O_ASYNC);
   fcntl(f2, F_SETFL, O_NONBLOCK | O_ASYNC);

   memset(&sa, 0, sizeof(sa));
   sa.sa_handler = sig_io;
   sigaction(SIGIO, &sa, NULL); 

   while( !bridge_term )
      sleep(1000); 
}


5. 异步I/O

在异步I/O中,I/O操作不会引起进程的阻塞。等待数据和拷贝数据都发生在了内核之中。数据就绪后,发送一个signal到进程中去,进程再进行处理。

在这里插入图片描述

下面给出posix aio的一个例子

先给出aiocb的结构体定义

struct aiocb
{
  int aio_fildes;		/* File descriptor.  */
  int aio_lio_opcode;		/* Operation to be performed.  */
  int aio_reqprio;		/* Request priority offset.  */
  volatile void *aio_buf;	/* Location of buffer.  */
  size_t aio_nbytes;		/* Length of transfer.  */
  struct sigevent aio_sigevent;	/* Signal number and value.  */

  /* Internal members.  */
  struct aiocb *__next_prio;
  int __abs_prio;
  int __policy;
  int __error_code;
  __ssize_t __return_value;

#ifndef __USE_FILE_OFFSET64
  __off_t aio_offset;		/* File offset.  */
  char __pad[sizeof (__off64_t) - sizeof (__off_t)];
#else
  __off64_t aio_offset;		/* File offset.  */
#endif
  char __glibc_reserved[32];
};

再给出示例程序,主要是用aio从文件中进行读取。

我们可以运行

./a.out /dev/stdin /dev/stdin

来观察程序运行的结果。

       #include <fcntl.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <stdio.h>
       #include <errno.h>
       #include <aio.h>
       #include <signal.h>

       #define BUF_SIZE 20     /* Size of buffers for read operations */

       #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

       struct ioRequest {      /* Application-defined structure for tracking
                                  I/O requests */
           int           reqNum;
           int           status;
           struct aiocb *aiocbp;
       };

       static volatile sig_atomic_t gotSIGQUIT = 0;
                               /* On delivery of SIGQUIT, we attempt to
                                  cancel all outstanding I/O requests */

       static void             /* Handler for SIGQUIT */
       quitHandler(int sig)
       {
           gotSIGQUIT = 1;
       }

       #define IO_SIGNAL SIGUSR1   /* Signal used to notify I/O completion */

       static void                 /* Handler for I/O completion signal */
       aioSigHandler(int sig, siginfo_t *si, void *ucontext)
       {
           if (si->si_code == SI_ASYNCIO) {
               write(STDOUT_FILENO, "I/O completion signal received\n", 31);

               /* The corresponding ioRequest structure would be available as
                      struct ioRequest *ioReq = si->si_value.sival_ptr;
                  and the file descriptor would then be available via
                      ioReq->aiocbp->aio_fildes */
           }
       }

       int
       main(int argc, char *argv[])
       {
           struct sigaction sa;
           int s;
           int numReqs;        /* Total number of queued I/O requests */
           int openReqs;       /* Number of I/O requests still in progress */

           if (argc < 2) {
               fprintf(stderr, "Usage: %s <pathname> <pathname>...\n",
                       argv[0]);
               exit(EXIT_FAILURE);
           }

           numReqs = argc - 1;

           /* Allocate our arrays. */

           struct ioRequest *ioList = calloc(numReqs, sizeof(*ioList));
           if (ioList == NULL)
               errExit("calloc");

           struct aiocb *aiocbList = calloc(numReqs, sizeof(*aiocbList));
           if (aiocbList == NULL)
               errExit("calloc");

           /* Establish handlers for SIGQUIT and the I/O completion signal. */

           sa.sa_flags = SA_RESTART;
           sigemptyset(&sa.sa_mask);

           sa.sa_handler = quitHandler;
           if (sigaction(SIGQUIT, &sa, NULL) == -1)
               errExit("sigaction");

           sa.sa_flags = SA_RESTART | SA_SIGINFO;
           sa.sa_sigaction = aioSigHandler;
           if (sigaction(IO_SIGNAL, &sa, NULL) == -1)
               errExit("sigaction");

           /* Open each file specified on the command line, and queue
              a read request on the resulting file descriptor. */

           for (size_t j = 0; j < numReqs; j++) {
               ioList[j].reqNum = j;
               ioList[j].status = EINPROGRESS;
               ioList[j].aiocbp = &aiocbList[j];

               ioList[j].aiocbp->aio_fildes = open(argv[j + 1], O_RDONLY);
               if (ioList[j].aiocbp->aio_fildes == -1)
                   errExit("open");
               printf("opened %s on descriptor %d\n", argv[j + 1],
                       ioList[j].aiocbp->aio_fildes);

               ioList[j].aiocbp->aio_buf = malloc(BUF_SIZE);
               if (ioList[j].aiocbp->aio_buf == NULL)
                   errExit("malloc");

               ioList[j].aiocbp->aio_nbytes = BUF_SIZE;
               ioList[j].aiocbp->aio_reqprio = 0;
               ioList[j].aiocbp->aio_offset = 0;
               ioList[j].aiocbp->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
               ioList[j].aiocbp->aio_sigevent.sigev_signo = IO_SIGNAL;
               ioList[j].aiocbp->aio_sigevent.sigev_value.sival_ptr =
                                       &ioList[j];

               s = aio_read(ioList[j].aiocbp);
               if (s == -1)
                   errExit("aio_read");
           }

           openReqs = numReqs;

           /* Loop, monitoring status of I/O requests. */

           while (openReqs > 0) {
               sleep(3);       /* Delay between each monitoring step */

               if (gotSIGQUIT) {

                   /* On receipt of SIGQUIT, attempt to cancel each of the
                      outstanding I/O requests, and display status returned
                      from the cancelation requests. */

                   printf("got SIGQUIT; canceling I/O requests: \n");

                   for (size_t j = 0; j < numReqs; j++) {
                       if (ioList[j].status == EINPROGRESS) {
                           printf("    Request %zu on descriptor %d:", j,
                                   ioList[j].aiocbp->aio_fildes);
                           s = aio_cancel(ioList[j].aiocbp->aio_fildes,
                                   ioList[j].aiocbp);
                           if (s == AIO_CANCELED)
                               printf("I/O canceled\n");
                           else if (s == AIO_NOTCANCELED)
                               printf("I/O not canceled\n");
                           else if (s == AIO_ALLDONE)
                               printf("I/O all done\n");
                           else
                               perror("aio_cancel");
                       }
                   }

                   gotSIGQUIT = 0;
               }

               /* Check the status of each I/O request that is still
                  in progress. */

               printf("aio_error():\n");
               for (size_t j = 0; j < numReqs; j++) {
                   if (ioList[j].status == EINPROGRESS) {
                       printf("    for request %zu (descriptor %d): ",
                               j, ioList[j].aiocbp->aio_fildes);
                       ioList[j].status = aio_error(ioList[j].aiocbp);

                       switch (ioList[j].status) {
                       case 0:
                           printf("I/O succeeded\n");
                           break;
                       case EINPROGRESS:
                           printf("In progress\n");
                           break;
                       case ECANCELED:
                           printf("Canceled\n");
                           break;
                       default:
                           perror("aio_error");
                           break;
                       }

                       if (ioList[j].status != EINPROGRESS)
                           openReqs--;
                   }
               }
           }

           printf("All I/O requests completed\n");

           /* Check status return of all I/O requests. */

           printf("aio_return():\n");
           for (size_t j = 0; j < numReqs; j++) {
               ssize_t s;

               s = aio_return(ioList[j].aiocbp);
               printf("    for request %zu (descriptor %d): %zd\n",
                       j, ioList[j].aiocbp->aio_fildes, s);
           }

           exit(EXIT_SUCCESS);
       }


参考

wiyi
linux-aio

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐