io与select介绍

简介: io与select介绍

什么是IO

  • IO:指既能收数据也能发数据。
    Linuxsocketfd(文件描述符)就是一种IO

观察程序启动流程

strace 可执行文件

  • 使用该条命令可以查看到进程是如何启动的
    是由bash所在的进程调用execve来启动的进程

调用main函数前操作系统会进行什么操作

  • 加载可执行文件:操作系统会将程序的可执行文件从磁盘读取到内存中,并进行一些必要的校验工作,例如校验文件格式、校验文件权限等。
  • 分配内存:操作系统会为程序分配内存空间,包括代码段、数据段、堆和栈等。代码段存储程序的指令,数据段存储程序的全局变量和静态变量,堆和栈用于动态内存分配和函数调用。
  • 解析动态链接库:如果程序使用了动态链接库,操作系统会在加载程序时解析这些库,并将它们链接到程序中。
  • 初始化进程环境:操作系统会初始化进程的环境,包括设置进程的根目录、文件描述符、信号处理器等。
  • 启动程序:最后,操作系统会定位main函数的入口地址,并跳转到这个地址开始执行程序。

Linux内核kernel没有main函数,他是如何执行的

通过硬件引导程序(bootloader)启动

  • 在计算机启动时,硬件会首先加载硬件引导程序(bootloader),它通常位于硬盘的第一个扇区(也称为主引导记录)。硬件引导程序的作用是加载内核映像(kernel image)到内存中,并跳转到内核的入口点开始执行。
  • Linux内核中,入口点的符号名为_start。当硬件引导程序将内核映像加载到内存中后,会将控制权转移给_start符号所在的地址,从而开始执行内核的初始化过程。
  • 在内核的初始化过程中,会进行各种硬件初始化、内存管理、进程管理等操作,最终启动一个称为init的用户进程作为系统的第一个进程,并将控制权转交给它,让它继续进行系统初始化和启动其他进程。

服务器常用IP地址IADDR_ANY127.0.0.1、虚拟机网卡指定IP之间的区别

  • IADDR_ANY:值为0,用点分十表达则为0.0.0.0,表示绑定本机随便选一个网卡的ip
  • 127.0.0.1:表示监听监听本地回环接口,只能用于本机访问
  • 指定IP:绑定指定的本地网卡IP地址

例子:远程访问非本机的Mysql的服务失败,在确定了其防火墙是关闭的情况,可以考虑是其配置文件的bind-address默认是设置的127.0.0.1只允许其本机访问,所以可以将其设置为0.0.0.0,监听所有可用的网络接口,这样也许就能解决无法访问远程mysql服务问题

服务器基本框架的简单理解

socket、bind、listen

  • 去吃饭,进门找到接引人(listen),他会将你带去找到点菜(发送数据)的人(accept

注意一种情况

send返回>0不等于发送成功,(只是代表将数据发送到了协议栈)

阻塞与非阻塞

简单的服务器框架socket、bind、listen、accept阻塞在accept阻塞的主要原因是监听的文件描述符默认是阻塞的

实现多个客户端连接,并且发送数据

多线程与多进程实现

来一个请求accept之后开辟一个线程去处理任务

**好处:**逻辑简单,线程只为一个fd服务,不用担心其他线程or进程来处理

**缺点:**代价太大

io多路复用

如何单线程实现多个客户端同时连接?

  • 不采取任何措施下多个客户端的连接listenfdio有效)是没有问题的
    但是不知道什么时候执行accept
    (后续的业务处理)不知道什么时候recv,不知道什么时候send
  • io多路复用就是检测io是否有事件(事件:描述符上是否有可读可写)
  • 如何标识一个IO的事件->可读(有还是没有)可写(有还是没有)->select用一个bit位表示

select

如何理解select

  • 一个人(server)喜欢去东莞,他跟很多技师(fd)关系很好,经常去,但是每次去的时候都要访问所有技师(fd)关于以下信息
  1. 晚上有没有时间?(io可读)
  2. 是否还愿意?(io可写)
  • 后来他找了一个秘书(select),他会叫她先去东莞采集完这些信息后再去,这个秘书的主要职责就是看这些技师(fd)是否可以办正事(recv\send

select接口介绍

IO多路复用是一种同时监控多个文件描述符(包括套接字)的技术,它允许一个进程可以同时等待多个IO操作完成,而不是阻塞在单个IO操作上。其中一个常用的IO多路复用技术是select

select函数可以同时监视多个文件描述符,等待其中任何一个文件描述符就绪(可读、可写或异常),然后通知应用程序进行相应的操作。使用select可以避免阻塞在单个IO操作上,提高程序的效率和响应速度。

在使用select函数时,需要准备一个文件描述符集合,包括需要监视的所有文件描述符,然后将该集合传递给select函数。select函数会不断地监视这些文件描述符,直到其中任何一个文件描述符就绪,然后返回就绪文件描述符的数量,并更新原始的文件描述符集合。

在编写基于select的程序时,需要注意以下几点:

  1. 设置文件描述符为非阻塞模式,以避免阻塞在单个IO操作上。
  2. 每次调用select时,需要重新设置原始的文件描述符集合,以避免之前的就绪文件描述符被遗漏。
  3. 在处理就绪文件描述符时,需要注意其对应的IO操作是否已经完成,以避免出现错误的操作。
函数原型
#include <sys/select.h>
int select(int nfds,        
           fd_set *readfds, 
           fd_set *writefds, 
           fd_set *exceptfds, 
           struct timeval *timeout
          );
  • nfds:需要监视的文件描述符集合中所有文件描述符的最大值加1。
  • readfds:监视可读性的文件描述符集合。
  • writefds:监视可写性的文件描述符集合。
  • exceptfds:监视异常性的文件描述符集合。
  • timeoutselect() 超时时间,设置为 NULL 表示阻塞等待,设置为 0 表示立即返回,设置为大于 0 的值表示等待指定时间后返回。
demo

只判断了io是否可读

  • 注册select读检测
// socket bind listen..
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
fd_set rfds, rset;
FD_ZERO(&rfds);       // 清空bit位
FD_SET(sockfd, &rfds);  // 设置listenfd位
int maxfd = sockfd;   // 设置最大fd
int clientfd = 0;
  • select
while (1) {  // master
    rset = rfds;  // 设置副本 保证同一循环下检测队列不会改变
    int nready = select(maxfd+1, &rset, NULL, NULL, NULL);
    if (FD_ISSET(sockfd, &rset)) { // 判断是否有客户端连接
        clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
        printf("accept: %d\n", clientfd);
        FD_SET(clientfd, &rfds);
        if (clientfd > maxfd) maxfd = clientfd;
        if (-- nready == 0) continue;
    }
    int i = 0;
    for (i = sockfd+1; i <= maxfd;i ++) {  
        if (FD_ISSET(i, &rset)) {  // 判断客户端io是否可读
            char buffer[BUFFER_LENGTH] = {0};
            int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
            if (ret == 0) {
                close(clientfd);
                break; 
            }
            printf("ret: %d, buffer: %s\n", ret, buffer);
            send(clientfd, buffer, ret, 0); 
      }
  }
}

细节注意点:

关于accept所做的事情

  • listenfd在监听到客户端连接后执行accept会清空掉其描述符上客户端写进来的数据。这样只有在下次客户端到来的时候listenfd描述符上才会有io提示,select才会刚好在客户端连接的时候检测到listenfd的动静。
    如果使用了select来检测listenfd,之后没有写accept,则select每次循环都会检测listenfd有数据

关于select的读写集合设置副本的原因

  • 是为了在同一循环中保证一开始想要检测的固定数量的文件描述符io不会变多或变少

maxfd设置为什么要+1

  • 内核中轮询的代码就类似于下面这种
for(int i = 0; i < maxfd_; i++){
    //
}
  • 这种判断是<要想包含maxfd那就必须+1
相关文章
|
2月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
97 0
|
7月前
|
存储 监控 Linux
【Linux IO多路复用 】 Linux下select函数全解析:驾驭I-O复用的高效之道
【Linux IO多路复用 】 Linux下select函数全解析:驾驭I-O复用的高效之道
1155 0
|
7月前
|
存储 网络协议
TCP服务器 IO多路复用的实现:select、poll、epoll
TCP服务器 IO多路复用的实现:select、poll、epoll
95 0
|
7月前
|
存储 Linux
图解IO多路复用模型之select、poll、epoll
图解IO多路复用模型之select、poll、epoll
126 0
|
3月前
|
网络协议 Java Linux
高并发编程必备知识IO多路复用技术select,poll讲解
高并发编程必备知识IO多路复用技术select,poll讲解
|
5月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
188 1
|
5月前
|
存储 Java Unix
(八)Java网络编程之IO模型篇-内核Select、Poll、Epoll多路复用函数源码深度历险!
select/poll、epoll这些词汇相信诸位都不陌生,因为在Redis/Nginx/Netty等一些高性能技术栈的底层原理中,大家应该都见过它们的身影,接下来重点讲解这块内容。
|
6月前
|
缓存 监控 网络协议
c++高级篇(二) ——Linux下IO多路复用之select模型
c++高级篇(二) ——Linux下IO多路复用之select模型
|
7月前
|
网络协议 架构师 Linux
一文说透IO多路复用select/poll/epoll
一文说透IO多路复用select/poll/epoll
279 0