当前位置: 首页> 游戏> 攻略 > 平面设计提升班_北京网页设计与制作教程_seopeix_友情视频

平面设计提升班_北京网页设计与制作教程_seopeix_友情视频

时间:2025/8/23 11:23:01来源:https://blog.csdn.net/weixin_74792326/article/details/146569991 浏览次数:0次
平面设计提升班_北京网页设计与制作教程_seopeix_友情视频

目录

1、五种IO模型:

2、非阻塞IO

3、多路转接之select

3.1、理解select的执行过程


1、应用层read&write的时候,本质把数据从用户写给OS ----  本质就是拷贝

2、IO = 等待 + 拷贝(大部分时间在等待,等到有数据了才进行拷贝)

要进行拷贝,必须先判断条件成立。  这个条件就是读写事件  读事件就绪,就是读缓冲区有足够的数据可以读   写事件就绪就是发送缓冲区中要有足够的空间

什么叫做高效IO呢?   单位时间内,IO过程中,等的比重越小,IO效率就越高!

其实几乎所有提高IO效率的策略,本质就是让等的比重变小。

1、五种IO模型:

五种IO模型:
1、张三:钓鱼的时候,专心钓鱼,不做任何其他的事,不和任何人说话。这叫阻塞式IO。

2、李四:钓鱼的时候,在等鱼咬勾的时候还会一边看书。这是非阻塞轮询IO。

3、王五:等鱼咬钩的时候,在鱼竿上面放一个铃铛,当鱼咬钩的时候,就会给他提醒。这是信号驱动式IO。

4、赵六:他有100个鱼竿,全都放在水里,自己在岸边巡查,有鱼咬钩立马去查看。这是多路复用、多路转接式IO。

5、田七:他是老板,他把钓鱼这件事委托给他的助理小王,田七只是钓鱼行为的发起者,他要的只是鱼。这是异步IO。

阻塞IO  vs  非阻塞IO   它们的IO效率其实是相同的,因为IO = 等待 + 拷贝 (等待的时间其实是一样的,只是等的方式不同)

同步IO  vs  异步IO     同步IO其实是当前这个人有没有参与IO,参与了就是同步,没参与就是异步。异步IO不参与IO只是发起IO,最后拿结果就行。 同步IO和线程同步其实没有任何关系。

因为多路复用式IO效率最高    我们后面重点说明多路复用式IO和非阻塞IO

2、非阻塞IO

文件描述符本质就是一个数组下标,每一个数组下标指向的都是一个内核中的文件对象,文件对象中有文件的flags,用fcntl设置一个文件描述符的属性,其实就是设置这个文件在底层struct file中的标志位。

#include <unistd.h>

#include <fcntl.h>

int fcntl(int  fd, int  cmd,... /* arg */ );

将文件描述符设置为非阻塞,以后在使用read、write、recv、send...都是使用非阻塞的方式进行IO的。

void SetNoBlock(int fd) { int fl = fcntl(fd, F_GETFL);  //获得指定文件描述符的标记位if (fl < 0)  //小于0,获取失败{ perror("fcntl");return; }fcntl(fd, F_SETFL, fl | O_NONBLOCK);//为指定的文件描述符设置标记位   在老的标记位f1的基础上添加 O_NONBLOCK(非阻塞)
}

1、将文件描述符设置成为非阻塞,如果底层fd数据没有就绪,recv、read、write、send返回值会以出错的形式返回(因为返回值大于0就是读到了,等于0就是关闭了)

2、有两种情况: 我真的出错了、底层没有就绪

3、我要怎么区分这两种情况??? 通过errno错误码区分(11是底层数据没有就绪)

else if(errno == EWOULDBLOCK)
{cout << "0 fd data not ready, try again!" << endl;
}
#include <fcntl.h>
#include <unistd.h>
#include<errno.h>
#include <cstdlib>
#include <iostream>
using namespace std;//对指定的fd设置非阻塞
void SetNonBlock(int fd) {int fl = fcntl(fd, F_GETFL);if (fl < 0) {cerr << "fcntl error" << endl;exit(1);}fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}int main() {SetNonBlock(0);while (1) {char buffer[1024];ssize_t s = read(0, buffer, sizeof(buffer) - 1);if (s > 0) {buffer[s] = 0;cout << buffer << endl;} else if (s == 0) {cout << "读到文件结尾了" << endl;break;}else{//1. 数据没用准备好 2. 真的出错了. 都以-1的返回值返回// 数据没有准备好,不算出错. 需要区分这两种情况if(errno == EWOULDBLOCK || errno == EAGAIN){cout<<"os底层数据还没就绪"<<endl;cout<<errno<<endl;}//被信号中断, 也不算read出错else if(errno == EINTR){cout<<"IO interrupted by signal"<<endl;}else{cout<<"read error"<<endl;break;}}sleep(1);}
}

3、多路转接之select

IO = 等 + 拷贝

select:只负责进行等待。一次可以等待多个fd

就绪事件通常分为可读事件,可写事件和异常事件

#inclde <sys/select>

int  select(int  nfds, fd_set *readfds, fd_set *writefds, fd_set  *exceptfds,   struct  timeval  *timeout);

第一个参数:要等的最大的文件描述符+1

第二个:关心读  fd_set是内核提供的一种数据类型,它是位图       输入输出型参数

输入时:用户告诉内核,我给你一个或多个fd,你要帮我关心fd上面的读事件哦,如果读事件就绪了,你要告诉我。

输出时:内核告诉用户,用户你要我关心的多个fd,有哪些就绪了,用户你赶紧读取吧。

输入时:比特位的位置(从右向左数),表示文件描述符编号,比特位的内容,0/1,表示是否需要内核关心。

输出时:比特位为0/为1,表示哪些用户关心的fd,上面的读事件已经就绪了。

fd_set是一张位图,让用户和内核传递fd是否就绪的信息的。

第三个: 关心写

这就涉及到很多对位图修改的动作,系统提供了接口。

fd_set类型参数, 输入你想要关心的fd集合, 输出时, 此结构中存放, 已经事件就绪的fd集合. 比如你想要关心0~10号文件描述符的读事件, 函数返回时, 此集合中可能只有1.3.5号fd被返回了, 也就是说只有1.3.5号fd的事件就绪了

第五个参数:

struct  timeval:结构体 时间结构体:

{5,0}:每隔5s,timeout一次。5s阻塞式等待,这5s没有文件描述符就绪,就返回,再重新进入,重复(我们需要重复设置)。如果等待5s期间有文件描述符就绪了,就会立即返回

timeval类型参数: 假如你设置阻塞时间为5秒, 但是等待了三秒后就有事件就绪, 函数就返回了, 那么timeval类型参数的值会被设置成为2秒.

他是输入输出参数,可能要进行周期的重复设置

{0,0}:非阻塞等待。立马返回

NULL:阻塞等待

这个参数是输入输出型参数

select返回值和错误码:

大于0,有n个fd就绪了

等于0,超时,没有出错,但是也没有fd就绪

小于0,等待出错

3.1、理解select的执行过程

如果事件就绪,上层不处理,select会一直通知你! 

select的缺点:

1、等待的fd是有上限的

2、输入输出型参数比较多,数据拷贝的频率比较高(用户到内核,内核到用户)

3、输入输出型参数比较多,每次都要对关心的fd进行事件重置

4、用户层,使用第三方数组管理用户的fd,用户层需要很多次遍历,内核中检测fd事件就绪也要遍历

#pragma once#include <iostream>
#include <sys/select.h>
#include <sys/time.h>
#include "Socket.hpp"using namespace std;static const uint16_t defaultport = 8080;
static const int fd_num_max = (sizeof(fd_set) * 8);
int defaultfd = -1;class SelectServer
{
public:SelectServer(uint16_t port = defaultport) : _port(port){for(int i = 0; i < fd_num_max; i++){fd_array[i] = defaultfd;std::cout <<"fd_array[" << i << "]" << " : " << fd_array[i] << std::endl;}}bool Init(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();return true;}void Accepter(){// 连接事件就绪了std::string clientip;uint16_t clientport = 0;int sock = _listensock.Accept(&clientip, &clientport); // 获取连接不会阻塞在这里if (sock < 0){return;}lg(Info, "accept success, %s:%d", clientip.c_str(), clientport);// 以前走到这里可以直接读取数据,可是,现在这里不可以// 以前是多线程、多进程, 文件描述符其实是托管给执行流的 他阻塞是不影响的// 我们现在是单进程,不能建立完连接,立马就进行读  如果不发,当前的进程就阻塞了// select里面只有一个listen套接字    要将sock设置进select里  这样读文件描述符集里的文件描述符就会变得越来越多// accept获取新连接, 不能直接读,因为不清楚是否就绪,selcet清楚是否就绪,所以要将新获取的连接 添加到辅助数组里// select把数据处理完之后,下次循环时会重新再进行把文件描述符添加到rfds里,再交给select由他来监听int pos = 1;for (; pos < fd_num_max; pos++){if (fd_array[pos] != default)continue; // 被占用elsebreak;}if (pos == fd_num_max){// 全部被占用lg(Warning, "server is full, close %d now!", sock);close(sock);}else // 提前break说明由-1位置(即没有被占用){fd_array[pos] = sock; // 新获取的连接往数组中添加}}void Recver(int fd, int pos){char buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 把它当字符串cout << "get a message: " << buffer << endl;}else if (n == 0) // 读失败{lg(Info, " client quit,me too, close fd is : ", fd);close(fd);fd_array[pos] = defaultfd; // 这里本质是从select中移除}}void Dispatchar(fd_set &rfds) // 读事件就绪   //事件派发器{for (int i = 0; i < fd_num_max; i++){int fd = fd_arry[i];if (fd == dafaultfd)conitnue;if (FD_ISSET(fd, &rfds)) // 判断listen套接字是否在集合里,即是否就绪{if(fd == _listensock.Fd()){Accepter(); //连接管理器}else //不是listenfd{Recver(fd, i); }}}}void Start(){int listensock = _listensock.Fd();fd_array[0] = listensock;for (;;){Fd_set rfds;Fd_ZERO(rfds);int maxfd = fd_array[0];for(int i = 0; i < fd_num_max; i++){if(fd_arry[i] == default) continue; //没有被设置过的Fd_SET(fd_array[i], &rfds); // 将文件描述符添加到集合里if(maxfd < fd_array[i]){maxfd = fd_array[i];}}// accept 的本质是检测listensocket上面有没有连接事件 即底层有三次握手 他一次只能等一个文件描述符 所以要交给select// 新连接到来,对于select来讲就是读事件就绪// 读文件描述符集,他是一个位图struct timeval timeout = {5, 0}; //需要被重复设置 因为如果不重复设置就 会剩余的时间替换//输入输出参数,每次从内核返回的时候值可能就被改过了 ,所以需要重复设置int n = select(maxfd + 1, &rfds, nullptr, nullptr, &timeout); // 告诉OS关心这个文件描述符上的读事件//select 是如果事件就绪,上层不处理,select会一直通知你//select告诉你就绪了,接下来的一次读取,fd不会阻塞switch(n){case 0://等待超时,在等待期间任何事情都没有发生break;case -1://异常break;default://有事件就绪HandlerEvent(rfds);//就绪的事件在rfds里break;}}}~SelectServer(){_listensock.Close();}
private:Sock _listensock;uint16_t _port;int fd_array[fd_num_max]{defaultfd};//设置辅助数组
};
关键字:平面设计提升班_北京网页设计与制作教程_seopeix_友情视频

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: