epoll的LT模式(水平触发)和ET模式(边沿触发)

2023-05-16

文章目录

  • 前言
  • 名称的记忆
  • 状态变化
  • LT模式
  • ET模式
  • 数据的读取和发送
  • 代码实践
    • 基础代码
    • 测试分类
    • 怎么解决ET触发了一次就不再触发了
  • 总结

前言

epoll的触发模式是个引发讨论非常多的话题,网络上这方面总结的文章也很多,首先从名字上就不是很统一,LT模式常被称为水平触发、电平触发、条件触发,而ET模式常被称为边缘触发、边沿触发等,这些都是从英文翻译过来的,只不过翻译的时候有些差异,LT全称 level-triggered,ET全称 edge-triggered。

虽然这个知识点热度很高,但很多人对于它的理解总是差那么一点,特别是在面试的时候,很多面试者总是处于一种回忆和背诵的状态,其实这两种模式真的不需要去死记硬背,下面说说我个人对这两种模式的理解和记忆方法。

名称的记忆

每次提到ET(边沿触发)首先映入我脑海的是大学里《数字逻辑电路》这门课程,里面会提到低电平、高电平,当电平从低到高时会有一个上升沿,而电平从高到低时会有一个下降沿,这个“沿”就是边沿触发时提到的“边沿”,跟马路边的马路牙子是同一种概念,也就是指状态变化的时候。提起上升沿和下降沿我还是印象很深的,当时我可是占用了好几节课的时间用Verilog语言写了一个显示“HELLO WORLD”的仿真波形,依靠的就是电平变化中的“沿”。

状态变化

LT模式和ET模式可以类比电平变化来学习,但是在实际应用中概念却不是完全一样的,在epoll的应用中涉及到关于IO的读写,而读写的状态变化有哪些呢?可读、不可读、可写、不可写,其实就是这四种状态而已,以socket为例。

可读:socket上有数据
不可读:socket上没有数据了
可写:socket上有空间可写
不可写:socket上无空间可写

对于水平触发模式,一个事件只要有,就会一直触发。
对于边缘触发模式,只有一个事件从无到有才会触发。

LT模式

对于读事件 EPOLLIN,只要socket上有未读完的数据,EPOLLIN 就会一直触发;对于写事件 EPOLLOUT,只要socket可写(一说指的是 TCP 窗口一直不饱和,我觉得是TCP缓冲区未满时,这一点还需验证),EPOLLOUT 就会一直触发。

在这种模式下,大家会认为读数据会简单一些,因为即使数据没有读完,那么下次调用epoll_wait()时,它还会通知你在上没读完的文件描述符上继续读,也就是人们常说的这种模式不用担心会丢失数据。

而写数据时,因为使用 LT 模式会一直触发 EPOLLOUT 事件,那么如果代码实现依赖于可写事件触发去发送数据,一定要在数据发送完之后移除检测可写事件,避免没有数据发送时无意义的触发。

ET模式

对于读事件 EPOLLIN,只有socket上的数据从无到有,EPOLLIN 才会触发;对于写事件 EPOLLOUT,只有在socket写缓冲区从不可写变为可写,EPOLLOUT 才会触发(刚刚添加事件完成调用epoll_wait时或者缓冲区从满到不满)

这种模式听起来清爽了很多,只有状态变化时才会通知,通知的次数少了自然也会引发一些问题,比如触发读事件后必须把数据收取干净,因为你不一定有下一次机会再收取数据了,即使不采用一次读取干净的方式,也要把这个激活状态记下来,后续接着处理,否则如果数据残留到下一次消息来到时就会造成延迟现象。

这种模式下写事件触发后,后续就不会再触发了,如果还需要下一次的写事件触发来驱动发送数据,就需要再次注册一次检测可写事件。

数据的读取和发送

关于数据的读比较好理解,无论是LT模式还是ET模式,监听到读事件从socket开始读数据就好了,只不过读的逻辑有些差异,LT模式下,读事件触发后,可以按需收取想要的字节数,不用把本次接收到的数据收取干净,ET模式下,读事件触发后通常需要数据一次性收取干净。

而数据的写不太容易理解,因为数据的读是对端发来数据导致的,而数据的写其实是自己的逻辑层触发的,所以在通过网络发数据时通常都不会去注册监可写事件,一般都是调用 send 或者 write 函数直接发送,如果发送过程中, 函数返回 -1,并且错误码是 EWOULDBLOCK 表明发送失败,此时才会注册监听可写事件,并将剩余的服务存入自定义的发送缓冲区中,等可写事件触发后再接着将发送缓冲区中剩余的数据发送出去。

代码实践

基础代码

以下为一个epoll触发模式测试的基础代码,也不算太长,直接拿来就可以测试:

#include <sys/socket.h> //for socket
#include <arpa/inet.h>  //for htonl htons
#include <sys/epoll.h>  //for epoll_ctl
#include <unistd.h>     //for close
#include <fcntl.h>      //for fcntl
#include <errno.h>      //for errno
#include <iostream>     //for cout

class fd_object
{
public:
    fd_object(int fd) { listen_fd = fd; }
    ~fd_object() { close(listen_fd); }
private:
    int listen_fd;
};

/*
./epoll for lt mode
and
./epoll 1 for et mode
*/
int main(int argc, char* argv[])
{
    //create a socket fd
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1)
    {
        std::cout << "create listen socket fd error." << std::endl;
        return -1;
    }
    fd_object obj(listen_fd);

    //set socket to non-block
    int socket_flag = fcntl(listen_fd, F_GETFL, 0);
    socket_flag |= O_NONBLOCK;
    if (fcntl(listen_fd, F_SETFL, socket_flag) == -1)
    {
        std::cout << "set listen fd to nonblock error." << std::endl;
        return -1;
    }

    //init server bind info
    int port = 51741;
    struct sockaddr_in bind_addr;
    bind_addr.sin_family = AF_INET;
    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind_addr.sin_port = htons(port);
    if (bind(listen_fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == -1)
    {
        std::cout << "bind listen socket fd error." << std::endl;
        return -1;
    }

    //start listen
    if (listen(listen_fd, SOMAXCONN) == -1)
    {
        std::cout << "listen error." << std::endl;
        return -1;
    }
    else
        std::cout << "start server at port [" << port << "] with [" << (argc <= 1 ? "LT" : "ET") << "] mode." << std::endl;

    //create a epoll fd
    int epoll_fd = epoll_create(88);
    if (epoll_fd == -1)
    {
        std::cout << "create a epoll fd error." << std::endl;
        return -1;
    }

    epoll_event listen_fd_event;
    listen_fd_event.data.fd = listen_fd;
    listen_fd_event.events = EPOLLIN;
    if (argc > 1) listen_fd_event.events |= EPOLLET;

    //add epoll event for listen fd
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &listen_fd_event) == -1)
    {
        std::cout << "epoll ctl error." << std::endl;
        return -1;
    }

    while (true)
    {
        epoll_event epoll_events[1024];
        int n = epoll_wait(epoll_fd, epoll_events, 1024, 1000);

        if (n < 0)
            break;
        else if (n == 0) //timeout
            continue;

        for (int i = 0; i < n; ++i)
        {
            if (epoll_events[i].events & EPOLLIN)//trigger read event
            {
                if (epoll_events[i].data.fd == listen_fd)
                {
                    //accept a new connection
                    struct sockaddr_in client_addr;
                    socklen_t client_addr_len = sizeof(client_addr);
                    int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len);
                    if (client_fd == -1)
                        continue;

                    socket_flag = fcntl(client_fd, F_GETFL, 0);
                    socket_flag |= O_NONBLOCK;
                    if (fcntl(client_fd, F_SETFL, socket_flag) == -1)
                    {
                        close(client_fd);
                        std::cout << "set client fd to non-block error." << std::endl;
                        continue;
                    }

                    epoll_event client_fd_event;
                    client_fd_event.data.fd = client_fd;
                    client_fd_event.events = EPOLLIN | EPOLLOUT;
                    if (argc > 1) client_fd_event.events |= EPOLLET;

                    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_fd_event) == -1)
                    {
                        std::cout << "add client fd to epoll fd error." << std::endl;
                        close(client_fd);
                        continue;
                    }

                    std::cout << "accept a new client fd [" << client_fd << "]." << std::endl;
                }
                else
                {
                    std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;

                    char recvbuf[1024] = { 0 };
                    int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggered

                    if (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR))
                    {
                        if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)
                            std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." <<  std::endl;
                        close(epoll_events[i].data.fd);
                    }

                    std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;
                }
            }
            else if (epoll_events[i].events & EPOLLOUT)
            {
                if (epoll_events[i].data.fd == listen_fd) //trigger write event
                    continue;

                std::cout << "EPOLLOUT event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;
            }
        }
    }

    return 0;
}

简单说下这段代码的测试方法,可以使用 g++ testepoll.cpp -o epoll 进行编译,编译后通过 ./epoll 运行为LT模式,通过 ./epoll et模式运行为ET模式,我们用编译好的epoll程序作为服务器,使用nc命令来模拟一个客户端。

测试分类

  1. 编译后直接./epoll,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741 命令模拟一次连接,此时 ./epoll 会产生大量的 EPOLLOUT event triggered for client fd ...,那是因为在LT模式下,EPOLLOUT会被一直触发。
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll
start server at port [51741] with [LT] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
...
  1. 注释包含 EPOLLOUT event triggered for client fd 输出内容的第152行代码,编译后 ./epoll运行,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741 模拟一次连接后,输入abcd回车,可以看到服务器./epoll输出内容,EPOLLIN被触发多次,每次读取一个字节。
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll
start server at port [51741] with [LT] mode.
accept a new client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [b].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [c].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [d].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
  1. 还原刚才注释的那行代码,编译后执行 ./epoll et 启动服务器,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741 模拟一次连接后,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741 模拟一次连接,服务器窗口显示触发了EPOLLOUT事件
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].

在此基础上,从刚刚运行 nc命令的窗口中输入回车、输入回车、输出回车,那么epoll服务器窗口看到的是触发了三次EPOLLIN事件,每次收到一个回车:

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].

但是如果在nc模拟的客户端里输出abcd回车,那么在epoll服务器窗口触发一次EPOLLIN事件接收到一个a之后便再也不会触发EPOLLIN了,即使你在nc客户端在此输入也没有用,那是因为在接受的缓冲区中一直还有数据,新数据来时没有出现缓冲区从空到有数据的情况,所以在ET模式下也注意这种情况。

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].

怎么解决ET触发了一次就不再触发了

改代码呗,ET模式在连接后触发一次EPOLLOUT,接收到数据时触发一次EPOLLIN,如果数据没收完,以后这两个事件就再也不会被触发了,要想改变这种情况可以再次注册一下这两个事件,时机可以选择接收到数据的时候,所以可以修改这部分代码:

else
{
    std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;

    char recvbuf[1024] = { 0 };
    int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggered

    if (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR))
    {
        if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)
            std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." <<  std::endl;
        close(epoll_events[i].data.fd);
    }

    std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;
}

添加再次注册的逻辑:

else
{
    std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;

    char recvbuf[1024] = { 0 };
    int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggered

    if (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR))
    {
        if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)
            std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." <<  std::endl;
        close(epoll_events[i].data.fd);
    }

    epoll_event client_fd_event;
    client_fd_event.data.fd = epoll_events[i].data.fd;
    client_fd_event.events = EPOLLIN | EPOLLOUT;
    if (argc > 1) client_fd_event.events |= EPOLLET;

    epoll_ctl(epoll_fd, EPOLL_CTL_MOD, epoll_events[i].data.fd, &client_fd_event);

    std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;
}

这次以 ./epoll et 方式启动服务器,使用 nc -v 127.0.0.1 51741 模拟客户端,输入abc回车发现,epoll服务器输出显示触发的事件变了:

albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [b].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [c].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLOUT event triggered for client fd [5].

总结

  • LT模式会一直触发EPOLLOUT,当缓冲区有数据时会一直触发EPOLLIN
  • ET模式会在连接建立后触发一次EPOLLOUT,当收到数据时会触发一次EPOLLIN
  • LT模式触发EPOLLIN时可以按需读取数据,残留了数据还会再次通知读取
  • ET模式触发EPOLLIN时必须把数据读取完,否则即使来了新的数据也不会再次通知了
  • LT模式的EPOLLOUT会一直触发,所以发送完数据记得删除,否则会产生大量不必要的通知
  • ET模式的EPOLLOUT事件若数据未发送完需再次注册,否则不会再有发送的机会
  • 通常发送网络数据时不会依赖EPOLLOUT事件,只有在缓冲区满发送失败时会注册这个事件,期待被通知后再次发送

==>> 反爬链接,请勿点击,原地爆炸,概不负责!<<==

即使是在灿烂的阳光下也会有黑暗的角落,不能因为角落的阴暗就忽略阳光下的美好,我们要做的不是把黑暗面放大,而是要做阳光的传递者,哪怕是一面面镜子,通过反射来照亮那星星点点的黑暗,认清自己,不与黑暗为伍,那绝不是你自甘堕落的借口。

两千光束已然出发~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

epoll的LT模式(水平触发)和ET模式(边沿触发) 的相关文章

  • 【Linux学习】epoll详解

    什么是 epoll epoll 是什么 xff1f 按照 man 手册的说法 xff1a 是为处理大批量句柄而作了改进的 poll 当然 xff0c 这不是 2 6 内核才有的 xff0c 它是在 2 5 44 内核中被引进的 epoll
  • Linux epoll详解

    Linux epoll详解 一 什么是epoll epoll是什么 xff1f 按照man手册的说法 xff1a 是为处理大批量句柄而作了改进的poll 当然 xff0c 这不是2 6内核才有的 xff0c 它是在2 5 44内核中被引进的
  • epoll源码解析翻译------说使用了mmap的都是骗子

    本文地址 https www cnblogs com l2017 p 10830391 html https blog csdn net li haoren select poll epoll这三个都是对poll机制的封装 只是select
  • 【Linux学习】epoll详解

    什么是 epoll epoll 是什么 xff1f 按照 man 手册的说法 xff1a 是为处理大批量句柄而作了改进的 poll 当然 xff0c 这不是 2 6 内核才有的 xff0c 它是在 2 5 44 内核中被引进的 epoll
  • Linux epoll详解

    Linux epoll详解 一 什么是epoll epoll是什么 xff1f 按照man手册的说法 xff1a 是为处理大批量句柄而作了改进的poll 当然 xff0c 这不是2 6内核才有的 xff0c 它是在2 5 44内核中被引进的
  • JS逆向:Webpack打包后的代码怎么搞?猿人学爬虫比赛第十六题详细题解

    实战地址 http match yuanrenxue com match 16 抓包分析 地址栏输入 地址 按下F12并回车 发现数据在这里 查看cookie 无加密相关的字段 请求的接口倒是有个m的加密参数 看来这题的主要目的就是 看看m
  • linux内核epoll实现分析

    为了更好的分享体验 博客搬迁至极客驿站 欢迎查阅 epoll与select poll的区别 select poll epoll都是IO多路复用的机制 I O多路复用就通过一种机制 可以监视多个描述符 一旦某个描述符就绪 能够通知程序进行相应
  • 谈谈Linux epoll惊群问题的原因和解决方案

    近期排查了一个问题 epoll惊群的问题 起初我并不认为这是惊群导致 因为从现象上看 只是体现了CPU不均衡 一共fork了20个Server进程 在请求负载中等的时候 有三四个Server进程呈现出比较高的CPU利用率 其余的Server
  • PyWebIo

    Part1什么是 PyWebIo PyWebIO 提供了一系列命令式的交互函数来在浏览器上获取用户输入和进行输出 将浏览器变成了一个 富文本终端 可以用于构建简单的 Web 应用或基于浏览器的 GUI 应用 使用 PyWebIO 开发者能像
  • 基于升序链表的定时器及其简单应用

    Linux网络编程笔记 定时器 基于升序链表的定时器 这其实就是一个结点为 class util timer public util timer prev NULL next NULL 构造函数 public time t expire 任
  • 从零编写c++之http服务器(3)-http服务

    http全称超文本传输协议 可调试性高 扩展性也强 上两个篇章我们已经拥有了epoll事件驱动框架和线程池处理网络事件 接下来我们要先写一个基础网络套接字 然后在此基础上扩展出http的套接字 献上类图如下 完整源码见
  • 面试官:说说Node中的EventEmitter? 如何实现一个EventEmitter?

    一 是什么 我们了解到 Node采用了事件驱动机制 而EventEmitter就是Node实现事件驱动的基础 在EventEmitter的基础上 Node几乎所有的模块都继承了这个类 这些模块拥有了自己的事件 可以绑定 触发监听器 实现了异
  • 一文明白IO模型和常问多路复用机制

    1 IO模型 Socket的输入操作有两步 wait for data 等待网络传输数据到达 到达后复制到内核缓冲区 copy data from kernel to user 把数据从内核缓冲区复制到应用进程缓冲区 涉及到两个对象 pro
  • 装上后这 14 个插件后,PyCharm 真的是无敌的存在

    来源 Python编程时光 作者 写代码的明哥 Key Promoter X 如果让我给新手推荐一个 PyCharm 必装插件 那一定是 Key Promoter X 它就相当于一个快捷键管理大师 它时刻地在 教导你 当下你的这个操作 应该
  • 基于epoll实现简单的web服务器

    1 简介 epoll 是 Linux 平台下特有的一种 I O 复用模型实现 于 2002 年在 Linux kernel 2 5 44 中被引入 在 epoll 之前 Unix Linux 平台下的 I O 复用模型包含 select 和
  • EPOLLRDHUP 不可靠

    我正在通过客户端 服务器 TCP 连接使用非阻塞读 写epoll wait 问题是 我无法使用以下方法可靠地检测 对等关闭连接 事件EPOLLRDHUP旗帜 经常会发生标志未设置的情况 客户端使用close 大多数时候 服务器从epoll
  • 具有边缘触发事件的 epoll

    的手册页epoll有一个边缘触发的示例代码 如下所示 for nfds epoll wait epollfd events MAX EVENTS 1 if nfds 1 perror epoll pwait exit EXIT FAILUR
  • 你能推荐一些关于 Linux 上的 Epoll 的指南吗?

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我需要了解 Linux 系统上的 Epoll 您能推荐有关 epoll 库的手册或指南吗 需要更详细的指南 最好有一些例子 帮我 感谢您的
  • poll() 超时为 0 时会做什么?

    我正在看poll man page http man7 org linux man pages man2 poll 2 html 它告诉我的行为poll 当超时参数传入正值和负值时 它没有告诉我如果超时会发生什么0 有任何想法吗 看着epo
  • C:epoll和多线程

    我需要创建专门的 HTTP 服务器 为此我计划使用 epoll sycall 但我想利用多个处理器 核心 但我无法提出架构解决方案 ATM我的想法如下 使用自己的epoll描述符创建多个线程 主线程接受连接并将它们分配给线程epoll 但还

随机推荐

  • 使用pyinstaller将具有多个python文件的项目打包为exe(含依赖库)

    1 将需要打包有python文件放到一个文件夹 xff0c 例如下图所示的Demo文件夹 xff0c 其中ClickEveryDay py为主文件 xff0c telegram ico为图标文件 2 生成主函数对应的spec文件 命令 xf
  • 在win10下使用PowerShell批量替换文件名

    步骤 通过PowerShell ISE来创建扩展名为 ps1的脚本文件 具体操作过程参考 xff1a https www ithome com html win10 250196 htm编辑新建的 ps1文件 xff0c 举个栗子进行简单说
  • Kotlin笔记15——字符串转数字类型

    前言 在使用Java编程语言开发的 xff0c 我们会经常遇到字符串转数字的需求 那么在Kotlin中是怎么实现的呢 xff1f 接下来跟大家分享一下 字符串转数字 首先我们必须保证字符串是数字类型 xff0c 不能出现a3这种数字与字符混
  • 使用gitLab过程中遇到的一些问题

    之前由于疫情 xff0c 电脑放在公司 xff0c 有一些数据需要其他同事帮忙提交 xff0c 怎知居然连了他的git账号 xff0c 搞得我自己代码提交拉取老有问题 xff0c 一开始没有意识到是这个原因 xff0c 知道打开了自己git
  • 【Micropython】肝货~使用USB_VCP通过USB串口与树莓派或PC端通信

    为什么要使用USB VCP xff1f Micropython有很多串口 xff0c 例如PYboard xff0c 有5个串口可以使用 xff0c 但是 xff0c 平时我们在做一些项目的时候 xff0c 需要使用的引脚较多 xff0c
  • ROS中安装配置并使用VScode(持续更新)

    1 为什么使用VScode VSCode 全称 Visual Studio Code xff0c 是微软出的一款轻量级代码编辑器 xff0c 免费 开源而且功能强大 它支持几乎所有主流的程序语言的语法高亮 智能代码补全 自定义热键 括号匹配
  • pip install airsim问题

    直接使用pip install airsim安装airsim包会失败 airsim C Users DELL gt pip install airsim Collecting airsim Using cached airsim 1 8 1
  • vm虚拟机无法拖拽文件和复制粘贴解决办法

    sudo apt install open vm tools sudo apt install open vm tools desktop
  • PX4和Airsim通信操作流程

    坑真几把多 先在Windows上安装UE4和Airsim不再赘述 xff0c 官网都有 虚拟机或其他计算机安装好ubuntu并安装PX4 1 安装PX4的ROS相关包 xff08 mavros xff09 1 第一种 xff1a 进入官网安
  • mavros安装流程(超简单)

    只适用于Ubuntu18 04 在Ubuntu中新建一个空白文本 xff0c 命名为123 sh bin bash Bash script for setting up ROS Melodic with Gazebo 9 developme
  • 安装WSL2+Ubuntu18.04(慢慢更新记录)

    1 安装WSL和Ubuntu WSL官网在此 安装 WSL Microsoft Learn Windows下CMD xff0c 先安装WSL2 wsl install 然后进入Microsoft Store xff0c 搜索Ubuntu然后
  • -bash: ./Setup.sh: Permission denied

    sudo chmod 777 xxx
  • Linux 给文件夹或者文件添加权限

    chmod R 777 文件夹 参数 R是递归的意思 777表示开放所有权限 chmod 777 test sh chmod 43 x 某文件 如果给所有人添加可执行权限 xff1a chmod a 43 x 文件名 xff1b 如果给文件
  • Postman使用笔记——Postman发送get请求

    前言 在实际的开发当中 xff0c 我们经常用到get或者post请求 在这篇博客里面分享一下 xff0c 如何在Postman中发送get请求 发送get请求 1 在Postman工作空间选定get请求 图中我们可以看到很多请求方式 xf
  • jdbc连接mysql数据库的详细步骤

    标题 jdbc连接mysql数据库 1 首先在项目根目录创lLib文件夹 xff0c 放入jdbc驱动程序 xff0c 然后Add As Library 2 建包 bean包 xff1a 专门放置属性类 dao包 xff1a 进行数据操作的
  • css高度从0到auto的transition动画

    如题 xff0c 想实现css高度从0到auto的transition动画 xff0c 发现直接写没有效果 查了一下 xff0c 发现可以用max height解决 xff0c 代码如下 lt DOCTYPE html gt lt html
  • beego打包

    beego打包 在main go 对应的目录下 windows平台 xff1a bee pack be GOOS 61 windows 打包后生成一个tar gz文件 xff0c 发送到部署服务器 xff0c 解压gz为tar xff0c
  • C++求解组合数的具体实现

    文章目录 前言问题起因组合公式公式变形递推公式递归实现备忘递归动态规划压缩DP其他优化 总结补充反向递归正向递推 前言 很少写关于具体算法的总结笔记 xff0c 因为很难把一个算法从头到尾的叙述清晰并且完整 xff0c 容易造成误解 这次想
  • protobuf中SerializeToString和SerializePartialToString的区别

    文章目录 前言proto2message定义message扩展注意事项 proto3序列化SerializeToString和SerializeAsString区别SerializeToString和SerializePartialToSt
  • epoll的LT模式(水平触发)和ET模式(边沿触发)

    文章目录 前言名称的记忆状态变化LT模式ET模式数据的读取和发送代码实践基础代码测试分类怎么解决ET触发了一次就不再触发了 总结 前言 epoll的触发模式是个引发讨论非常多的话题 xff0c 网络上这方面总结的文章也很多 xff0c 首先