《网络编程实战》学习笔记 Day9

系列文章目录

这是本周期内系列打卡文章的所有文章的目录



前言

提示:这里可以添加本文要记录的大概内容:

学习内容:https://time.geekbang.org/column/article/138948
与知识建立主客体之间的联系:
其实我之前对高性能网络编程接触的少,除了计算机网络基础课TCP协议(三次握手、四次分手)、应用程序对Socket编程+多线程编程的实践理解外,没有真实需求推动,深究的就更少了。八股文背了,可能当时有些概念,过后又忘了。

但随着中间件的设计,团队有时在高并发改造场景,会谈到“poll”类比该I/O多路复用模型。这次真发现了这个网络编程实践专栏高性能篇,将阻塞/非阻塞/异步、I/O多路复用(select、poll、epoll)、多进程/多线程编程模型,三个维度循序渐进的组合起来,一步步揭开、对比、明晰,初看3篇文章概念+实践,和后续目录安排,方觉大快朵颐。


提示:以下是本篇文章正文内容,下面案例可供参考

一、select I/O多路复用模型是什么?(What)

问题场景:
I/O多路复用最初设计解决的场景是:标准输入、套接字等都看做I/O的一路。多路复用的意思是在任何一路有“事件”发生的情况下,通知应用程序去处理相应的I/O事件。而在没有I/O事件触发时,应用程序进程是挂起(阻塞,时间片调度执行其他进程),还是继续做其他事情(在剩余的时间片内)这就是I/O阻塞、非阻塞的维度了。

select之所以大名鼎鼎,可能跟golang通过协程实现高效的I/O多路复用有关。其关键字:select的使用和传播,让该I/O多路复用大名鼎鼎。

从专栏几篇文章看下来,要理解I/O多路复用,除了有开发者应用进程的视角,得理解到要从操作系统内核这个角度看(在序列图中,表示出这个实体)。从而,对比select、poll、epoll多种I/O多路复用模型。

使用select函数,通知内核挂起进程,当一个或多个I/O事件发生后,控制权返还给应用程序,由应用程序进行I/O事件的处理。

草图
在这里插入图片描述

二、select 内核是如何实现的?(Why)

select 函数调用传递给内核的数据结构,概念上是:文件描述符的集合 fd_set
在实现上,是通过INT型的bit位表示集合中的元素,因而也受实现的限制,select支持的I/O多路复用有1024的限制。


int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1

在这里插入图片描述
这块我没翻内核源码,之后补上 TODO。

集合数据结构fd_set *readset, fd_set *writeset, fd_set *exceptset传递给内核,其实也暗示了内核的实现,内核根据I/O事件给读文件描述符、写文件描述符、异常文件描述符集合填充数据。

三、如何使用select,demo实验show me the code?(How)

demo:
详见:https://gitee.com/jahentao/experiments/pulls/1/files

调用select函数,传参。
操作宏:

  • FD_ISSET对向量进行检测,判断出对应套接字的元素 a[fd]是 0 还是 1

根据readmask返会的文件描述符判断那一路I/O可读,进而返回控制权处理。


int main(int argc, char **argv) {
    if (argc != 2) {
        error(1, 0, "usage: select01 <IPaddress>");
    }
    int socket_fd = tcp_client(argv[1], SERV_PORT);

    char recv_line[MAXLINE], send_line[MAXLINE];
    int n;

    fd_set readmask;
    fd_set allreads;
    FD_ZERO(&allreads);
    FD_SET(0, &allreads);
    FD_SET(socket_fd, &allreads);

    for (;;) {
        readmask = allreads;
        int rc = select(socket_fd + 1, &readmask, NULL, NULL, NULL);

        if (rc <= 0) {
            error(1, errno, "select failed");
        }

        if (FD_ISSET(socket_fd, &readmask)) {
            n = read(socket_fd, recv_line, MAXLINE);
            if (n < 0) {
                error(1, errno, "read error");
            } else if (n == 0) {
                error(1, 0, "server terminated \n");
            }
            recv_line[n] = 0;
            fputs(recv_line, stdout);
            fputs("\n", stdout);
        }

        if (FD_ISSET(STDIN_FILENO, &readmask)) {
            if (fgets(send_line, MAXLINE, stdin) != NULL) {
                int i = strlen(send_line);
                if (send_line[i - 1] == '\n') {
                    send_line[i - 1] = 0;
                }

                printf("now sending %s\n", send_line);
                size_t rt = write(socket_fd, send_line, strlen(send_line));
                if (rt < 0) {
                    error(1, errno, "write failed ");
                }
                printf("send bytes: %zu \n", rt);
            }
        }
    }

}

我想进一步在GEM5/QEMU仿真中,进行I/O多路复用实验的性能工程对比,TODO。


总结

提示:这里对文章进行总结:

今天的学习,我了解了select 函数的使用。select 函数提供了最基本的 I/O 多路复用方法,在使用 select 时,我们需要建立两个重要的认识:

  • 描述符基数是当前最大描述符 +1;
  • 每次 select 调用完成之后,记得要重置待测试集合。

内容来源:
极客时间:20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值