【IO多路转接】select编程模型

文章目录

  • 1 :peach:五种IO模型:peach:
    • 1.1 :apple:阻塞IO:apple:
    • 1.2 :apple:非阻塞IO:apple:
    • 1.3 :apple:信号驱动IO:apple:
    • 1.4 :apple:IO多路转接:apple:
    • 1.5 :apple:异步IO:apple:
    • 1.6 :apple:同步通信&异步通信:apple:
    • 1.7 :apple:阻塞&非阻塞:apple:
    • 1.8 :apple:总结:apple:
    • 1.9 :apple:其他高级IO:apple:
  • 2 :peach:非阻塞IO:peach:
  • 3 :peach:I/O多路转接之select:peach:
    • 3.1 :apple:select函数原型:apple:
    • 3.2 :apple:第一版本的SelectServer:apple:
    • 3.3 :apple:第二版本的SelectServer:apple:
    • 3.3 :apple:socket就绪条件:apple:
      • 3.3.1 :lemon:读就绪:lemon:
      • 3.3.2 :lemon:写就绪:lemon:
    • 3.4 :apple:select缺点:apple:


1 🍑五种IO模型🍑

1.1 🍎阻塞IO🍎

在内核将数据准备好之前,系统调用会一直等待。所有的套接字, 默认都是阻塞方式。
在这里插入图片描述

1.2 🍎非阻塞IO🍎

如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK错误码。非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询。这对CPU来说是较大的浪费, 一般只有特定场景下才使用。

在这里插入图片描述

1.3 🍎信号驱动IO🍎

内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作。(注意在这里的拷贝是应用程序的拷贝而不是内核的拷贝)
在这里插入图片描述

1.4 🍎IO多路转接🍎

虽然从流程图上看起来和阻塞IO类似,但实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。
在这里插入图片描述

1.5 🍎异步IO🍎

由内核在数据拷贝完成时, 通知应用程序。(而信号驱动是告诉应用程序何时可以开始拷贝数据)

在这里插入图片描述

1.6 🍎同步通信&异步通信🍎

  • 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回. 但是一旦调用返回,就得到返回值了; 换句话说,就是由调用者主动等待这个调用的结果;
  • 异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果; 换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果; 而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

另外, 我们回忆在讲多进程多线程的时候, 也提到同步和互斥。但这里的同步通信和进程之间的同步是完全不想干的概念。

1.7 🍎阻塞&非阻塞🍎

阻塞和非阻塞关注的是程序在等待调用结果(如消息,返回值)时的状态。

  • 阻塞调用是指调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回;
  • 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

1.8 🍎总结🍎

任何IO过程中, 都包含两个步骤:第一是等待,第二是拷贝。而且在实际的应用场景中, 等待消耗的时间往往都远远高于拷贝的时间。让IO更高效, 最核心的办法就是让等待的时间尽量少。

1.9 🍎其他高级IO🍎

非阻塞IO,纪录锁,系统V流机制,I/O多路转接(也叫I/O多路复用),readvwritev函数以及存储映射IO(mmap),这些统称为高级IO。
我们此处重点讨论的是I/O多路转接。


2 🍑非阻塞IO🍑

我们知道,一个文件描述符默认是阻塞IO的。
那我们应该怎样设置为非阻塞的呢?
可以使用fcntl函数:

SYNOPSIS
       #include <unistd.h>
       #include <fcntl.h>

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

传入的cmd的值不同, 后面追加的参数也不相同.
fcntl函数有5种功能:

  • 复制一个现有的描述符(cmd=F_DUPFD)
  • 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)
  • 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL)
  • 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
  • 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)

我们此处只是用第三种功能,获取/设置文件状态标记, 就可以将一个文件描述符设置为非阻塞。

void SetNoBlock(int fd)
{
    int fc=fcntl(fd, F_GETFL);
    if(fc<0)
    {
        std::cout<<"fcntl fail errno:"<<errno<<strerror(errno)<<std::endl;
        return;
    }
    fcntl(fd, F_SETFL, fc|O_NONBLOCK);
}
  • 使用F_GETFL将当前的文件描述符的属性取出来(这是一个位图)
  • 然后再使用F_SETFL将文件描述符设置回去. 设置回去的同时, 加上一个O_NONBLOCK参数

案例:轮询方式读取标准输入

void SetNoBlock(int fd)
{
    int fc=fcntl(fd, F_GETFL);
    if(fc<0)
    {
        std::cout<<"fcntl fail errno:"<<errno<<strerror(errno)<<std::endl;
        return;
    }
    fcntl(fd, F_SETFL, fc|O_NONBLOCK);
}

int main()
{
    SetNoBlock(0);
    while(true)
    {
        printf(">>>");
        fflush(stdout);
        char buffer[100];
        int n=read(0, buffer, sizeof(buffer)-1);
        if(n<0)
        {
            if(errno == EAGAIN)
            {
                std::cout<<"please try agagin"<<std::endl;
                sleep(1);
                continue;
            }
            else
            {
                std::cout << "read fail errno:" << errno << " " << strerror(errno) << std::endl;
                return 2;
            }
        }
        else if(n==0)
        {
            std::cout<<"read file end"<<std::endl;
        }
        else
        {
            buffer[n]=0;
            std::cout<<buffer;
        }
    }
    return 0;
}

代码中注意点:
当我们使用非阻塞方式进行读取时,由于我们没有输入数据所以read调用返回值会小于0(以出错形式返回),所以当返回值小于0时还得判断是否是真正的错误。
在这里插入图片描述
验证:
在这里插入图片描述


3 🍑I/O多路转接之select🍑

系统提供select函数来实现多路复用输入/输出模型:

  • select系统调用是用来让我们的程序监视多个文件描述符的状态变化的;
  • 程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变.

3.1 🍎select函数原型🍎

/* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

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



参数解释:

  • 参数nfds是需要监视的最大的文件描述符值+1;
  • readfds,writefds,exceptfds分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合;(均是输入输出型参数)
  • 参数timeout为结构timeval,用来设置select()的等待时间;

我们先来理解下这三个输入输出型参数的作用,拿readfds为例,用户假定要让操作系统帮助我们关心文件描述符3,6,8,9就绪状态,当select函数返回后,就会将已经就绪的文件描述符通过readfds参数传递出来,比如此时只有文件描述符3和8就绪,此时在readfds结构中对应的比特位就会被置1,而没有就绪的事件在位图中就会被置为0(6和9对应的比特位就被置为0)。

参数timeout取值:

  • NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件;
  • 0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生;
  • 特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回

fd_set结构:
在这里插入图片描述在这里插入图片描述
其实这个结构就是一个整数数组, 更严格的说, 是一个 "位图"结构,使用位图中对应的位来表示要监视的文件描述符。
我们可以计算下它的大小:
在这里插入图片描述
OS提供了一组操作fd_set的接口, 来比较方便的操作位图:

 void FD_CLR(int fd, fd_set *set); // 清除描述词组set中相关fd的位
 int FD_ISSET(int fd, fd_set *set); // 测试描述词组set中相关fd的位是否为真
 void FD_SET(int fd, fd_set *set); // 设置描述词组set中相关fd的位
 void FD_ZERO(fd_set *set); // 清除描述词组set的全部位

timeval结构:
timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

select函数返回值:

  • 执行成功则返回文件描述词状态已改变的个数;
  • 如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;
  • 当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds, exceptfds和timeout的值变成不可预测;

3.2 🍎第一版本的SelectServer🍎

有了上面的认识后我们来简单的编写下select服务器:
大家先看看下面的写法有什么问题?
在这里插入图片描述

  • 问题1:根据输入输出型参数的特点我们可以知道,当select函数返回时会将已经准备就绪的文件描述符设置进rfds中,同时将没有准备就绪的文件描述符从rfds清除,但是用户想要继续关心之前关心的文件描述符应该怎么办呢?
  • 问题2:当已经有n个连接到来了,此时我们能够直接Accept吗?很明显是不能够的。因为此时我们要区分文件描述符是不是_listensock,是的话用_listensock进行accept,否则的话我们使用该文件描述符进行数据IO。

有了上面的问题,我们可以选择合适的解决方式:比如我们可以创建fd数组来记录我们想要内核帮助关心的文件描述符,每次select前都用fd数组数组初始化一下rfds:

const uint16_t g_port = 8899;
const int N = sizeof(fd_set) * 8;
const int default_fd = -1;
class SelectServer
{
private:
    Sock _listensock;
    uint16_t _port;
    int _fdarr[N];

public:
    SelectServer(const uint16_t port = g_port)
        : _port(port)
    {
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

    void init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (int i = 0; i < N; ++i)
            _fdarr[i] = default_fd;
    }

    void run()
    {

        _fdarr[0] = _listensock.Fd();
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int max_fd = _fdarr[0];
            for (int i = 0; i < N; ++i)
            {
                if (_fdarr[i] != default_fd)
                    FD_SET(_fdarr[i], &rfds);
                max_fd = max(max_fd, _fdarr[i]);
            }
            // struct timeval timeout = {2, 0};
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, nullptr);
            if (n > 0)
            {
                cout << "有一个就绪事件发生了" << endl;
                // 表示已经有n个连接到来了,此时我们能够直接accept吗?
                hand_event(rfds);
                printf_fd();
            }
            else if (n == 0)
            {
                cout << "time out" << endl;
            }
            else
            {
                cout << "select errno:" << errno << ":" << strerror(errno) << endl;
            }
        }
    }

private:
    void accepter()
    {
        string clientip;
        uint16_t clientport;
        int sock = _listensock.Accept(&clientip, &clientport);
        cout << "[ip:port]:" << clientip << ":" << clientport << endl;
        int pos = 1;
        while (pos < N)
        {
            if (_fdarr[pos] == default_fd)
            {
                _fdarr[pos] = sock;
                break;
            }
            ++pos;
        }
        if (pos > N)
        {
            cout << "_fdarr full" << endl;
            close(sock);
        }
    }

    void serverio(int fd, int i)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
        if (n < 0)
        {
            cout << "read fail" << endl;
            return;
        }
        else if (n == 0)
        {
            cout << "client close,me too" << endl;
            close(fd);
            _fdarr[i]=default_fd;
        }
        else
        {
            buffer[n - 1] = 0;
            cout << "client:" << buffer << endl;
            string echo = buffer;
            echo += " [select server echo]";
            send(fd, echo.c_str(), echo.size(), 0);
        }
    }

    void hand_event(const fd_set &rfds)
    {
        for (int i = 0; i < N; ++i)
        {
            if (_fdarr[i] == _listensock.Fd() && FD_ISSET(_listensock.Fd(), &rfds))
            {
                accepter();
            }
            else if (_fdarr[i] != _listensock.Fd() && FD_ISSET(_fdarr[i], &rfds))
            {
                serverio(_fdarr[i], i);
            }
        }
    }

    void printf_fd()
    {
        for (int i = 0; i < N; ++i)
        {
            if (_fdarr[i] != default_fd)
                cout << _fdarr[i] << " ";
        }
        cout<<endl;
    }
};

上面的程序进行serverio时还会存在着下面的两个问题:

  • 我们在readwrite时并没有自定义协议读取或者发送数据,由于我们采用的是TCP协议,读取或者发送数据时都是以字节流的形式进行的,所以会存在着粘包问题,因为我们并没有定制协议读取/发送一个完整的报文;
  • 在进行read读取完数据后,使用write发送数据时也要将fd交给select管理,因为写事件是不一定就绪的。

此时我们可以来验证下:
在这里插入图片描述当我们使用另外一个用户再发起请求:

在这里插入图片描述
然后让wjb用户退出:
在这里插入图片描述

3.3 🍎第二版本的SelectServer🍎

从select函数原型我们知道select等待的条件不仅有可读,还有可写与异常,假如我们想要同时处理可写与异常应该咋办呢?
其实并不难,我们可以将fd数组替换成一个带有事件方式的自定义类型的数组。
比如参考下面的这种方式:
在这里插入图片描述
修改后的版本:

const uint16_t g_port = 8899;
const int N = sizeof(fd_set) * 8;
const int default_fd = -1;

#define READ_EVENT 0X1
#define WRITE_EVENT 0X1 << 1
#define EXPECT_EVENT 0x1 << 2

const uint8_t default_event=READ_EVENT;
struct FdEvent
{
    int fd;
    uint8_t event;
    string clientip;
    uint16_t clientport;
};

class SelectServer
{
private:
    Sock _listensock;
    uint16_t _port;
    FdEvent _fdarr[N];

public:
    SelectServer(const uint16_t port = g_port)
        : _port(port)
    {
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

    void init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (int i = 0; i < N; ++i)
        {
            _fdarr[i].fd = default_fd;
            _fdarr[i].event=default_event;
        }
    }

    void run()
    {

        _fdarr[0].fd = _listensock.Fd();
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int max_fd = _fdarr[0].fd;
            for (int i = 0; i < N; ++i)
            {
                if (_fdarr[i].fd != default_fd)
                    FD_SET(_fdarr[i].fd, &rfds);
                max_fd = max(max_fd, _fdarr[i].fd);
            }
            // struct timeval timeout = {2, 0};
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, nullptr);
            if (n > 0)
            {
                cout << "有一个就绪事件发生了" << endl;
                // 表示已经有n个连接到来了,此时我们能够直接accept吗?
                hand_event(rfds);
                printf_fd();
            }
            else if (n == 0)
            {
                cout << "time out" << endl;
            }
            else
            {
                cout << "select errno:" << errno << ":" << strerror(errno) << endl;
            }
        }
    }

private:
    void accepter()
    {
        string clientip;
        uint16_t clientport;
        int sock = _listensock.Accept(&clientip, &clientport);
        cout << "[ip:port]:" << clientip << ":" << clientport << endl;
        int pos = 1;
        while (pos < N)
        {
            if (_fdarr[pos].fd == default_fd)
            {
                _fdarr[pos].fd = sock;
                break;
            }
            ++pos;
        }
        if (pos > N)
        {
            cout << "_fdarr full" << endl;
            close(sock);
        }
    }

    void serverio(int fd, int i)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
        if (n < 0)
        {
            cout << "read fail" << endl;
            return;
        }
        else if (n == 0)
        {
            cout << "client close,me too" << endl;
            close(fd);
            _fdarr[i].fd=default_fd;
        }
        else
        {
            buffer[n - 1] = 0;
            cout << "client:" << buffer << endl;
            string echo = buffer;
            echo += " [select server echo]";
            send(fd, echo.c_str(), echo.size(), 0);
        }
    }

    void hand_event(const fd_set &rfds)
    {
        for (int i = 0; i < N; ++i)
        {
            if ((_fdarr[i].event & READ_EVENT) && FD_ISSET(_listensock.Fd(), &rfds))
            {
                if (_fdarr[i].fd == _listensock.Fd())
                {
                    accepter();
                }
                else if (_fdarr[i].fd != _listensock.Fd())
                {
                    serverio(_fdarr[i].fd, i);
                }
                else
                {

                }
            }
            else if((_fdarr[i].event & WRITE_EVENT) && FD_ISSET(_listensock.Fd(), &rfds))
            {
                
            }

        }
    }

    void printf_fd()
    {
        for (int i = 0; i < N; ++i)
        {
            if (_fdarr[i].fd != default_fd)
                cout << _fdarr[i].fd << " ";
        }
        cout<<endl;
    }
};

注意上面的代码中serverio还是有着第一个版本的两个问题:粘包问题和没有将write的fd也交给select管理(写事件不一定就绪)。

3.3 🍎socket就绪条件🍎

3.3.1 🍋读就绪🍋

  • socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT. 此时可以无阻塞的读该文件描述符, 并且返回值大于0;
  • socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0;
  • 监听的socket上有新的连接请求;
  • socket上有未处理的错误;

3.3.2 🍋写就绪🍋

  • socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记SO_SNDLOWAT, 此时可以无阻塞的写, 并且返回值大于0;
  • socket的写操作被关闭(close或者shutdown). 对一个写操作被关闭的socket进行写操作, 会触发SIGPIPE信号;
  • socket使用非阻塞connect连接成功或失败之后;
  • socket上有未读取的错误;

3.4 🍎select缺点🍎

  • 每次调用select, 都需要手动设置fd集合, 从接口使用角度来说也非常不便;
  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;
  • select支持的文件描述符数量太小;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/116736.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA快捷键总结+常识积累

&#xff08;一&#xff09;常用快捷键总结 以下快捷键输入完成后按Tab键即可。 1、输入main public static void main(String[] args) {}2、输入sout System.out.println();3、输入fori for (int i 0; i < ; i) {}4、输入foreach&#xff08;增强for循环快捷键&#x…

蓝桥杯(C++ 扫雷)

题目&#xff1a; 思想&#xff1a; 1、遍历每个点是否有地雷&#xff0c;有地雷则直接返回为9&#xff0c;无地雷则遍历该点的周围八个点&#xff0c;计数一共有多少个地雷&#xff0c;则返回该数。 代码&#xff1a; #include<iostream> using namespace std; int g[…

Prometheus接入AlterManager配置企业微信告警(基于K8S环境部署)

文章目录 一、创建企业微信机器人二、配置AlterManager告警发送至企业微信三、Prometheus接入AlterManager配置四、部署PrometheusAlterManager(放到一个Pod中)五、测试告警 注意&#xff1a;请基于 PrometheusGrafana监控K8S集群(基于K8S环境部署)文章之上做本次实验。 一、创…

装修服务预约小程序的内容如何

大小装修不断&#xff0c;市场中大小品牌也比较多&#xff0c;对需求客户来说&#xff0c;可以线下咨询也可以线上寻找品牌&#xff0c;总是可以找到满意的服务公司&#xff0c;而对装修公司来说如今线下流量匮乏&#xff0c;很多东西也难以通过线下方式承载&#xff0c;更需要…

配置Raspberry自动连接WIFI,在无法查看路由器的校园网情况下使用自己电脑热点

1、开启电脑热点&#xff0c;并共享电脑WLAN2 打开控制面板->网络和Internet->网络连接 选择自己的校园网&#xff0c;我这里是WLAN2&#xff0c;右键属性&#xff0c;如下操作&#xff1a; 如果没有看到 本地连接*10类似的图标 则按如下操作&#xff1a;winx键&#x…

SpringBoot-WebSocket浏览器-服务器双向通信

文章目录 WebSocket 介绍入门案例 WebSocket 介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输。 应用场景&#xff1a; 视…

Android - 编译 openssl 踩坑之路

一、简述 如果你想快速在项目中使用上 openssl,可以使用网上其他开发者提供好的预编译库: OpenSSL(All):https://builds.viaduck.org/prebuilts/openssl/OpenSSL(3.1.*) :https://github.com/217heidai/openssl_for_android以上的预编译库可能最低只支持 API 21(即 Andro…

windows内存取证-中等难度-下篇

上文我们对第一台Target机器进行内存取证&#xff0c;今天我们继续往下学习&#xff0c;内存镜像请从上篇获取&#xff0c;这里不再进行赘述​ Gideon 攻击者访问了“Gideon”&#xff0c;他们向AllSafeCyberSec域控制器窃取文件,他们使用的密码是什么&#xff1f; 攻击者执…

将Series中每个值v替换为v在Series中升序排列时的位置值s.rank()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将Series中每个值v 替换为v在Series中 升序排列时的位置值 s.rank() 选择题 下列代码执行三次排名索引a的名次值分别为&#xff1f; import pandas as pd s pd.Series([3,2,0,3],index list…

【RabbitMQ】RabbitMQ 集群的搭建 —— 基于 Docker 搭建 RabbitMQ 的普通集群,镜像集群以及仲裁队列

文章目录 一、集群分类1.1 普通模式1.2 镜像模式1.3 仲裁队列 二、普通集群2.1 目标集群2.2 获取 Erlang Cookie2.3 集群配置2.4 启动集群2.5 测试集群 三、镜像模式3.1 镜像模式的特征3.2 镜像模式的配置3.2.1 exactly 模式3.2.2 all 模式3.2.3 nodes 模式 3.3 测试镜像模式 四…

Adobe After Effects 2024(Ae2024)在新版本中的升级有哪些?

After Effects 2024是Adobe公司推出的一款视频处理软件&#xff0c;它适用于从事设计和视频特技的机构&#xff0c;包括电视台、动画制作公司、个人后期制作工作室以及多媒体工作室。通过After Effects&#xff0c;用户可以高效且精确地创建无数种引人注目的动态图形和震撼人心…

STM32F103C8T6第二天:按键点灯轮询法和中断法、RCC、电动车报警器(振动传感器、继电器、喇叭、433M无线接收发射模块)

1. 点亮LED灯详解&#xff08;307.11&#xff09; 标号一样的导线在物理上是连接在一起的。 将 PB8 或 PB9 拉低&#xff0c;就可以实现将对应的 LED 灯点亮。常用的GPIO HAL库函数&#xff1a; void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);//I/…

Windows ObjectType Hook 之 OkayToCloseProcedure

1、背景 Object Type Hook 是基于 Object Type的一种深入的 Hook&#xff0c;比起常用的 SSDT Hook 更为深入。 有关 Object Type 的分析见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》。 这里进行的 Hook 为 其中之一的 OkayToCloseProcedure。…

2023全新小程序广告流量主奖励发放系统源码 流量变现系统 带安装教程

2023全新小程序广告流量主奖励发放系统源码 流量变现系统 分享软件&#xff0c;吃瓜视频&#xff0c;或其他资源内容&#xff0c;通过用户付费买会员来变现&#xff0c;用户需要付费&#xff0c;有些人喜欢白嫖&#xff0c;所以会流失一部分用户&#xff0c;所以就写了这个系统…

【并行计算】多核处理器

这张图连接了几个并行计算的思想。 从上往下。 1.两个fetch/decode部件&#xff0c;是superscalar技术&#xff0c;每个cycle可以发射多个指令。 2.多个执行单元&#xff0c;支持乱序执行&#xff0c;是ILP&#xff0c;指令级并行。 3.每个执行单元里还支持SIMD操作。 4.有…

Centos 7.9.2009 firewalld 防火墙无法拦截Docker映射端口

一、linux版本&#xff1a; lsb_release -a 二、现象&#xff1a; firewalld 防火墙开启状态&#xff0c;未开放3306端口&#xff1b;但是本地依然可以链接mysql服务 三、原因&#xff1a; docker run -p 启动的时候会往iptables里面添加规则&#xff0c;firewall底层是基于…

Leetcode1122. 数组的相对排序

Every day a Leetcode 题目来源&#xff1a;1122. 数组的相对排序 解法1&#xff1a;哈希 用集合 set 存储 arr2 中的元素。 遍历数组 arr1 &#xff0c;设当前元素为 num&#xff1a; 如果 num 在 set 中出现&#xff0c;用哈希表 hash 记录 num 和它出现的次数。否则&a…

中文大语言模型汇总

推荐一篇非常棒的github&#xff1a;Awesome-Chinese-LLM 另附语言模型排行榜&#xff1a;FastChat 里面总结了几乎所有目前主流的中文大语言模型。在此记录一下&#xff0c;方便以后慢慢学习。

ELK极简上手

目录 引言 首先&#xff0c;下载相关的包 其次&#xff0c;安装启动elasticsearch 下一步&#xff0c;安装并启动logstash 最后&#xff0c;安装并启动kibana 进一步的&#xff0c;测试数据的流动 引言 最近整理电脑发现之前的一篇ELK极简入门笔记&#xff0c;现整理发出…

5G物联网关相较有线网关有哪些独特优势

5G为产业物联网应用带来了质的飞跃&#xff0c;5G技术实现更高速率、更低延迟和更大带宽&#xff0c;使得物联网能够接入更多数量的设备&#xff0c;实现更稳定、高效的连接和数据传输&#xff0c;在提高生产效率的同时&#xff0c;也进一步促进了物联网的应用发展和升级。 针对…