多路转接select系统调用详解
此文章对应五种IO模型的1.3I/O多路复用select系统调用的讲解目录1. 初识select2. select函数原型2.1 参数解释2.2 函数返回值2.3 关于fd_set结构2.4 理解readfds、writefds、exceptfds3. 理解select执行过程4. socket就绪条件5. select的特点6. select缺点7. select使用示例7.1 检测标准输入输出7.2 使用select实现网络消息传送1. 初识selectselect 是系统提供的经典 IO 多路复用实现通过 select 系统调用可搭建多路复用输入 / 输出模型是实现单进程 / 线程管理多个 IO 操作的核心接口之一核心特性如下select 系统调用的核心作用是让程序统一监视多个文件描述符的可读、可写、异常三类核心状态变化什么叫可读底层有数据读事件就绪什么叫可写底层有空间写事件就绪调用 select 后进程 / 程序会有阻塞和非阻塞状态非阻塞状态时的返回值有所不同阻塞状态直到被监视的文件描述符中有一个或多个发生了指定的状态变化或达到预设的等待超时时间阻塞才会解除。总结select就是通过等待多个fd状态变化的一种就绪事件通知机制2. select函数原型select 的函数原型如下Linux 系统编程标准接口#includesys/select.hintselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);2.1 参数解释nfds需要监视的最大文件描述符值 1文件描述符从 0 开始遍历检测此参数用于限定内核检测的范围提升效率readfds可读文件描述符集合的指针传入需监视的可读 fd返回后仅保留发生可读状态变化的 fdwritefds可写文件描述符集合的指针传入需监视的可写 fd返回后仅保留发生可写状态变化的 fdexceptfds异常文件描述符集合的指针传入需监视的异常 fd返回后仅保留发生异常状态变化的 fd注以上三个集合参数若无需监视对应状态可传入NULL。timeouttimeval结构体指针用于设置 select 的阻塞等待超时时间内核会在该时间内等待 fd 状态变化超出时间则直接返回有三种核心取值NULL永久阻塞直到被监视的 fd 中有状态变化才解除阻塞时间值全为 0非阻塞模式立即检测所有 fd 状态并返回不做任何等待设定具体时间值超时阻塞若超时前无 fd 状态变化到点后自动返回。补充timeval 结构体定义timeout 依赖的timeval结构体用于精确设置秒和微秒级超时时间定义如下structtimeval{longtv_sec;// 秒longtv_usec;// 微秒1秒10^6微秒};2.2 函数返回值select 调用的返回值为 int 类型不同返回值对应不同执行结果仅返回值大于 0 时表示有文件描述符状态发生变化具体说明如下返回值 0执行成功返回状态发生变化的文件描述符总数可读、可写、异常状态的 fd 数量累加返回值 0超时返回在timeout设定的时间内无任何被监视的文件描述符状态发生变化程序解除阻塞继续执行返回值 -1调用失败此时错误原因会存入系统全局变量errno传入的 readfds、writefds、exceptfds 和 timeout 参数值会变得不可预测不可继续使用。常见错误值errno及含义select 调用失败时errno常见的取值及对应错误原因如下EBADF传入的文件描述符为无效值或对应的文件 / 套接字已被关闭EINTRselect 调用过程中被系统信号中断如收到 SIGINT、SIGCHLD 等信号EINVAL参数非法常见原因是nfds为负值或timeout结构体中的时间值设置无效如微秒数 tv_usec≥10^6ENOMEM内核内存资源不足无法完成 select 的相关初始化操作。补充使用要点若因EINTR导致 select 调用失败通常可在代码中做重入处理重新调用 select这类情况属于可恢复的错误并非真正的程序异常。常见的程序片段如下:fs_set readsetFD_SET(fd,readset);select(fd1,readset,NULL,NULL,NULL);if(FD_ISSET(fd,readset)){……}2.3 关于fd_set结构fd_set是系统为 select 专门定义的文件描述符集合类型其底层实现为整数数组本质是一块位图通过位图中每一位的置 1 / 置 0来标识对应的文件描述符是否被纳入监视范围 ——位图的第 n 位对应文件描述符 fdn位为 1 表示监视该 fd位为 0 表示不监视。为避免直接操作位图带来的位运算错误、提升代码可读性系统提供了一组专用的接口函数来操作fd_set无需开发者手动处理位运算接口如下#includesys/select.h// 清除描述符集合set中fd对应的位置0取消对该fd的监视voidFD_CLR(intfd,fd_set*set);// 检测描述符集合set中fd对应的位是否为1非0表示已置10表示未置1intFD_ISSET(intfd,fd_set*set);// 设置描述符集合set中fd对应的位置1加入对该fd的监视voidFD_SET(intfd,fd_set*set);// 将描述符集合set中所有位清0初始化空集合voidFD_ZERO(fd_set*set);2.4 理解readfds、writefds、exceptfds这三个参数是输入输出型参数以readfds为例其余同理输入时用户告诉内核你要帮我关心哪些fd上的读事件比特位的位置表示fd编号比特位的内容表示是否关心。例如0000 1111表示只关心0~3这四个fd输出时内核告诉用户你让我关心的哪些fd上面的读事件已经就绪比特位的位置表示fd的编号比特位的内容表示是否就绪例如0000 0010表示只有fd1的文件描述符的内容是就绪的。fd_set是位图结构一次性可以向里面添加多个fd细节位图是输入输出的所以将来这个位图一定会被频繁变更位图有多少个比特位就决定了select最多能关心多少个fd。fd_set是一个系统提供的数据类型(struct)fd_set大小固定readfds如果把fd添加到readfds集合中表示改fd只关心读事件。告诉内核你只需要帮我关心fd的读事件。3. 理解select执行过程理解 select 模型的核心是掌握内核对 fd_set 的修改逻辑。为简化理解我们做一个经典的简化假设取fd_set的长度为 1 字节实际系统中fd_set长度由宏FD_SETSIZE定义Linux 默认对应 1024 个 fdfd_set的每 1 个二进制位对应 1 个文件描述符 fd位图的第 n 位对应文件描述符 fdn1 字节的fd_set最多可监视 fd0~7 共 8 个文件描述符。以下通过可读事件监视的完整步骤直观演示 select 的执行过程定义并初始化 fd_set 集合执行fd_set set; FD_ZERO(set);将集合所有位清 0此时 set 的二进制表示为0000 0000添加需监视的 fd若要监视 fd5 的可读事件执行FD_SET(5, set);将第 5 位置 1此时 set 变为0001 0000继续添加监视 fd再依次执行FD_SET(2, set);、FD_SET(1, set);将第 2 位、第 1 位置 1此时 set 变为0001 0011调用 select 阻塞等待执行select(6, set, NULL, NULL, NULL);进入阻塞状态。 关键参数呼应nfds6是监视的最大 fd51后三个参数为 NULL表示仅监视可读事件、不监视可写 / 异常事件、永久阻塞直到有 fd 触发事件内核检测到事件select 解除阻塞并返回若此时 fd1、fd2 上触发了可读事件fd5 无任何事件select 会立即返回返回值为 2对应 2 个状态变化的 fd内核会重写传入的 fd_set 集合仅保留触发事件的 fd 对应位为 1未触发的位全部清 0此时 set 变为0000 0011fd5 对应的位被清空。核心执行要点select 的执行过程本质是用户层设置监视集合 → 内核层检测 fd 状态 → 内核重写集合并返回的过程其中最关键的是select 调用返回后原传入的 fd_set 会被内核覆盖重写仅保留触发对应事件的 fd 位为 1未触发事件的 fd 位会被强制清 0正因内核会重写 fd_set每次重新调用 select 前都需要重新执行 FD_ZERO 初始化 FD_SET 设置监视 fd否则会因脏数据导致监视异常。4. socket就绪条件select 监视 socket 触发的可读、可写、异常事件本质是检测 socket 满足就绪条件只有当 socket 符合对应就绪规则时内核才会将其标记为状态变化触发 select 返回。以下是 Linux 下 socket 触发可读、可写、异常事件的核心就绪条件其中异常就绪为选学内容。读就绪socket 触发可读事件的条件满足以下任一条件socket 即处于读就绪状态对其执行无阻塞读操作可直接获取结果socket 内核接收缓冲区中的数据字节数大于等于低水位标记 SO_RCVLOWATLinux 系统默认值为 1 字节此时无阻塞读会成功且返回值大于 0TCP 通信中对端主动关闭连接发送 FIN 包此时对该 socket 执行读操作会直接返回 0表示读到流结束处于 listen 状态的监听 socket上有新的客户端连接请求此时调用 accept () 可无阻塞获取新的连接描述符socket 上存在未处理的错误此时对其执行无阻塞读操作会返回 - 1且系统全局变量 errno 会置为对应的错误码。写就绪socket 触发可写事件的条件满足以下任一条件socket 即处于写就绪状态对其执行无阻塞写操作可直接获取结果socket 内核发送缓冲区的可用空闲字节数剩余可写入空间大于等于低水位标记 SO_SNDLOWATLinux 系统默认值为 1024 字节此时无阻塞写会成功且返回值大于 0socket 的写端被关闭如调用 shutdown (fd, SHUT_WR) 关闭写端或 TCP 对端关闭连接此时对该 socket 执行写操作会触发SIGPIPE 信号若忽略该信号写操作会返回 - 1对 socket 执行非阻塞 connect ()后连接成功建立或连接失败此时 socket 会触发写就绪可通过 getsockopt () 获取连接结果socket 上存在未处理的错误此时对其执行无阻塞写操作会返回 - 1且系统全局变量 errno 会置为对应的错误码。异常就绪选学socket 触发异常就绪的核心条件为socket 接收到 TCP 带外数据紧急数据。带外数据与 TCP 协议的紧急模式相关TCP 协议头中的紧急指针字段用于标识带外数据的位置内核会为带外数据开辟独立的缓冲区不会与普通数据混存带外数据的传输优先级高于普通数据触发异常就绪后可通过 recv ()/recvmsg () 的 MSG_OOB 标志读取带外数据同学们可课后自行收集相关资料深入学习。5. select的特点select 作为经典的 IO 多路复用实现其设计特性决定了使用上的核心特点同时也存在明显的固有局限性核心特点如下可监视的文件描述符数量存在硬限制select 能监视的最大 fd 数量由fd_set的底层大小决定而fd_set的容量由系统宏FD_SETSIZE固定定义与sizeof(fd_set)一一对应。例如服务器上sizeof(fd_set)512字节每 1 位对应 1 个 fd则最大可监视512*84096个文件描述符常见 Linux 系统默认FD_SETSIZE1024即sizeof(fd_set)128字节默认最大监视 1024 个 fd。该限制为内核级硬限制若需调整需重新编译内核开发中一般不建议修改这也是 select 在高并发场景下的主要缺陷。必须额外维护独立数据结构保存待监视的 fd由于 select 返回后内核会清空 fd_set 中无事件发生的 fd 对应位仅保留触发事件的 fd 位导致原监视集合被覆盖无法复用。因此实际开发中需在将 fd 加入 select 监视集合的同时额外使用一个独立的数组如 int 数组保存所有待监视的 fd该数组的核心作用有两点作为 select 返回后的检测源select 返回后遍历该数组结合FD_ISSET逐个判断数组中的 fd 是否在改写后的 fd_set 中以此确定具体哪个 fd 触发了事件避免盲目遍历所有可能的 fd提升检测效率作为重新初始化监视集合的数据源每次调用 select 前需先执行FD_ZERO清空 fd_set再遍历该数组通过FD_SET将所有待监视 fd 重新加入集合同时遍历数组的过程中可同步获取待监视 fd 的最大值maxfd为 select 的第一个参数nfdsmaxfd1提供准确值。延伸使用要点正因为 select 存在 “需重新初始化监视集合” 的特点每次调用 select 都需要执行 FD_ZERO、FD_SET 的初始化操作且需遍历数组获取 maxfd这些操作会随着待监视 fd 数量的增加带来额外的性能开销这也是 select 在高并发场景下性能下降的原因之一。6. select缺点select 虽实现了 IO 多路复用但受限于设计年代的技术背景存在多处固有缺陷使其在高并发网络场景下性能表现不佳核心缺点如下接口使用繁琐需手动重复初始化 fd 集合因 select 返回后内核会覆盖重写 fd_set每次调用 select 前都必须手动执行 FD_ZERO 清空集合 FD_SET 重新添加待监视 fd且需额外维护独立数组保存 fd从开发使用角度来说操作繁琐、代码冗余。fd 集合存在用户态到内核态的频繁拷贝开销每次调用 select都需要将用户态的 fd_set 完整拷贝到内核态待监视的 fd 数量越多拷贝的数据量越大系统内存和 CPU 的开销会显著增加。内核层存在全量 fd 遍历检测开销内核接收到拷贝后的 fd_set 后会逐个遍历所有待监视的 fd检测其是否处于就绪状态fd 数量越多遍历的耗时越长高并发场景下该操作会成为明显的性能瓶颈。可监视的 fd 数量存在内核级硬限制能监视的最大 fd 数由系统宏FD_SETSIZE固定定义默认多为 1024该限制无法通过简单的代码修改突破需重新编译内核完全无法适配高并发的网络服务场景。7. select使用示例7.1 检测标准输入输出只检测标准输入:#includestdio.h#includeunistd.h#includesys/select.hintmain(){fd_set read_fds;FD_ZERO(read_fds);FD_SET(0,read_fds);for(;;){printf( );fflush(stdout);intretselect(1,read_fds,NULL,NULL,NULL);if(ret0){perror(select);continue;}if(FD_ISSET(0,read_fds)){charbuf[1024]{0};read(0,buf,sizeof(buf)-1);printf(input: %s,buf);}else{printf(error! invaild fd\n);continue;}FD_ZERO(read_fds);FD_SET(0,read_fds);}return0;}说明当只检测文件描述符0标准输入时因为输入条件只有在你有输入信息的时候才成立所以如果一直不输入就会产生超时信息。7.2 使用select实现网络消息传送main函数创建SelectServer类型的智能指针svr构造函数创建TcpSocket套接字将存储文件描述符的数组清空svr调用SelectServer类中的Start函数创建select函数需要的变量fd_set rfds使用FD_ZERO函数将rfds清空select函数的第一个参数需要放置最大文件描述符1所以要判断并存放最大文件描述符调用select函数timeout使用nullptr阻塞模式跟据select函数的返回值n来判断是否可以调用后续处理函数当n0时表示有事件就绪调用Dispatcher事件派发器函数找到已经就绪的文件描述符判断是连接就绪还是普通读事件就绪并执行不同的后续函数如果是连接就绪调用Accepter连接管理器函数添加连接如果连接超过限制则关闭连接请求如果是普通事件就绪调用IO处理器Recver函数。此处只使用了读事件功能是将收到的数据进行打印代码测试指令make # 编译./selectserver 8080 # 开启服务端telnet 127.0.0.1 8080 # 本机客户端测试新开终端连接成功后输入任意数据即可在服务端回显【免费】linux网络-多路转接select的使用资源-CSDN下载SelectServer.hpp#pragmaonce#includeiostream#includememory#includeunistd.h#includeSocket.hpp#includeLog.hppusingnamespaceSocketModule;usingnamespaceLogModule;classSelectServer{conststaticintsizesizeof(fd_set)*8;conststaticintdefaultfd-1;public:SelectServer(intport):_listensock(std::make_uniqueTcpSocket()),_isrunning(false){_listensock-BuildTcpSocketMethod(port);for(inti0;isize;i){_fd_array[i]defaultfd;}_fd_array[0]_listensock-Fd();}voidStart(){_isrunningtrue;while(_isrunning){// 因为listensockfd 也是一个fd 进程怎么知道listenfd上面有新连接到来呢// auto res _listensock-Accept(); // 我们在select 这里可以进行accpet马// 将listencoskfd添加到select内部 让OS帮我关心listensockfd上面的读事件fd_set rfds;// 定义 读 fds集合FD_ZERO(rfds);// 清空fdsintmaxfddefaultfd;for(inti0;isize;i){if(_fd_array[i]defaultfd)continue;// 1.每次select之前都要对rfds进行重置FD_SET(_fd_array[i],rfds);// 有没有设置到内核中 1 or 0// 2.最大fd一定是变化的if(maxfd_fd_array[i]){maxfd_fd_array[i];// 更新出最大fd}}PrintFd();// struct timeval timeout {2, 0};// select 返回之后你怎么还知道哪些fd需要被添加到rfds让select关心呢// 所以select要进行完整的设计需要借助一个辅助数组保存服务器历史获取过的所有的fdintnselect(maxfd1,rfds,nullptr,nullptr,nullptr);switch(n){case-1:LOG(LogLevel::ERROR)select error;break;case0:LOG(LogLevel::INFO)time out ...;break;default:LOG(LogLevel::DEBUG)有事件就绪了..., n:n;Dispatcher(rfds);break;}}_isrunningfalse;}// 事件派发器voidDispatcher(fd_setrfds){// 不仅仅是新连接到来也包括读事件就绪 // 指定的文件描述符在rfds里面就证明该fd就绪了for(inti0;isize;i){if(_fd_array[i]defaultfd)continue;// fd合法不一定就绪if(FD_ISSET(_fd_array[i],rfds)){// fd_array[i] 上面一定是读就绪了// listensockfd 新连接到来也是读事件就绪// sockfd 数据到来读事件就绪if(_fd_array[i]_listensock-Fd()){// listensockfd 新连接到来Accepter();}else{// 普通的事件就绪Recver(_fd_array[i],i);}}}}// 链接管理器:将新连接的文件描述符添加到_fd_array数组中voidAccepter(){InetAddr client;intsockfd_listensock-Accept(client);// accept会不会阻塞if(sockfd0){// 获取新连接到来成功然后呢能不能直接read/recvcoskfd是否读就绪我们不清楚// 只有谁最清楚未来sockfd上是否有事件就绪答select!// 将新的sockfd托管给select// 如何托管将新的fd放入辅助数组LOG(LogLevel::INFO)get a new link, sockfd: sockfd, client is: client.StringAddr();intpos0;for(;possize;pos){if(_fd_array[pos]defaultfd)break;}if(possize){LOG(LogLevel::WARNING)select server full;close(sockfd);}else{_fd_array[pos]sockfd;}}}// IO处理器voidRecver(intfd,intpos){charbuffer[1024];// 我在这里读取的时候会不会阻塞ssize_t nrecv(fd,buffer,sizeof(buffer)-1,0);// recv写的时候有bug吗if(n0){buffer[n]0;std::coutclient say bufferstd::endl;}elseif(n0){LOG(LogLevel::INFO)client quit...;// 1.不要让select在关心这个fd了_fd_array[pos]defaultfd;// 2.关闭fdclose(fd);}else{LOG(LogLevel::ERROR)recv error;// 1.不要让select在关心这个fd了_fd_array[pos]defaultfd;// 2.关闭fdclose(fd);}}voidPrintFd(){std::cout_fd_array[]: ;for(inti0;isize;i){if(_fd_array[i]defaultfd)continue;std::cout_fd_array[i] ;}std::cout\r\n;}voidStop(){_isrunningfalse;}~SelectServer(){}private:std::unique_ptrSocket_listensock;bool_isrunning;int_fd_array[size];};Main.cc#includeSelectServer.hppintmain(intargc,char*argv[]){if(argc!2){std::coutUsage: argv[0] portstd::endl;exit(USAGE_ERR);}uint16_tportstd::stoi(argv[1]);std::unique_ptrSelectServersvrstd::make_uniqueSelectServer(port);svr-Start();return0;}

相关新闻

# 云南茶叶数据分析系统(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

# 云南茶叶数据分析系统(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

云南茶叶数据分析系统(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码项目亮点基于大数据技术栈构建的完整茶叶行业分析平台实现从数据采集到可视化的全链路处理多维度分析:销售趋势、茶叶类型、产地、价格等前后端…

2026/5/17 3:27:08 阅读更多 →
【开题答辩全过程】以 基于SSM的电子书店管理系统设计与实现为例,包含答辩的问题和答案

【开题答辩全过程】以 基于SSM的电子书店管理系统设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人,语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

2026/7/3 12:57:59 阅读更多 →
文【牛客tracker  每日一题】

文【牛客tracker 每日一题】

文 时间限制:1秒 空间限制:256M 知识点:枚举 网页链接 牛客tracker 牛客tracker & 每日一题,完成每日打卡,即可获得牛币。获得相应数量的牛币,能在【牛币兑换中心】,换取相应奖品&…

2026/7/4 20:43:38 阅读更多 →

最新新闻

基于YOLOv8-seg的高精度道路缺陷检测系统开发

基于YOLOv8-seg的高精度道路缺陷检测系统开发

1. 项目背景与核心价值道路缺陷检测是智慧交通和市政养护领域的关键技术痛点。传统人工巡检方式存在效率低、漏检率高、主观性强等问题,尤其在夜间或恶劣天气条件下表现更差。我们团队基于YOLOv8-seg框架,融合EfficientRepBiPAN、AFPN-P345等50余项创新改…

2026/7/4 22:50:52 阅读更多 →
AI技术决策指南:从信息过载到可执行落地

AI技术决策指南:从信息过载到可执行落地

1. 项目概述:一份AI领域 Newsletter 的真实价值拆解“This AI newsletter is all you need #60”——看到这个标题,你第一反应可能是:又一份泛泛而谈的AI资讯合集?点开就看三行摘要、五个链接、一个ChatGPT新插件预告,…

2026/7/4 22:46:48 阅读更多 →
TC78H660FTG与PIC18F86J10的直流电机驱动优化方案

TC78H660FTG与PIC18F86J10的直流电机驱动优化方案

1. 项目背景与核心器件选型在工业自动化和消费电子领域,直流电机驱动系统的效率优化一直是工程师面临的关键挑战。TC78H660FTG作为东芝新一代H桥驱动器,与Microchip的PIC18F86J10微控制器组合,为解决这一问题提供了高性价比方案。TC78H660FTG…

2026/7/4 22:46:48 阅读更多 →
AntiDupl终极指南:三步快速清理重复照片,释放磁盘空间

AntiDupl终极指南:三步快速清理重复照片,释放磁盘空间

AntiDupl终极指南:三步快速清理重复照片,释放磁盘空间 【免费下载链接】AntiDupl A program to search similar and defect pictures on the disk 项目地址: https://gitcode.com/gh_mirrors/an/AntiDupl AntiDupl是一款专业的开源图片去重工具&a…

2026/7/4 22:42:44 阅读更多 →
基于STM32和MAX9744的高效D类音频放大器设计

基于STM32和MAX9744的高效D类音频放大器设计

1. 项目背景与核心器件选型在音频系统设计中,功率放大环节直接决定了最终的声音表现。传统AB类放大器虽然音质优秀,但效率普遍低于50%,导致发热严重、能耗高。而D类放大器采用PWM调制技术,理论效率可达90%以上,特别适合…

2026/7/4 22:40:42 阅读更多 →
Java毕设选题推荐:景观设计作品展示与项目管理系统的设计与实现 基于 SpringBoot 的园林素材资源管理系统【附源码、mysql、文档、调试+代码讲解+全bao等】

Java毕设选题推荐:景观设计作品展示与项目管理系统的设计与实现 基于 SpringBoot 的园林素材资源管理系统【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/4 22:38:41 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻