当前位置: 首页 > news >正文

快速网站推广做吉祥物设计看什么网站

快速网站推广,做吉祥物设计看什么网站,wordpress按月归档,wordpress wdone1. 五种IO模型 在 UNIX/Linux 系统中,IO 模型是处理输入输出操作的核心机制,主要分为以下五种:阻塞 IO、非阻塞 IO、IO 多路复用、信号驱动 IO、异步 IO。它们的核心区别在于 “等待数据准备” 和 “数据复制到用户空间” 两个阶段的处理方式…

1. 五种IO模型

在 UNIX/Linux 系统中,IO 模型是处理输入输出操作的核心机制,主要分为以下五种:阻塞 IO、非阻塞 IO、IO 多路复用、信号驱动 IO、异步 IO。它们的核心区别在于 “等待数据准备” 和 “数据复制到用户空间” 两个阶段的处理方式不同。

1.1 阻塞IO(Blocking IO)

最基础的 IO 模型,进程执行 IO 操作时会全程阻塞,直到数据准备完成并复制到用户空间后才返回。

  1. 进程发起 IO 请求(如recvfrom);
  2. 内核开始准备数据(如等待网络数据到达),此阶段进程阻塞;
  3. 数据准备完成后,内核将数据从内核空间复制到用户空间,此阶段进程继续阻塞;
  4. 复制完成,IO 调用返回,进程恢复运行。
  • 优点:实现简单,无需额外处理;
  • 缺点:进程阻塞期间无法做其他事,效率低,不适用于高并发场景;
  • 适用场景:简单小程序(如单机小工具),或并发量极低的场景。

1.2 非阻塞IO(Non-blocking IO)

进程发起 IO 请求后,若数据未准备好,内核会立即返回错误(而非阻塞);进程可不断轮询(重复发起请求),直到数据准备好并完成复制。

  1. 进程发起 IO 请求,若数据未准备好,内核返回EWOULDBLOCK错误,进程不阻塞;
  2. 进程可做其他事,之后主动轮询再次发起 IO 请求;
  3. 当数据准备好后,内核将数据复制到用户空间(此阶段进程阻塞);
  4. 复制完成,IO 调用返回,进程处理数据。
  • 优点:进程不阻塞在等待数据阶段,可并行处理其他任务;
  • 缺点:轮询会消耗大量 CPU 资源,效率不高;
  • 适用场景:对响应速度要求极高,且并发量小的场景(实际中很少单独使用)。

1.3 IO 多路复用/多路转接(IO Multiplexing)

通过一个监控进程(如select/poll/epoll)同时监控多个 IO 流,当某个 IO 流的数据准备好时,再通知进程处理。进程阻塞在 “监控” 操作上,而非直接阻塞在 IO 请求上。

  1. 进程创建监控器(如epoll_create),并将需要监控的 IO 流注册到监控器;
  2. 进程调用监控接口(如epoll_wait),进入阻塞状态(等待任一 IO 流就绪);
  3. 当某个 IO 流数据准备好,内核通知监控器,监控接口返回就绪的 IO 流;
  4. 进程针对就绪的 IO 流发起 IO 请求,内核将数据复制到用户空间(此阶段进程阻塞);
  5. 复制完成,进程处理数据。
  • 优点:单进程可高效管理多个 IO 流,适合高并发(如服务器同时处理多个客户端的连接);
  • 缺点:数据复制阶段仍会阻塞,且监控器本身有一定开销(但远小于轮询);
  • 适用场景:高并发网络服务器(如 Nginx、Redis),是实际中最常用的模型之一。

1.4 信号驱动 IO(Signal-driven IO)

进程通过信号机制被动等待通知:先注册信号处理函数,内核在数据准备好时主动发送 SIGIO 信号,进程收到信号后再处理 IO。

  1. 进程注册 SIGIO 信号的处理函数,并告知内核需要监控的 IO 流;
  2. 进程不阻塞,可正常执行其他任务;
  3. 当数据准备好,内核发送 SIGIO 信号给进程;
  4. 进程捕获信号,在信号处理函数中发起 IO 请求,内核将数据复制到用户空间(此阶段进程阻塞);
  5. 复制完成,进程处理数据。
  • 优点:无需轮询,等待阶段不阻塞;
  • 缺点:信号处理逻辑复杂(如信号队列溢出、多信号竞争),实际中很少使用;
  • 适用场景:特殊场景(如某些网络设备驱动),极少用于通用高并发程序。

1.5 异步 IO(Asynchronous IO)

最 “彻底” 的异步模型:进程发起 IO 请求后立即返回,内核会完成 “数据准备” 和 “复制到用户空间” 的全部操作,完成后再通知进程。

  1. 进程发起异步 IO 请求(如aio_read),并指定操作完成后的通知方式(如信号或回调);
  2. 进程立即返回,可继续执行其他任务(全程不阻塞);
  3. 内核自动完成数据准备和复制到用户空间的所有操作;
  4. 操作全部完成后,内核通过信号或回调通知进程;
  5. 进程收到通知后,直接处理用户空间中的数据。
  • 优点:全程无阻塞,理论上效率最高;
  • 缺点:实现复杂,内核支持有限(如 Linux 的io_uring是较新的高效实现)。
  • 适用场景:对性能要求极致的高并发场景(如高性能数据库、分布式存储)。

2. 非阻塞IO

要使用非阻塞IO的方式进行数据的读写非常简单,基本上所有与读写有关的系统调用都会提供一个flag参数,通过对这个参数进行设置,就可以使得该系统调用变为非阻塞式IO。

这里,我们就不对这些接口的flag参数进行详细介绍了,毕竟使用man就可以直接查询。

我们要介绍的是一个通用的设置非阻塞IO的方式,即直接对文件描述符的属性进行设置。

为什么这个方式是通用的呢?因为Linux当中“一切皆文件”

2.1 fcntl系统调用

在 UNIX/Linux 系统中,fcntl(file control)是一个功能强大的系统调用,用于控制已打开文件描述符的各种属性和行为。

它支持多种操作,包括设置非阻塞模式、管理文件锁、修改文件状态标志等,是系统编程中处理文件和 IO 的核心接口之一。

#include <unistd.h>
#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );

参数

  • fd:需要操作的文件描述符(已通过open/socket等函数打开)。
  • cmd:操作命令(决定fcntl的行为,如获取 / 设置属性、加锁等)。
    常见操作
    • F_GETFL:获取文件状态标志。返回值为当前的状态标志(如O_RDONLY、O_WRONLY、O_NONBLOCK等)。
    • F_SETFL:设置文件状态标志。通过arg参数传入新的状态标志(只能修改部分标志,如O_NONBLOCK、O_APPEND等,不可修改读写模式)。
    • F_DUPFD:复制文件描述符,返回一个大于等于arg的新描述符(与dup类似,但可指定最小描述符值)。
  • 可选参数arg:根据cmd的不同,可能是整数或结构体(如struct flock)。

返回值

  • 成功:返回值取决于cmd(如文件状态标志、锁状态等)。
  • 失败:返回-1,并设置errno(如EBADF表示文件描述符无效)。

2.2 设置非阻塞

void SetNoBlock(int fd) 
{// 先获取原本的文件描述符属性int fl = fcntl(fd, F_GETFL); if (fl < 0) { perror("fcntl");return;}// 再将非阻塞属性设置进去fcntl(fd, F_SETFL, fl | O_NONBLOCK); 
}

3. select系统调用实现多路复用

3.1 select系统调用

在 UNIX/Linux 系统中,select是IO 多路复用的经典系统调用,允许进程同时监控多个文件描述符(file descriptor),等待其中一个或多个处于 “就绪” 状态(如可读、可写或发生异常),从而高效处理多 IO 流场景(如同时管理多个网络连接)。

#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);// fd_set为一个位图结构, 在这里用于表示我们希望select帮助我们监控的fd
// 我们需要使用下面的几个宏来对其进行操作
void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

参数

  • nfds:需要监控的 “最大文件描述符 + 1”。内核会从 0 到nfds-1遍历检查文件描述符,因此必须正确设置(否则可能遗漏监控)。
  • readfds可读事件监控集合。若某个文件描述符在此集合中,且内核检测到其 “可读”(如收到数据),则该描述符会被标记为就绪(直接修改readfds)。
  • writefds可写事件监控集合。若某个文件描述符在此集合中,且内核检测到其 “可写”(如发送缓冲区有空间),则该描述符会被标记为就绪。
  • exceptfds异常事件监控集合。用于检测文件描述符的异常状态(如带外数据到达)。
  • timeout超时设置(struct timeval类型),决定select的阻塞行为:
    • NULL:无限阻塞,直到有事件就绪才返回。
    • tv_sec=0且tv_usec=0:非阻塞,立即返回(不管是否有事件就绪)。
    • 其他值:阻塞等待指定时间(秒 + 微秒),超时后返回。
      struct timeval {long tv_sec;  // 秒long tv_usec; // 微秒(1秒=1e6微秒)
      };

注意:除了第一个参数以外,其余参数都是输入/输出型参数。在调用结束之后,fd_set类型的变量会被设置为事件(读/写)就绪的文件描述符集合;timeout中的值为距离超时剩余的事件(超时返回则为0)。

返回值

  • 成功:返回就绪的文件描述符总数(可读、可写、异常事件的总和)。
  • 超时(未检测到任何就绪事件):0。
  • 出错(如被信号中断):-1,并设置errno。

3.2 用select实现一个多路复用的网络服务

下面的代码是结合了博主自己封装的Socket来写的,仅供借鉴,完整代码参考:https://gitee.com/da-guan-mu-lao-sheng/linux-c/tree/master/%E5%A4%9A%E8%B7%AF%E8%BD%AC%E6%8E%A5/Select

#pragma once
#include <sys/select.h>
#include "Common.hpp"
#include "Socket.hpp"
using namespace SocketModule;class SelectServer : public NoCopy
{
private:void ListenSocketHandler(){std::shared_ptr<TCPConnectSocket> connect_socket = _listen_socket->Accept();int connect_sockfd = connect_socket->SockFd();_fds[connect_sockfd] = connect_socket;}void ConnectSocketHandler(int fd){std::string message;int n = _fds[fd]->Receive(message);if(n > 0){// 正常收到消息, 回显std::cout << "Client[" << _fds[fd]->Addr().Info() << "] say# " << message << std::endl;}else if(n == 0){// 客户端断开连接LOG(LogLevel::INFO) << "Client[" << _fds[fd]->Addr().Info() << "]已断开连接...";_fds[fd] = _default_socket_ptr;}else{// 出错LOG(LogLevel::ERROR) << "Receive: 接收Client[" << _fds[fd]->Addr().Info() << "]的数据失败! ";}}void Dispatch(fd_set *readfds){for (int fd = 0; fd < _fds.size(); fd++){if (!_fds[fd] || !FD_ISSET(fd, readfds))continue;if (fd == _listen_socket->SockFd()){// 监听套接字ListenSocketHandler();}else{// 连接套接字ConnectSocketHandler(fd);}}}public:SelectServer(in_port_t port): _isrunning(false), _listen_socket(std::make_shared<TCPListenSocket>(port)), _fds(sizeof(fd_set) * 8, _default_socket_ptr){int listen_sockfd = _listen_socket->SockFd();_fds[listen_sockfd] = _listen_socket;}void Run(){_isrunning = true;while (_isrunning){fd_set readfds;FD_ZERO(&readfds);int max_fd = _listen_socket->SockFd();for (int fd = 0; fd < _fds.size(); fd++){if (_fds[fd]){FD_SET(fd, &readfds);max_fd = std::max(max_fd, fd);}}struct timeval timeout = {10, 0};int n = select(max_fd + 1, &readfds, nullptr, nullptr, &timeout);if (n > 0){// 有事件就绪LOG(LogLevel::INFO) << "有事件就绪, 即将处理...";Dispatch(&readfds);}else if (n == 0){// 超时LOG(LogLevel::DEBUG) << "超时处理...";}else{// 出错LOG(LogLevel::FATAL) << "select: " << strerror(errno);break;}}_isrunning = false;}~SelectServer() {}private:bool _isrunning;std::shared_ptr<TCPListenSocket> _listen_socket; // 监听套接字std::vector<std::shared_ptr<Socket>> _fds;       // 需要select等待的套接字static const std::shared_ptr<Socket> _default_socket_ptr;
};
const std::shared_ptr<Socket> SelectServer::_default_socket_ptr = nullptr;

上面的代码就可以在只有单个执行流的情况下,并发地处理多个客户端发来的请求。

3.3 优缺点

优点

  • 跨平台支持:几乎所有 UNIX/Linux 系统(及 Windows)都支持,兼容性好。
  • 功能完整:可同时监控可读、可写、异常三类事件。

缺点

  • 文件描述符数量限制: fd_set大小受FD_SETSIZE(通常为 1024)限制,默认最多监控 1024 个文件描述符(需重新编译内核才能修改,成本高)。
  • 效率随 FD 数量下降: 每次调用select,内核需遍历所有监控的 FD(O (n) 复杂度),当 FD 数量庞大时(如 10 万级),效率极低。
  • 集合需重复初始化: select返回后会修改fd_set(只保留就绪的 FD),下次调用前必须重新用FD_ZERO和FD_SET重置,操作繁琐。
  • 内核 / 用户空间复制开销: 每次调用select,fd_set需从用户空间复制到内核空间,FD 数量越多,复制开销越大。

select适合FD 数量较少的场景(如几百个以内),例如简单的多客户端网络服务器。但在高并发场景(如需要支持上万连接),已被更高效的epoll(Linux)kqueue(BSD/macOS)等替代。

4. poll系统调用实现多路复用

4.1 poll系统调用

在 Linux 系统中,poll 也是一个用于 I/O 多路复用的系统调用。

#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数

  • fds:指向 struct pollfd 结构体数组的指针,每个结构体描述一个要监视的文件描述符及其关注的事件
  • nfds:要监视的文件描述符数量
  • timeout:超时时间(毫秒),-1 表示无限等待,0 表示立即返回

struct pollfd结构体

struct pollfd {int   fd;         // 要监视的文件描述符,-1 表示忽略此结构体short events;     // 要监视的事件(输入参数)short revents;    // 实际发生的事件(输出参数)
};

返回值

  • 成功:等待成功的事件个数(超时返回0);
  • 失败:返回-1,并设置错误码。

4.2 用poll实现一个多路复用的网络服务

完整代码参考:https://gitee.com/da-guan-mu-lao-sheng/linux-c/tree/master/%E5%A4%9A%E8%B7%AF%E8%BD%AC%E6%8E%A5/Poll

#pragma once
#include <poll.h>
#include <list>
#include <unordered_map>
#include "Common.hpp"
#include "Socket.hpp"
using namespace SocketModule;class PollServer : public NoCopy
{
private:void ListenSocketHandler(){std::shared_ptr<TCPConnectSocket> connect_socket = _listen_socket->Accept();if (!connect_socket){ // 检查Accept是否成功LOG(LogLevel::ERROR) << "Accept failed";return;}int connect_sockfd = connect_socket->SockFd();RegisterFd(connect_sockfd, POLLIN);_connect_socket[connect_sockfd] = connect_socket;}void ConnectSocketHandler(int fd){auto it = _connect_socket.find(fd);if (it == _connect_socket.end()){LOG(LogLevel::ERROR) << "Invalid socket fd: " << fd;return;}std::string message;int n = it->second->Receive(message);std::string client = it->second->Addr().Info();if (n > 0){// 正常收到消息std::cout << "Client[" << client << "] say# " << message << std::endl;}else if (n == 0){// 客户端断开连接LOG(LogLevel::INFO) << "Client[" << client << "]已断开连接...";_connect_socket.erase(it);DeleteFd(fd);}else{// 出错LOG(LogLevel::ERROR) << "Receive: 接收Client[" << client << "]的数据失败! ";_connect_socket.erase(it);DeleteFd(fd);}}void Dispatch(){for (auto &poll : _fds){// 跳过无效的fd或没有事件的fdif (poll.fd == _default_socket_fd || !(poll.revents & POLLIN))continue;if (poll.fd == _listen_socket->SockFd()){// 监听套接字ListenSocketHandler();}else{// 连接套接字ConnectSocketHandler(poll.fd);}}}void RegisterFd(int fd, short events){// 尝试找到一个空闲位置for (auto &poll : _fds){if (poll.fd == _default_socket_fd){poll.fd = fd;poll.events = events;return;}}// 如果没有空闲位置,则添加新元素_fds.push_back((pollfd){fd, events, 0});}void DeleteFd(int fd){for (auto &poll : _fds){if (poll.fd == fd){poll.fd = _default_socket_fd;poll.events = 0;poll.revents = 0;break;}}}public:PollServer(in_port_t port): _isrunning(false), _listen_socket(std::make_shared<TCPListenSocket>(port)){if (!_listen_socket || _listen_socket->SockFd() < 0){LOG(LogLevel::FATAL) << "PollServer: 初始化监听套接字失败! ";exit(EXIT_FAILURE);}int listen_sockfd = _listen_socket->SockFd();RegisterFd(listen_sockfd, POLLIN);}void Run(){_isrunning = true;while (_isrunning){// 使用实际有效的fd数量int n = poll(_fds.data(), _fds.size(), 10000);if (n < 0){// 处理错误,EINTR是可恢复的if (errno == EINTR)continue;LOG(LogLevel::FATAL) << "poll error: " << strerror(errno);break;}else if (n == 0){// 超时LOG(LogLevel::DEBUG) << "poll timeout...";}else{// 有事件就绪LOG(LogLevel::INFO) << "有" << n << "个事件就绪, 即将处理...";Dispatch();}}_isrunning = false;}~PollServer(){_isrunning = false;}private:bool _isrunning;std::shared_ptr<TCPListenSocket> _listen_socket;                            // 监听套接字std::unordered_map<int, std::shared_ptr<TCPConnectSocket>> _connect_socket; // 需要poll等待的连接套接字std::vector<pollfd> _fds;                                                   // poll等待列表static const int _default_socket_fd;
};const int PollServer::_default_socket_fd = -1;

4.3 优缺点

优点: 克服了 select 对文件描述符数量的限制(select 通常限制为 1024) 不需要每次调用都重新初始化文件描述符集合 可以处理更多的文件描述符。

缺点: 随着监视的文件描述符数量增加,性能会有所下降 相比 epoll,在处理大量并发连接时效率较低

poll 适用于需要监视中等数量文件描述符的场景,在高性能网络编程中,epoll 通常是更好的选择。

相对来说,select兼容性更高,epoll性能更好,poll的位置较为尴尬,我们在下一篇文章当中会继续探讨epoll的用法与特点。

http://www.dtcms.com/a/448800.html

相关文章:

  • 提供网站空间服务器网络营销推广方法范文
  • 桐梓网站建设北京百度总部电话
  • 凡客网站建设支付宝网站开发流程
  • 包头焦点网站建设腾讯云网站搭建
  • 河南金城建设工程有限公司网站建行网站首页登录
  • 做网站 花园路国贸vps装网站管理系统
  • 广州做网络服装的网站建设网站策划书的撰写流程
  • 织梦做的网站好优化品牌设计工作室
  • 做怎么网站推广专做视频和ppt的网站
  • 空间设计师网站手机版百度入口
  • 网站多少页面合适全球搜索引擎市场份额
  • 国外哪个网站做c 挣钱推广普通话标语
  • 蓬莱做网站价格网站头部设计
  • 中国建设监理协会网站查询成绩wordpress 视频 去广告插件下载
  • 上海专业做网站推广的公司静态网页做的网站怎么发到网上
  • 别人的网站是怎么做的网站设置不能通过链接访问
  • 重庆站外推广网站网站设计评分标准
  • 网站闭站网站建设 系统维护
  • 东莞短视频推广是的无锡网站关键词优化
  • 网站开发公司赚钱么辽宁建设工程信息网、
  • 南皮县做网站价格三明网站优化
  • 网站推广策划方案的主要内容?outlook企业邮箱
  • 免费做手机网站建设汽车网站代码
  • 网站分享到朋友圈代码小红书推广平台有哪些
  • 象山网站优化公司建设网站开发方案
  • 做网站建设公司crm在线广东的一起做网站
  • 相册特效手机网站阿里云虚拟主机多网站
  • 电商网站活动推广门户网站建设工作管理办法
  • 免费网站推广渠道怎样给自己的店做网站
  • 音乐盒网站源码树莓派wordpress