APUE,TLPI,UNP读书笔记——linux IO模型以及IO复用

异步,同步,阻塞,非阻塞的区别:

参考:知乎

阻塞指线程被挂起,关注程序调用结果返回的状态

同步和异步指的是调用后是否立即返回,是否得到结果,关注的是消息机制


linux上的五种IO模型:

1.阻塞式IO

APUE,TLPI,UNP读书笔记——linux IO模型以及IO复用_第1张图片

程序一直阻塞在读写函数的调用上,一直等待内核数据复制完成。阻塞同步

2.非阻塞式IO

APUE,TLPI,UNP读书笔记——linux IO模型以及IO复用_第2张图片

进程反复调用recvform轮询,直到返回成功指示。非阻塞同步

3.IO复用

APUE,TLPI,UNP读书笔记——linux IO模型以及IO复用_第3张图片

进程阻塞在select调用,但IO可读写时,调用读写处理。

4.信号驱动IO

APUE,TLPI,UNP读书笔记——linux IO模型以及IO复用_第4张图片

调用前准备信号处理,在IO事件发生时递交信号,交由信号处理。

5.异步IO

APUE,TLPI,UNP读书笔记——linux IO模型以及IO复用_第5张图片

异步IO是由内核通知我们IO何时完成。


IO复用:

select:select

       #include <sys/select.h>

       int pselect(int nfds, fd_set *restrict readfds,
           fd_set *restrict writefds, fd_set *restrict errorfds,
           const struct timespec *restrict timeout,
           const sigset_t *restrict sigmask);
       int select(int nfds, fd_set *restrict readfds,
           fd_set *restrict writefds, fd_set *restrict errorfds,
           struct timeval *restrict timeout);
       
       void FD_CLR(int fd, fd_set *fdset);
       int FD_ISSET(int fd, fd_set *fdset);
       void FD_SET(int fd, fd_set *fdset);
       void FD_ZERO(fd_set *fdset);

poll:poll

       #include <poll.h>
       int poll(struct pollfd fds[], nfds_t nfds, int timeout);

           #include <stropts.h>
           #include <poll.h>
           ...
           struct pollfd fds[2];
           int timeout_msecs = 500;
           int ret;
               int i;

           /* Open STREAMS device. */
           fds[0].fd = open("/dev/dev0", ...);
           fds[1].fd = open("/dev/dev1", ...);
           fds[0].events = POLLOUT | POLLWRBAND;
           fds[1].events = POLLOUT | POLLWRBAND;

           ret = poll(fds, 2, timeout_msecs);

           if (ret > 0) {
               /* An event on one of the fds has occurred. */
               for (i=0; i<2; i++) {
                   if (fds[i].revents & POLLWRBAND) {
                   /* Priority data may be written on device number i. */
           ...
                   }
                   if (fds[i].revents & POLLOUT) {
                   /* Data may be written on device number i. */
           ...
                   }
                   if (fds[i].revents & POLLHUP) {
                   /* A hangup has occurred on device number i. */
           ...
                   }
               }
           }
对于select,poll何时能够让文件描述符变为就绪。SUSv3规定:如果对IO函数调用不会阻塞,无论是否能够传输数据,就指定为就绪。

select,poll的比较:

1.select的fd_set有个1024的默认上限。

2.循环中重复调用时,select重复初始化fd_set;poll通过独立的evenets(针对输入)和revenets(针对输出)处理,避免重复调用。

3.select提供超时精度微秒,而poll为毫秒

4.当一个被检查的文件描述符关闭时,poll的revents会设置POLLNVAL标记,会准确告知那个文件关闭;select只会返回1,设置错误码为EBADF,只有自己通过在描述符上执行IO检查错误码才能判断。

select和poll存在的问题:

1.检查所有被指定的文件描述符

2.传递表示被检查的文件描述符的数据结构给内核,会随文件描述符增大而增大,占用拷贝时间等等

3.而这调用后必须检查每个元素查明文件描述符的就绪态。



epoll:epoll

epoll支持ET和LT模式,select,poll只支持LT,信号驱动IO支持ET;并且在大量描述符检查时性能比select和poll更优

           #define MAX_EVENTS 10
           struct epoll_event ev, events[MAX_EVENTS];
           int listen_sock, conn_sock, nfds, epollfd;

           /* Code to set up listening socket, 'listen_sock',
              (socket(), bind(), listen()) omitted */

           epollfd = epoll_create1(0);//epoll新创建方式
           if (epollfd == -1) {
               perror("epoll_create1");
               exit(EXIT_FAILURE);
           }//防御式编程,每次调用系统函数用这种形式判断调用是否成功。

           ev.events = EPOLLIN;//检测的事件
           ev.data.fd = listen_sock;//检测的fd
           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
               perror("epoll_ctl: listen_sock");
               exit(EXIT_FAILURE);
           }

           for (;;) {
               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);//最大返回max_evenets个就绪的fd
               if (nfds == -1) {
                   perror("epoll_wait");
                   exit(EXIT_FAILURE);
               }

               for (n = 0; n < nfds; ++n) {
                   if (events[n].data.fd == listen_sock) {
                       conn_sock = accept(listen_sock,
                                          (struct sockaddr *) &addr, &addrlen);
                       if (conn_sock == -1) {
                           perror("accept");
                           exit(EXIT_FAILURE);
                       }
                       setnonblocking(conn_sock);
                       ev.events = EPOLLIN | EPOLLET;
                       ev.data.fd = conn_sock;
                       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                                   &ev) == -1) {
                           perror("epoll_ctl: conn_sock");
                           exit(EXIT_FAILURE);
                       }
                   } else {
                       do_use_fd(events[n].data.fd);
                   }
               }
           }
这里有一篇:epoll的LT和ET模式的参考 ET和LT



你可能感兴趣的:(linux)