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

epoll单台设备支持百万并发连接

一些概念:

linux下一切接文件,文件描述符fd,文件I/O(包含socket,文本文件等),I/O多路复用,reactor模型,水平触发,边沿触发,多线程模型,阻塞和非阻塞,同步和异步

设备有限,目前实测能达到 11万并发连接。当客户端端口耗尽退出时,服务端异常退出待解决。 

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<sys/epoll.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>

#define BUFFER_LEN 128
#define MAX_CON 1024
#define EVENTS_LEN 128
#define ITEM_LEN 4096

struct sock_item {
    int fd;

    char *rbuffer;
    int rlength;

    char *wbuffer;
    int wlength;

    int event;

    void (*recv_cb)(int fd, char *buffer, int length);
    void (*send_cb)(int fd, char *buffer, int length);

    void (*accept_cb)(int fd, char *buffer, int length);
};

struct eventblock {
    struct sock_item *items;
    struct eventblock *next;
};

struct reactor {
    int epfd;
    int blkcnt;
    struct eventblock *evblk;
};

int reactor_alloc(struct reactor *r){
    if(!r) return -1;
    
    struct eventblock *blk = r->evblk;
    while(blk != NULL && blk->next != NULL) blk = blk->next;

    struct sock_item *item = (struct sock_item *)calloc(1, ITEM_LEN * sizeof(struct sock_item));
    if(!item){
        printf("no memory ret:%d %s\n", errno, strerror(errno));
        return -1;
    }

    struct eventblock *block = (struct eventblock *)calloc(1, sizeof(struct eventblock));
    if(!block) {
        free(item);
        printf("no memory ret:%d %s\n", errno, strerror(errno));
        return -1;
    }

    
    block->items = item;
    block->next = NULL;

    //blk == NULL 时表示首个节点
    if(!blk) 
        r->evblk = block;
    else 
        blk->next = block;

    r->blkcnt++;

    printf("create block:%p, cnt:%d\n", block, r->blkcnt);
    
    return 0;
}

struct sock_item* reactor_lookup(struct reactor *r, int sockfd){
    if(!r || !r->evblk || sockfd <= 0) return NULL;

    int ret;
    int blkidx = sockfd / ITEM_LEN;
    while(blkidx >= r->blkcnt){
        ret = reactor_alloc(r);
        if(ret != 0) return NULL;
    }

    int i = 0;
    struct eventblock *blk = r->evblk;
    while(i++ < blkidx) blk = blk->next;
    
    return &blk->items[sockfd % ITEM_LEN];
}

void *routine(void *arg){
    int ret;
    int clientfd = *(int *)arg;
    while(1){
        unsigned char buf[BUFFER_LEN] = {0};
        ret = recv(clientfd, buf, BUFFER_LEN, 0);
        if(ret == 0){  //客户端close,这里不做处理会产生 close_wait 状态
            close(clientfd);
            break;
        }
        printf("clientfd:%d buf:%s %d\n", clientfd, buf, ret);
        ret = send(clientfd, buf, ret, 0);
        printf("clientfd send %d\n", ret);
    }
}

int main(){
    //socket 插座,理解 socketfd 是插,bind sockaddr_in 是座
    int ret;
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd == -1) return -1;

    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);

    ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if(ret == -1) return -2;

    //fd 默认阻塞
    //设置非阻塞
#if 0
    int flag = fcntl(listenfd, F_GETFL, 0);
    flag |= O_NONBLOCK;
    fcntl(listenfd, F_SETFL, flag);
#endif
    
    listen(listenfd, 10); //类似酒店迎宾的人

#if 0
    struct sockaddr_in client;
    socklen_t len = sizeof(struct sockaddr_in);
    int clientfd = accept(listenfd, (struct sockaddr *)&client, &len);

    while(1){
        unsigned char buf[BUFFER_LEN] = {0};
        ret = recv(clientfd, buf, BUFFER_LEN, 0);
        printf("clientfd:%d %s %d\n", clientfd, buf, ret);
        ret = send(clientfd, buf, ret, 0);
        printf("clientfd send %d\n", ret);
    }
    
#elif 0
    while(1){
        struct sockaddr_in client;
        socklen_t len = sizeof(struct sockaddr_in);
        //类似酒店服务员
        int clientfd = accept(listenfd, (struct sockaddr *)&client, &len);
        pthread_t tid;
        pthread_create(&tid, NULL, routine, &clientfd);
    }

#elif 0  //<apue> pg404 select
    fd_set rfds, wfds, rset, wset;
    FD_ZERO(&rfds);
    FD_SET(listenfd, &rfds);

    FD_ZERO(&wfds);

    int maxfd = listenfd; //对 maxfd 的理解?内核 bitmap 的下标上限?

    unsigned char buf[MAX_CON][BUFFER_LEN + 1] = {{0}};
    while(1){
        rset = rfds;
        wset = wfds;
        //内核循环上限       可读集合     可写集合     出错   超时时间
        select(maxfd + 1, &rset, &wset, NULL, NULL); //只监听listen的读事件
        if(FD_ISSET(listenfd, &rset)){
            printf("listenfd event:%d\n", listenfd);

            //listen 已经有读事件,accpet该连接
            struct sockaddr_in client;
            socklen_t len = sizeof(struct sockaddr_in);
            int clientfd = accept(listenfd, (struct sockaddr *)&client, &len); //accept会清空listen读事件

            FD_SET(clientfd, &rfds);    //设置客户端的监听读事件
            if(clientfd > maxfd) maxfd = clientfd;
        }

        int i = 0;
        
        for(i = listenfd + 1; i <= maxfd; ++i){
            if(i >= MAX_CON) {
                printf("over fd %d\n", i);
                continue;
            }
            if(FD_ISSET(i, &rset)){
                
                ret = recv(i, buf[i], BUFFER_LEN, 0);
                if(ret == 0){
                    close(i);
                    FD_CLR(i, &rfds);
                    printf("close fd:%d\n", i);
                    continue;
                }
                printf("clientfd:%d %s %d\n", i, buf[i], ret);
                FD_SET(i, &wfds); //监听是否可写
            }

            if(FD_ISSET(i, &wset)){
                ret = send(i, buf[i], ret, 0);
                printf("clientfd:%d send %d\n", i, ret);
                FD_CLR(i, &wfds); //清空监听写
            }
        }
    }

#elif 0  //搜索系统调用 SYSCALL_DEFINE(N) N表示该系统调用的参数个数
    int epfd = epoll_create(1);

    struct epoll_event ev, events[EVENTS_LEN];
    ev.events = EPOLLIN;  //这里选择水平还是边沿触发;如果是边沿触发,怎么做?(while accept listenfd 读事件)
    ev.data.fd = listenfd;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); //将listenfd加入到红黑树中, 边沿触发监听listenfd的读事件

    char buf[BUFFER_LEN] = {0};
    while(1){
        int nready = epoll_wait(epfd, events, EVENTS_LEN, -1); //-1阻塞,0立刻返回, 1000为1秒返回
        printf("epoll_wait ret ready:%d\n", nready);
    
        int i = 0;
        for(i = 0; i < nready; ++i){
            int clientfd = events[i].data.fd;
            if(listenfd == clientfd){
                //accept 建立连接
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int connfd = accept(listenfd, (struct sockaddr *)&client, &len);

                printf("accept connfd:%d\n", connfd);
                ev.events =  EPOLLET | EPOLLIN;  //水平触发 和 边沿触发 的区别?
                ev.data.fd = connfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
            }else if(events[i].events & EPOLLIN){
                
                ret = recv(clientfd, buf, BUFFER_LEN, 0);
                if(ret == 0){
                    epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &events[i]);
                    close(clientfd);
                    printf("clientfd:%d closed\n", clientfd);
                    //continue;
                }
                ev.events = EPOLLOUT; //改为监听可写事件
                ev.data.fd = clientfd;
                epoll_ctl(epfd, EPOLL_CTL_MOD, clientfd, &ev);
            }else if(events[i].events & EPOLLOUT){
                ret = send(clientfd, buf, ret, 0);
                if(ret <= 0){ 
                    char *err = strerror(errno);
                    printf("clientfd:%d closed ? ret %d errno %d:%s\n", 
                                    clientfd, ret, errno, err == NULL ? "unknow": err);
                }
                printf("clientfd:%d send:%s num:%d\n", clientfd, buf, ret);
                ev.events = EPOLLIN; //改为监听可写事件
                ev.data.fd = clientfd;
                epoll_ctl(epfd, EPOLL_CTL_MOD, clientfd, &ev);
            }
        }
    }

#elif 1  //reactor 百万并发实现?
    struct reactor *r = (struct reactor *)calloc(1, sizeof(struct reactor));
    if(!r) {
        printf("memory failed\n");
        return -1;
    }
    
    ret = reactor_alloc(r);
    if(ret != 0) return -1;
    
    r->epfd = epoll_create(1);

    struct epoll_event ev;
    struct epoll_event events[EVENTS_LEN]; //准备好的事件缓冲区
    ev.events = EPOLLIN; //默认水平触发
    ev.data.fd = listenfd;
    epoll_ctl(r->epfd, EPOLL_CTL_ADD, listenfd, &ev);

    while(1){
        int nready = epoll_wait(r->epfd, events, EVENTS_LEN, -1); //-1阻塞,0立刻返回, 1000为1秒返回
        printf("epoll_wait ret ready:%d\n", nready);
    
        int i = 0;
        for(i = 0; i < nready; ++i){
            
            int clientfd = events[i].data.fd;
        
            if(listenfd == clientfd){
                //accept 建立连接
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int connfd = accept(listenfd, (struct sockaddr *)&client, &len);

                printf("accept connfd:%d\n", connfd);
                if(connfd <= 0) continue;
                
                ev.events =  EPOLLIN;  //水平触发 和 边沿触发 的区别?
                ev.data.fd = connfd;
                epoll_ctl(r->epfd, EPOLL_CTL_ADD, connfd, &ev);

#if 0
                //填充 sock_item
                assert(r->items[connfd].rbuffer == NULL);
                r->items[connfd].rbuffer = (char *)calloc(1, BUFFER_LEN);
                r->items[connfd].rlength = 0;

                assert(r->items[connfd].wbuffer == NULL);
                r->items[connfd].wbuffer = (char *)calloc(1, BUFFER_LEN);
                r->items[connfd].wlength = 0;

                r->items[connfd].event = EPOLLIN;

                assert(r->items[connfd].fd == 0);
                r->items[connfd].fd = connfd;
#endif

                struct sock_item *item = reactor_lookup(r, connfd);
                if(item == NULL) {
                    printf("error lookup item\n");
                    continue;
                }
                
                item->fd = connfd;
                assert(item->rbuffer == NULL);
                item->rbuffer = calloc(1, BUFFER_LEN);
                item->rlength = 0;

                assert(item->wbuffer == NULL);
                item->wbuffer = calloc(1, BUFFER_LEN);
                item->wlength = 0;

                printf("get item:%p, cfd:%d\n", item, item->fd);
                
            }else if(events[i].events & EPOLLIN){                
                struct sock_item *item = reactor_lookup(r, clientfd);
                printf("opr recv item:%p, item->fd:%d, connfd:%d\n", item, item->fd, clientfd);
                assert(item != NULL && item->fd == clientfd);
                
                char *rbuf = item->rbuffer;
                char *wbuf = item->wbuffer;

                assert(rbuf != NULL && wbuf != NULL);
                ret = recv(item->fd, rbuf, BUFFER_LEN, 0);
                
                if(ret == 0){
                    epoll_ctl(r->epfd, EPOLL_CTL_DEL, item->fd, &events[i]);

                    free(item->rbuffer); item->rbuffer = NULL;                
                    free(item->wbuffer); item->wbuffer = NULL;
                    
                    close(item->fd);
                    
                    printf("clientfd:%d closed\n", item->fd);
                    item->fd = 0;
                    
                    continue;
                }
                if(ret < 0) {
                    printf("clientfd:%d recv error ret:%d %s\n", item->fd, ret, strerror(errno));
                    continue;
                }

                item->rlength = ret;
    
                memcpy(wbuf, rbuf, item->rlength);
                item->wlength = ret;

                printf("opr recv item:%p, clientfd:%d send:%s num:%d\n", item, item->fd, rbuf, ret);
                
                ev.events = EPOLLOUT; //改为监听可写事件
                ev.data.fd = item->fd;
                epoll_ctl(r->epfd, EPOLL_CTL_MOD, item->fd, &ev);
                
            }else if(events[i].events & EPOLLOUT){
                struct sock_item *item = reactor_lookup(r, clientfd);
                
                printf("opr send item:%p, item->fd:%d, connfd:%d\n", item, item->fd, clientfd);
                assert(item != NULL && item->fd == clientfd);
                char *wbuf = item->wbuffer;

                assert(wbuf != NULL);
                ret = send(item->fd, wbuf, item->wlength, 0);
                if(ret <= 0){ 
                    char *err = strerror(errno);
                    printf("clientfd:%d closed ? ret %d errno %d:%s\n", 
                                    item->fd, ret, errno, err == NULL ? "unknow": err);
                    continue;
                }
                printf("opr send item:%p clientfd:%d send:%s num:%d\n", item, item->fd, wbuf, ret);
                ev.events = EPOLLIN; //改为监听可写事件
                ev.data.fd = item->fd;
                epoll_ctl(r->epfd, EPOLL_CTL_MOD, item->fd, &ev);
            }
        }
    }
    
#endif
    
    return 0;
}

相关文章:

epoll单台设备支持百万并发连接

一些概念&#xff1a; linux下一切接文件&#xff0c;文件描述符fd&#xff0c;文件I/O(包含socket&#xff0c;文本文件等)&#xff0c;I/O多路复用&#xff0c;reactor模型&#xff0c;水平触发&#xff0c;边沿触发&#xff0c;多线程模型&#xff0c;阻塞和非阻塞&#xf…...

网络字节序

文章目录网络字节序网络字节序 内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 网络数据流的地址统一按大端处理 发送主机通常将发送缓冲区中的数据按内存地址从低到高的…...

03- SVC 支持向量机做人脸识别 (项目三)

数据集描述: sklearn的lfw_people函数在线下载55个外国人图片文件夹数据集来精确实现人脸识别并提取人脸特征向量数据集地址: sklearn.datasets.fetch_lfw_people — scikit-learn 1.2.1 documentationPCA降维: pca PCA(n_components0.9) 数据拆分: X_train, X_test, y_tra…...

浅谈指向二维数组元素的指针变量

(1)指向数组元素的指针变量 例1.有一个3X4的二维数组,要求用指向元素的指针变量输出二维数组各元素的值. 编写程序 1 #include <stdio.h>2 int main()3 {4 int a[3][4] { 1,3,5,7,9,11,13,15,17,19,21,23 };5 int *p;6 for (p a[0]; p < a[0] 12; p) …...

左右值引用和移动语义

文章首发公众号&#xff1a;iDoitnow 1. 左右值和左右值引用 什么是左值、右值呢&#xff1f;一种极不严谨的理解为&#xff1a;在赋值的时候&#xff0c;能够被放到等号左边的值为左值&#xff0c;放在右边的值为右值。例如&#xff1a; int sum(int x, int y){return x y;…...

一起学习用Verilog在FPGA上实现CNN----(七)全连接层设计

1 全连接层设计 1.1 Layer 进行线性计算的单元layer&#xff0c;原理图如图所示&#xff1a; 1.2 processingElement Layer中的线性计算单元processingElement&#xff0c;原理图如图所示&#xff1a; processingElement模块展开原理图&#xff0c;如图所示&#xff0c;包含…...

tomcat打debug断点调试

windows debug调试 jdk版本&#xff1a;1.8.0_181 tomcat版本&#xff1a;apache-tomcat-9.0.68.0 idea版本&#xff1a;2020.1 方法一 修改catalina.bat 在%CATALINA_HOME%\bin\catalina.bat中找到 set “JAVA_OPTS%JAVA_OPTS% -Djava.protocol.handler.pkgsorg.apache…...

如果持有互斥锁的线程没有解锁退出了,该如何处理?

文章目录如果持有互斥锁的线程没有解锁退出了&#xff0c;该如何处理&#xff1f;问题引入PTHREAD_MUTEX_ROBUST 和 pthread_mutex_consistent登场了结论&#xff1a;如果持有互斥锁的线程没有解锁退出了&#xff0c;该如何处理&#xff1f; 问题引入 看下面一段代码&#xf…...

信息论绪论

本专栏针包含信息论与编码的核心知识&#xff0c;按知识点组织&#xff0c;可作为教学或学习的参考。markdown版本已归档至【Github仓库&#xff1a;information-theory】&#xff0c;需要的朋友们自取。或者关注公众号【AIShareLab】&#xff0c;回复 信息论 也可获取。 文章目…...

Buffer Status Reporting(BSR)

欢迎关注同名微信公众号“modem协议笔记”。 以一个实网中的异常场景开始&#xff0c;大概流程是有UL data要发送&#xff0c;UE触发BSR->no UL grant->SR->no UL grant->trigger RACH->RACH fail->RLF->RRC reestablishment&#xff1a;简单描述就是UE触…...

代码随想录LeetCode | 单调栈问题

前沿&#xff1a;撰写博客的目的是为了再刷时回顾和进一步完善&#xff0c;其次才是以教为学&#xff0c;所以如果有些博客写的较简陋&#xff0c;是为了保持进度不得已而为之&#xff0c;还请大家多多见谅。 预&#xff1a;看到题目后的思路和实现的代码。 见&#xff1a;参考…...

C++之可调用对象、bind绑定器和function包装器

可调用对象在C中&#xff0c;可以像函数一样调用的有&#xff1a;普通函数、类的静态成员函数、仿函数、lambda函数、类的非静态成员函数、可被转换为函数的类的对象&#xff0c;统称可调用对象或函数对象。可调用对象有类型&#xff0c;可以用指针存储它们的地址&#xff0c;可…...

MongoDB--》文档查询的详细具体操作

目录 统计查询 分页列表查询 排序查询 正则的复杂条件查询 比较查询 包含查询 条件连接查询 统计查询 统计查询使用count()方法&#xff0c;其语法格式如下&#xff1a; db.collection.count(query,options) ParameterTypeDescriptionquerydocument查询选择条件optio…...

网络协议(六):网络层

网络协议系列文章 网络协议(一)&#xff1a;基本概念、计算机之间的连接方式 网络协议(二)&#xff1a;MAC地址、IP地址、子网掩码、子网和超网 网络协议(三)&#xff1a;路由器原理及数据包传输过程 网络协议(四)&#xff1a;网络分类、ISP、上网方式、公网私网、NAT 网络…...

热启动预示生态起航的Smart Finance,与深度赋能的SMART通证

2023年初加密市场的回暖&#xff0c;意味着各个赛道都将在新的一年里走向新的叙事。最近&#xff0c;我们看到GameFi赛道也在市场回暖的背景下&#xff0c;逐渐走出阴霾。从融资数据上看&#xff0c;1月获得融资的GameFi项目共12个&#xff0c;融资突破8000万美元&#xff0c;1…...

提分必练,中创教育PMP全真模拟题分享

湖南中创教育每日五题分享来啦&#xff0c;“日日行&#xff0c;不怕千万里&#xff1b;常常做&#xff0c;不怕千万事。”&#xff0c;每日五题我们练起来&#xff01; 1、在系统测试期间&#xff0c;按已识别原因的类型或类别记录了失败测试的数量。项目经理首先需要从最大故…...

PID控制算法基础介绍

PID控制的概念 生活中的一些小电器&#xff0c;比如恒温热水器、平衡车&#xff0c;无人机的飞行姿态和飞行速度控制&#xff0c;自动驾驶等等&#xff0c;都有应用到 PID——PID 控制在自动控制原理中是一套比较经典的算法。 为什么需要 PID 控制器呢&#xff1f; 你一定用…...

Ajax 学习笔记

一、Ajax1.1 什么是AjaxAJAX Asynchronous JavaScript and XML(异步的JavaScript和XML)。Ajax是一种在无需加载整个网页的情况下&#xff0c;能够更新部分网页的技术&#xff0c;它不是一种新的编程语言&#xff0c;而是一种用于创建更好更快以及交互性更强的Web应用程序的技术…...

​力扣解法汇总1234. 替换子串得到平衡字符串​

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 有一个只含有 Q, W, E, R 四种字符&#xff0c;且长度为 n 的字符串。 假如在该…...

C++关键字之const、inline、static

C 关键字总结 1.const const是 constant 的缩写&#xff0c;本意是不变的、不易改变的意思。在C中用来修饰内置类型变量&#xff0c;自定义对象&#xff0c;成员函数&#xff0c;返回值&#xff0c;函数参数使用如下&#xff1a; //修饰普通类型变量 const int a 7; int ba;…...

别再只调包了!用PyTorch和DGL从零实现一个GCN层(附Cora节点分类实战代码)

从零构建图卷积网络&#xff1a;PyTorch与DGL实战中的底层逻辑拆解 当你第一次调用g.update_all()时&#xff0c;是否好奇过DGL框架背后究竟发生了什么&#xff1f;那些看似简单的消息传递和聚合操作&#xff0c;实际上隐藏着图卷积网络最精妙的设计思想。本文将带你深入GCN的数…...

【信息科学与工程学】【数据科学】 第三篇 数学基础

数学知识体系:现代核心领域的深度架构 数学知识体系:历史脉络与逻辑结构总览表 时代/脉络​ 核心分支​ 核心概念/定理/理论​ 历史渊源/思想脉络​ 与其他领域的连接​ 数学哲学/元视角​ 1. 古典起源与奠基 (公元前 ~ 16世纪)​ 算术​ 自然数、素数、整除、欧几里…...

Webots 机器人仿真平台(一) 从零到一:跨平台安装全攻略

1. Webots机器人仿真平台初探 第一次接触机器人仿真时&#xff0c;我和大多数新手一样茫然。市面上有Gazebo这样知名的仿真工具&#xff0c;但配置复杂得让人望而生畏。直到发现了Webots&#xff0c;这个开源的3D机器人仿真平台&#xff0c;才真正找到了适合初学者的入门利器。…...

你的手机变砖前兆?聊聊Android救援模式(Rescue Mode)的5次机会与触发逻辑

你的手机变砖前兆&#xff1f;聊聊Android救援模式(Rescue Mode)的5次机会与触发逻辑 最近有位朋友在群里吐槽&#xff1a;"新装的购物App让手机卡成幻灯片&#xff0c;重启三次都没用&#xff0c;最后居然弹窗问我要不要恢复出厂设置&#xff1f;"这其实是触发了And…...

告别语法冲突!用SLR分析法搞定编译原理中的移进/归约难题(附FOLLOW集实战)

告别语法冲突&#xff01;用SLR分析法搞定编译原理中的移进/归约难题&#xff08;附FOLLOW集实战&#xff09; 当你第一次尝试构建LR(0)分析表时&#xff0c;是否遇到过这样的报错&#xff1a;"状态I2存在移进/归约冲突"&#xff1f;这种既想移进又想归约的矛盾&…...

【游戏开发进阶】Unity ToLua热更新实战:从框架集成到资源加密与版本管理全流程解析

1. ToLua热更新核心价值与实现原理 热更新技术对于现代游戏开发而言&#xff0c;早已不是可选项而是必选项。想象一下这样的场景&#xff1a;你的游戏上线后突然发现致命BUG&#xff0c;传统方式需要重新打包、提交审核、等待上架&#xff0c;玩家还得重新下载安装包。这个过程…...

告别虚拟机臃肿:用QEMU+OVMF在Ubuntu上快速搭建一个32MB的极简Linux内核调试环境

极简Linux内核调试环境&#xff1a;QEMUOVMF实战指南 每次打开臃肿的虚拟机都要等待漫长的启动时间&#xff0c;看着进度条缓慢爬行&#xff0c;作为开发者的你是否感到效率被无情吞噬&#xff1f;在调试内核模块或研究启动流程时&#xff0c;我们真正需要的只是一个轻量级、即…...

FanControl深度解析:解锁Windows风扇控制的专业级配置哲学

FanControl深度解析&#xff1a;解锁Windows风扇控制的专业级配置哲学 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trendin…...

娱乐圈天降紫微星终结乱象,海棠山铁哥终结资源咖霸屏时代

资源咖的丧钟&#xff0c;已鸣。 草根王的号角&#xff0c;已响。一、旧秩序罪状书固化霸权三宗罪现场速写1. 资源垄断霸占赛道、包揽曝光、红利通吃2. 圈层护城出身即顶流&#xff0c;背景即通行证3. 劣币驱逐流水线泛滥&#xff0c;原创被碾压&#xff0c;审美被带偏 “无资源…...

ClawTick CLI:为AI Agent构建可靠任务调度与监控的实践指南

1. 项目概述&#xff1a;为AI Agent构建可靠的任务调度基础设施 如果你正在开发或使用AI Agent&#xff0c;无论是基于LangChain、CrewAI还是OpenClaw&#xff0c;迟早会遇到一个核心问题&#xff1a;如何让这些智能体定时、可靠地执行任务&#xff1f;自己写个定时脚本&#…...