ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

实验总结分析报告:从系统的角度分析影响程序执行性能的因素

2021-05-16 22:01:17  阅读:157  来源: 互联网

标签:分析 总结 调用 socket 程序执行 描述符 fdset select 函数


实验总结分析报告:从系统的角度分析影响程序执行性能的因素

要求

一、 精简的Linux系统概念模型

Linux系统一般有4个主要部分:内核、shell、文件系统和应用程序。内核、shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序、管理文件并使用系统。

img

二、I/O多路复用之select系统调用验证模型

select调用介绍

I/O多路复用模型允许我们同时等待多个套接字描述符是否就绪。Linux系统为实现I/O多路复用提供的最常见的一个函数是select函数,该函数允许进程指示内核等待多个事件中的任何一个发生,并只有在一个或多个事件发生或经历一段指定的时间后才唤醒它。

select模型如下图所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

图片描述

select系统调用验证模型

应用程序层面

在服务器端,服务器通过调用socket()函数创建一个socket对象,通过bind()方法绑定一个端口号与IP,在调用listen()方法完成初始化后,调用accept()阻塞等待,处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。

socket连接成功之后,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。

 

文件系统层面

socket()函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。当我们调用socket()创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()listen()时系统会自动随机分配一个端口。

 

shell层面

select是一个复杂的函数,有许多不同的应用场景,我们将只讨论第一种场景:等待一组描述符准备好读。

select函数的参数有多个。其中,参数n指定需要测试的描述符的数目,测试的描述符范围从0到n-1。第二个参数fdset指定需要测试的可读描述符集合。当fdset集合中有描述符可读,或者经历了timeout时间时,select将返回。当select返回时,作为一个副作用,select修改了参数fdset指向的描述符集合,这时fdset变成由读集合中准备好可以读了的描述符组成。select函数的返回值则指明了就绪集合的基数。值得注意的是,由于这个副作用,我们必须每次在调用select时都更新读集合。

#include <unistd.h>
#include <sys/types.h>

int select(int n, fd_set *fdset, NULL, NULL, struct timeval *timeout);

FD_ZERO(fd_set *fdset);          // 将fdset初始为为空集合
FD_CLR(int fd, fd_set *fdset);   // 从fdset清除fd
FD_SET(int fd, fd_set *fdset);   // 将fd添加到fdset
FD_ISSET(int fd, fd_set *fdset); // fd是否存在于fdset

 

内核层面

通过调用select()函数,在用户空间中保存了已连接的 Socket 文件描述符数组的fds,会被映射到一个set结构的bitmapbitmap的每一位对应fds的下标,表示需要监听的socket),select()函数将bitmap结构拷贝到内核空间中,让内核去监听网络事件是否发生,我们调用select可以告知内核我们对哪些描述符感兴趣以及等待多久时间。

在内核层面中,内核就是让CPU就是不停遍历检测bitmap中每一位对应的文件描述符,检查这个文件描述符是否有IO网络事件的发生。如果是,则接收数据。如果接收的数据长度为0,或者发生WSAECONNRESET错误,则表示客户端套接字主动关闭,这时需要将服务器中对应的套接字所绑定的资源释放掉,然后调整我们的套接字数组(将数组中最后一个套接字挪到当前的位置上),关系到套接字列表的操作都需要使用循环,在轮询的时候,需要遍历一次,再新的一轮开始时,将列表加入队列又需要遍历一次.也就是说,Select在工作一次时,需要至少遍历2次列表,这是它效率较低的原因之一。

图片描述

三、影响应用程序性能的因素分析

select调用的性能影响因素主要在于:

1.每次调用时要重复地从用户态读入参数。

2.每次调用时要重复地扫描文件描述符。由上面对select()函数的分析可知,select()函数主要的工作在于内核中不停遍历检测bitmap中每一位对应的文件描述符,检查这个文件描述符是否有IO网络事件的发生。

3.每次在调用开始时,要把当前进程放入各个文件描述符的等待队列。在调用结束后,又把进程从各个等待队列中删除。

4.select调用中使用到的bitmap结构为1024位,最多只能监听1024个文件描述符(监听1024个socket)

 

 

标签:分析,总结,调用,socket,程序执行,描述符,fdset,select,函数
来源: https://www.cnblogs.com/xhli1998/p/14775001.html

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

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

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

ICode9版权所有