ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

网络IO之由浅入深(1)

2020-11-30 23:30:21  阅读:167  来源: 互联网

标签:由浅入深 set socket 队列 网络 fd IO 进程 FD


文章目录

1、啥是IO复用

网络交互比喻

服务员 和 食客的关系,每天都会有食客到餐厅用餐,而每次用餐都需要服务员的招待,如果没有服务员的招待则会出现餐厅异常的情况,所以当食客的出现则必须要有服务员的接待,

1.1、但是接待会出现两种情况:

(1)一种是没出现一个食客,则专门指派一个服务员进行服务,此服务员只处理这个食客的请求

(2)另一种则是,安排一个服务员同时对接多个食客,也能保证食客的正常请求

在这里插入图片描述
(食客 与 服务员的两种图)

1.2、这种接待形式对应的也是我们cs架构中两种形式:

(1)在早期的cs架构中就是一个client 连接请求过来,server端直接使用一个进程或者线程去对接,这样一个检测fd的接收缓存中是否有数据,如若有数据则进行读取接收,众所周知基于Linux的线程开辟的进程资源都是受限于内核空间,则注定了不能多开,所以一个进程/线程对应一个fd在之后的历史发展中就慢慢淘汰了
在这里插入图片描述

(2)而第二种也就是今天的主角,一个服务员接待多个食客乃至N多个这种行为叫做IO 复用。
在这里插入图片描述

2、server端如何接受数据

2.1、阻塞、非阻塞、同步、异步

以出门去商店打酱油的方式讲一下这四种形式

同步:(占用客户资源,己方线程)

(1)阻塞:客户出门之后去打酱油,商店没有酱油了,客户此时一直到商店有酱油了再回来

(2)非阻塞:出门去当酱油,商店没有酱油了,客户就回家,不就之后又来商店看是否有酱油,如果没有酱油,客户就一直执行回家、来店咨询

异步:(占用商家资源,就是对方线程)
客户出门之后去打酱油,商家没有了,客户说有了给我送过来

2.2、网口接收数据

(1)我们的数据最开始是先到达至网口,网口将数据映射至Linux内存中,通过硬件中断通知到cpu(众所周知硬件中断是级别最高的通知),CPU将网络数据写至socket得到接收缓存
在这里插入图片描述

(2)然后协议栈将数据放入socket的接收缓存,数据在接收缓存可读写之后,应用层根据之前所说

  • 要么自身的进程一直读取此IO,直到读取到数据(同步操作)

  • 要么通过各种IO复用的形式管理这些IO,保证数据已经在缓存中的时候应用层能知道,并将句柄发送给专门的线程池进行读取数据(同步、异步都有)
    在这里插入图片描述

2.3、系统层面的处理

2.2.1、socket 的组成

socket的是由接收缓存、发送缓存、等待队列和异步通知队列一些成员组成(这些存在都是由内核进行管理)

2.2.2、进程调度:

Linux经常是以多进程存在,但是又时常都是单核的CPU,

所以在同一个时间是没法运行多个进程,就会存在有的进程在运行,而有的进程则在等待,就会有以下两种队列

(1)工作队列
多个业务需要执行时就将数据排列在工作队列,等待时间轮片算法的调度

(2)等待队列
在我们所说的socket阻塞模式下,这些负责管理的阻塞socket的进程就在等待队列休眠,直到socket的接收缓存中有了数据,等待操作系统的唤醒

在这里插入图片描述

3、IO复用之select

3.1、Select 使用方式

fd_set set;

FD_ZERO(&set); /将set清零使集合中不含任何fd/

FD_SET(fd, &set); /将fd加入set集合/

FD_CLR(fd, &set); /将fd从set集合中清除/

FD_ISSET(fd, &set); /在调用select()函数后,用FD_ISSET来检测fd是否在set集合中,
当检测到fd在set中则返回真,否则,返回假(0)
/


FD_ZERO(&readfds);              // 初始化集合
FD_SET(server_sockfd, &readfds);// 将服务器端socket加入到集合中

testfds = readfds;
while(1) 
{
	select(&testfds); // 内核中针对readfds 集合句柄进行检测 ,将有数据的句柄返回回来
	
	/*扫描所有的文件描述符*/
    for(fd = 0; fd < FD_SETSIZE; fd++) 
    {
        /*找到相关文件描述符*/
        if(FD_ISSET(fd,&testfds)) 
    	{
    		if(fd == can_connect)        // 如果与服务端FD 一致,则接受客户请求建立链接,并将建立链接对应的客户FD加入集合
    		{
    		    aeecpt(socket);
    		    FD_SET(client_sockfd, &readfds);
    		}
    		else if(fd == can_read)    // 如果是集合中的客户FD,则读取
    		{
    			read(socket, buffer);
    			process(buffer);
    		}
    		 
    	}
}

3.2、Select 底层实现原理

回顾知识点:

(1)socket 拥有 读写缓存区、等待队列、异步通知队列

  • 等待队列:

当有一个socket事件时,会将其相关的进程放至其等待队列上等待唤醒

(zmz: 缺图一张)

(1)select 将其管理所有socket的等待队列都填写的当前进程A

(2)当有一个或者多个socket收到数据的时候,就会将进程A从所有的等待队列中A进程移除,加入到工作队列中

(3)进程将他管理的socket缓存便利一遍即可知道哪些socket有数据了

(4)将对应的集合以比特位的形式返回

其一,每次调用select都需要将进程加入到所有监视socket的等待队列,每次唤醒都需要从每个队列中移除(此时已经成为工作队列中节点,不再适合等待队列)。这里涉及了两次遍历,而且每次都要将整个fds列表传递给内核,有一定的开销。正是因为遍历操作开销大,出于效率的考量,才会规定select的最大监视数量,默认只能监视1024个socket。

其二,进程被唤醒后,应用程序并不知道哪些socket收到数据,还需要遍历一次。

标签:由浅入深,set,socket,队列,网络,fd,IO,进程,FD
来源: https://blog.csdn.net/FDmitnick/article/details/110410058

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有