
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- 前言
- [1 ~> poll 整体定位与设计背景](#1 ~> poll 整体定位与设计背景)
-
- [1.1 IO 多路转接的核心思想](#1.1 IO 多路转接的核心思想)
- [1.2 select 的固有缺陷(poll 诞生的核心原因)](#1.2 select 的固有缺陷(poll 诞生的核心原因))
- [1.3 poll 相比于select的核心优化方向](#1.3 poll 相比于select的核心优化方向)
- [2 ~> poll 核心 API 与参数详解](#2 ~> poll 核心 API 与参数详解)
-
- [2.1 函数原型与依赖头文件](#2.1 函数原型与依赖头文件)
-
- [2.1.1 基础函数声明](#2.1.1 基础函数声明)
- [2.1.2 返回值定义](#2.1.2 返回值定义)
- [2.2 三大参数完整解析](#2.2 三大参数完整解析)
-
- [2.2.1 参数一:`struct pollfd *fds`](#2.2.1 参数一:
struct pollfd *fds) - [2.2.2 参数二:`nfds_t nfds`](#2.2.2 参数二:
nfds_t nfds) - [2.2.3 参数三:`int timeout`](#2.2.3 参数三:
int timeout)
- [2.2.1 参数一:`struct pollfd *fds`](#2.2.1 参数一:
- [3 ~> 核心结构体 pollfd 与事件宏](#3 ~> 核心结构体 pollfd 与事件宏)
-
- [3.1 struct pollfd 结构体定义](#3.1 struct pollfd 结构体定义)
-
- [3.1.1 字段细则](#3.1.1 字段细则)
- [3.2 标准事件宏(比特位标识)](#3.2 标准事件宏(比特位标识))
-
- [3.2.1 可读类事件(可作为入参、出参 -- 输入输出类型参数)](#3.2.1 可读类事件(可作为入参、出参 -- 输入输出类型参数))
- [3.2.2 可写类事件(可作为入参、出参 -- 输入输出类型参数)](#3.2.2 可写类事件(可作为入参、出参 -- 输入输出类型参数))
- [3.2.3 异常类事件(仅内核回填,不可作为入参)](#3.2.3 异常类事件(仅内核回填,不可作为入参))
- [3.2.4 扩展事件](#3.2.4 扩展事件)
- [3.2.5 常用组合规则](#3.2.5 常用组合规则)
- [4 ~> 基于 select 改造 Poll 服务端(C++ 代码实战)](#4 ~> 基于 select 改造 Poll 服务端(C++ 代码实战))
-
- [4.1 公共依赖说明](#4.1 公共依赖说明)
- [4.2 PollServer 核心类实现(PollServer.hpp)](#4.2 PollServer 核心类实现(PollServer.hpp))
- [4.3 主函数入口(Main.cc(Main.cc))](#4.3 主函数入口(Main.cc))
- [4.4 编译脚本(Makefile)](#4.4 编译脚本(Makefile))
- [4.5 代码逻辑与运行说明](#4.5 代码逻辑与运行说明)
-
- [4.5.1 整体执行流程](#4.5.1 整体执行流程)
- [4.5.2 不同 timeout 运行效果](#4.5.2 不同 timeout 运行效果)
- [4.5.3 边界场景处理](#4.5.3 边界场景处理)
- [4.6 优化扩展方向](#4.6 优化扩展方向)
- [5 ~> poll 与 select 深度对比 & 技术优劣分析](#5 ~> poll 与 select 深度对比 & 技术优劣分析)
-
- [5.1 poll 相对 select 的核心优势](#5.1 poll 相对 select 的核心优势)
- [5.2 poll 遗留的共性问题(与 select 一致)](#5.2 poll 遗留的共性问题(与 select 一致))
- [6 ~> 收尾总结:重难点、面试考点](#6 ~> 收尾总结:重难点、面试考点)
-
- [6.1 核心概念考点](#6.1 核心概念考点)
- [6.2 代码实操考点](#6.2 代码实操考点)
- [6.3 面试 & 原理高频考点](#6.3 面试 & 原理高频考点)
- [6.4 学习延伸方向](#6.4 学习延伸方向)
- [7 ~> 多路转接Poll相关知识图谱](#7 ~> 多路转接Poll相关知识图谱)
- 结尾

前言
一、 开头部分(框架引入)
导入语
在 Linux 网络编程中,IO 多路转接是实现单线程高并发网络服务 的核心技术,select 是最早落地的多路转接方案,但存在文件描述符上限、参数频繁重置、输入输出参数耦合等设计缺陷。poll 作为 select 的迭代版本,针对性优化了上述短板,同时沿用了事件等待、就绪派发的核心思想,是介于 select 与高性能 epoll 之间的经典过渡方案。本文将从设计背景、函数原型、核心结构体、事件宏、代码实战、运行特性、优劣对比等维度,完整梳理 poll 的全套知识点,同时结合可运行 C++ 服务端代码完成落地验证,形成闭环的知识体系。
文章框架思维导图
bash
poll 多路转接 知识总览
├─ 基础认知
│ ├─ 定位:IO多路转接方案、就绪事件派发机制
│ └─ 诞生原因:解决 select 三大缺陷
├─ 核心API
│ ├─ 函数原型 & 头文件
│ ├─ 参数详解:fds / nfds / timeout
│ └─ 返回值含义
├─ 核心结构体 pollfd
│ ├─ fd:待监听文件描述符(-1表示忽略)
│ ├─ events:用户设置 → 内核监听事件(入参)
│ └─ revents:内核回填 → 实际发生事件(出参)
├─ 事件宏定义(比特位标识)
│ ├─ 可读类事件:POLLIN / POLLRDNORM / POLLPRI 等
│ ├─ 可写类事件:POLLOUT / POLLWRNORM 等
│ └─ 异常类事件:POLLERR / POLLHUP / POLLNVAL(仅出参)
├─ 超时 timeout 三种工作模式
│ ├─ timeout < 0:永久阻塞
│ ├─ timeout = 0:纯非阻塞轮询
│ └─ timeout > 0:限时阻塞 + 超时轮询
├─ 代码实战:基于 select 改造 Poll 服务端
│ ├─ 整体架构:服务类封装、监听套接字初始化
│ ├─ 核心逻辑:事件循环、新连接接收、IO读写处理
│ ├─ 边界处理:客户端断开、服务满载、异常捕获
│ └─ 编译配置 & 运行测试
├─ poll 与 select 对比
│ ├─ 优化点:无fd上限、参数解耦、无需每次重置
│ └─ 遗留问题:仍需全量遍历fd数组、内核拷贝开销依旧存在
└─ 重难点总结 & 考点梳理
1 ~> poll 整体定位与设计背景
1.1 IO 多路转接的核心思想
IO 多路转接的本质是由内核代为监听多个文件描述符的 IO 事件,当任意一个描述符就绪(可读、可写、异常)时,主动通知应用程序,应用程序再执行对应的读写逻辑。该模型可以使用单线程管理大量文件描述符,避免多线程 / 多进程带来的资源开销与线程安全问题,是高并发网络服务的基础模型。
poll 与 select 属于同类型的多路转接实现,核心工作逻辑完全一致:阻塞等待事件就绪 → 遍历就绪描述符 → 执行业务逻辑。
1.2 select 的固有缺陷(poll 诞生的核心原因)
select 在实际使用中暴露了三处关键短板,这也是 poll 被设计出来的核心动机:
- 文件描述符存在硬上限 :
select使用fd_set位图结构存储文件描述符,位图长度固定,系统默认限制了最大可监听的 fd 数量,无法动态扩容。 - 输入输出参数耦合 :
fd_set同时作为入参(告诉内核需要监听哪些 fd)和出参(内核回填就绪的 fd),每次调用select后位图都会被内核修改,应用程序必须每次循环都重新重置监听集合,增加代码冗余与运行开销。 - 调用后需全量遍历 :无论有多少 fd 就绪,应用程序都必须遍历整个 fd 集合判断就绪状态,该问题
poll并未解决,直至epoll才完成优化。
1.3 poll 相比于select的核心优化方向
poll 针对性修复了 select 的前两大缺陷,同时保留了多路转接的基础逻辑:
- 摒弃固定长度位图,使用动态数组管理文件描述符,理论上无 fd 数量硬上限(仅受系统内存限制)。
- 将监听事件 和就绪事件拆分为两个独立字段,实现入参、出参解耦,无需每次调用后重置监听集合。
2 ~> poll 核心 API 与参数详解
2.1 函数原型与依赖头文件
2.1.1 基础函数声明
c
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- 头文件:
<poll.h>,属于标准 C 库(libc),编译时无需额外链接库。 - 扩展函数
ppoll:功能与poll一致,增加信号屏蔽字支持,日常网络编程优先使用基础poll。
2.1.2 返回值定义
poll 的返回值为整型,三种结果对应不同场景:
- 返回值 > 0 :代表有
N个文件描述符产生就绪事件,N为就绪 fd 的总数。 - 返回值 = 0:代表监听超时,没有任何 fd 事件就绪。
- 返回值 = -1 :代表函数调用出错,可通过
errno获取具体错误信息。
2.2 三大参数完整解析
2.2.1 参数一:struct pollfd *fds
指向 pollfd 结构体数组的首地址,数组中每一个元素对应一个待监听的文件描述符及事件配置,是 poll 实现多 fd 监听的核心载体。数组可动态申请内存,因此突破了 select 的 fd 数量限制。
2.2.2 参数二:nfds_t nfds
nfds_t 是系统自定义无符号整型,代表 **fds 数组的有效元素个数 **,即当前需要内核监听的文件描述符总数。
2.2.3 参数三:int timeout
超时时间,单位为毫秒 (ms),区分三种工作模式,是控制阻塞行为的核心参数:
- timeout < 0(常用值 -1):永久阻塞。内核持续监听事件,直到至少一个 fd 就绪才返回。
- timeout = 0:非阻塞轮询。内核仅做一次事件检测,无论是否有就绪事件都立即返回,配合循环可实现轮询逻辑。
- timeout > 0:限时阻塞。内核阻塞等待指定毫秒数,期间有事件就绪则立即返回;超时无事件则返回 0。
3 ~> 核心结构体 pollfd 与事件宏
3.1 struct pollfd 结构体定义
poll 通过结构体分离监听事件 与就绪事件 ,彻底解决 select 参数耦合问题,结构体定义如下:
c
struct pollfd
{
int fd; // 待监听的文件描述符
short events; // 入参:用户设置,需要内核监听的事件(比特位)
short revents; // 出参:内核回填,当前fd实际触发的事件(比特位)
};
3.1.1 字段细则
- fd
- 有效值:合法的文件描述符(套接字、管道、文件等)。
- 特殊值
-1:内核会直接忽略该数组元素,不做任何监听,常用于标记数组中闲置位置。
- events 由应用程序主动赋值,使用比特位 组合多个监听事件,调用
poll时传递给内核。调用后该字段不会被内核修改,无需重复赋值。 - revents 初始置 0,
poll调用结束后由内核填充当前 fd 实际发生的事件。该字段仅作为结果输出,应用程序读取后自行处理。
3.2 标准事件宏(比特位标识)
所有事件宏均为短整型比特位,位与运算 & 是判断事件是否触发的标准方式。按照功能分为可读事件、可写事件、异常事件三大类,同时区分入参 / 出参权限。
3.2.1 可读类事件(可作为入参、出参 -- 输入输出类型参数)
| 宏定义 | 含义说明 |
|---|---|
| POLLIN | 普通数据、优先数据可读,最常用读事件 |
| POLLRDNORM | 普通数据可读,属于 POLLIN 的细分 |
| POLLRDBAND | 优先级带数据可读,Linux 系统不支持 |
| POLLPRI | 高优先级数据可读,典型场景:TCP 带外数据 |
3.2.2 可写类事件(可作为入参、出参 -- 输入输出类型参数)
| 宏定义 | 含义说明 |
|---|---|
| POLLOUT | 普通数据、优先数据可写,最常用写事件 |
| POLLWRNORM | 普通数据可写,属于 POLLOUT 的细分 |
| POLLWRBAND | 优先级带数据可写,Linux 极少使用 |
3.2.3 异常类事件(仅内核回填,不可作为入参)
这类事件由内核主动检测,应用程序无法主动监听,仅会出现在 revents 字段中:
- POLLERR:文件描述符发生错误。
- POLLHUP:挂起事件,典型场景:管道写端关闭、TCP 连接对方关闭读写端。
- POLLNVAL:无效文件描述符,fd 未正常打开。
3.2.4 扩展事件
POLLRDHUP:GNU 扩展事件,标识 TCP 对方关闭连接或关闭写端,常用于优雅断开连接检测。
3.2.5 常用组合规则
网络编程中三大核心事件 :POLLIN(读就绪)、POLLOUT(写就绪)、POLLERR(异常),基本覆盖绝大多数服务端场景。判断事件格式统一为:if (fds[i].revents & 事件宏)。
4 ~> 基于 select 改造 Poll 服务端(C++ 代码实战)
本节基于传统 select TCP 服务端代码改造为 poll 版本,完整实现监听连接、接收客户端、读写数据、断开清理全流程,代码适配 C++17 标准,附带完整注释与边界处理。
4.1 公共依赖说明
项目依赖基础组件:Socket.hpp(TCP 套接字封装)、Logger.hpp(日志工具)、Mutex.hpp(互斥锁)、InetAddr.hpp(网络地址封装),下文仅展示 PollServer 核心逻辑、主函数与编译脚本。
4.2 PollServer 核心类实现(PollServer.hpp)
cpp
#pragma once
#include <iostream>
#include <memory>
#include <poll.h>
#include "Logger.hpp"
#include "Socket.hpp"
#include "InetAddr.hpp"
// 全局常量定义
const int NUM = 1024; // pollfd 数组最大容量
const int gdefaultfd = -1; // 标记闲置fd的默认值
// Poll 多路转接 TCP 服务端类
class PollServer
{
public:
// 构造函数:初始化监听套接字、pollfd数组
PollServer(uint16_t port)
: _port(port),
_listensockfd(std::make_unique<TcpSocket>())
{
// 1. 创建、绑定、监听套接字
_listensockfd->BuildSocket(port);
// 2. 初始化整个pollfd数组,全部置为闲置状态
for (int i = 0; i < NUM; ++i)
{
_fdevent[i].fd = gdefaultfd;
_fdevent[i].events = 0;
_fdevent[i].revents = 0;
}
// 3. 将监听套接字加入poll监听,监听读事件(新连接)
_fdevent[0].fd = _listensockfd->Socketfd();
_fdevent[0].events |= POLLIN;
}
// 事件派发主循环
void Dispatcher()
{
while (true)
{
PrintFds(); // 打印当前监听的fd列表,用于调试
// 超时配置:0=非阻塞轮询,2000=2秒限时阻塞,-1=永久阻塞
int timeout = 0;
int ret = poll(_fdevent, NUM, timeout);
switch (ret)
{
case 0:
// 超时:无事件就绪
LOG(LogLevel::DEBUG) << "poll timeout...";
break;
case -1:
// 调用出错
LOG(LogLevel::ERROR) << "poll call error!";
break;
default:
// 存在就绪事件,执行事件处理
LOG(LogLevel::DEBUG) << "event ready, count: " << ret;
EventHandler();
break;
}
}
}
private:
// 打印当前所有被监听的文件描述符
void PrintFds()
{
std::cout << "poll Server fds list: ";
for (int i = 0; i < NUM; ++i)
{
if (_fdevent[i].fd == gdefaultfd)
continue;
std::cout << _fdevent[i].fd << " ";
}
std::cout << std::endl;
}
// 事件统一处理器:区分新连接、客户端读写
void EventHandler()
{
for (int i = 0; i < NUM; ++i)
{
// 跳过闲置fd
if (_fdevent[i].fd == gdefaultfd)
continue;
// 判断读事件就绪
if (_fdevent[i].revents & POLLIN)
{
// 场景1:监听套接字就绪 → 新连接到来
if (_fdevent[i].fd == _listensockfd->Socketfd())
{
Accepter();
}
// 场景2:普通客户端套接字就绪 → 读取客户端数据
else
{
IOHandler(i);
}
}
}
}
// 接收新客户端连接
void Accepter()
{
InetAddr clientaddr;
// 接受连接:poll已检测就绪,此处不会阻塞
int new_fd = _listensockfd->Accepter(&clientaddr);
if (new_fd < 0)
{
LOG(LogLevel::ERROR) << "accept new client failed!";
return;
}
LOG(LogLevel::INFO) << "get new client link, fd: " << new_fd;
// 寻找pollfd数组中的闲置位置,托管新fd
int pos = 0;
for (; pos < NUM; ++pos)
{
if (_fdevent[pos].fd == gdefaultfd)
break;
}
// 数组已满,服务无法承载新连接
if (pos >= NUM)
{
LOG(LogLevel::WARNING) << "server full, close client fd: " << new_fd;
close(new_fd);
return;
}
// 将新客户端fd加入poll监听,监听读事件
_fdevent[pos].fd = new_fd;
_fdevent[pos].events |= POLLIN;
_fdevent[pos].revents = 0;
}
// 客户端IO读写处理
void IOHandler(int idx)
{
int fd = _fdevent[idx].fd;
char buffer[1024] = {0};
// 读取数据:读事件已就绪,不会阻塞
ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);
if (n > 0)
{
// 读取到有效数据,回显数据
buffer[n] = '\0';
std::cout << "client message: " << buffer << std::endl;
std::string echo_msg = "echo: " + std::string(buffer);
send(fd, echo_msg.c_str(), echo_msg.size(), 0);
}
else if (n == 0)
{
// n=0:客户端正常关闭连接
LOG(LogLevel::INFO) << "client disconnect, fd: " << fd;
close(fd);
// 重置pollfd元素,标记为闲置
_fdevent[idx].fd = gdefaultfd;
_fdevent[idx].events = 0;
_fdevent[idx].revents = 0;
}
else
{
// n<0:读取异常
LOG(LogLevel::ERROR) << "recv error, fd: " << fd;
close(fd);
_fdevent[idx].fd = gdefaultfd;
_fdevent[idx].events = 0;
_fdevent[idx].revents = 0;
}
}
private:
uint16_t _port; // 服务端口
std::unique_ptr<TcpSocket> _listensockfd; // 监听套接字
struct pollfd _fdevent[NUM]; // pollfd 监听数组
};
4.3 主函数入口(<Main.cc>)
cpp
#include "PollServer.hpp"
#include <memory>
// 服务端口
const uint16_t g_port = 8080;
int main()
{
// 创建Poll服务端并启动事件循环
std::unique_ptr<PollServer> poll_svr = std::make_unique<PollServer>(g_port);
poll_svr->Dispatcher();
return 0;
}
4.4 编译脚本(Makefile)
bash
# 编译目标与编译指令
poll_server: Main.cc
g++ -o $@ $^ -std=c++17
# 清理规则
.PHONY: clean
clean:
rm -f poll_server
4.5 代码逻辑与运行说明
4.5.1 整体执行流程
- 服务初始化:创建监听套接字,初始化
pollfd数组,将监听 fd 加入监听列表。 - 事件循环:循环调用
poll,根据返回值区分超时、错误、事件就绪三种状态。 - 事件分发:遍历
pollfd数组,通过revents & POLLIN判断读就绪。- 监听 fd 就绪:调用
Accepter接收新客户端,将新 fd 加入监听数组。 - 客户端 fd 就绪:调用
IOHandler读取数据、回显数据。
- 监听 fd 就绪:调用
- 连接清理:客户端断开或读写异常时,关闭 fd 并重置对应
pollfd元素。
4.5.2 不同 timeout 运行效果
- timeout = 0:纯非阻塞轮询,进程持续循环打印超时日志,CPU 占用率较高。
- timeout = 2000:2 秒限时阻塞,无事件时每 2 秒打印一次超时日志。
- timeout = -1:永久阻塞,无事件时进程挂起,不消耗 CPU,生产环境常用配置。
4.5.3 边界场景处理
- 监听数组满载:拒绝新连接,关闭客户端 fd 并打印警告日志。
- 客户端正常断开:
recv返回 0,主动关闭 fd 并清空监听配置。 - 读写异常:
recv返回 -1,关闭 fd 并做异常日志记录。
4.6 优化扩展方向
当前代码使用固定大小数组,可改造为动态数组 (struct pollfd* _fdevent 动态内存申请),实现数组动态扩容,彻底摆脱固定容量限制,最大化发挥 poll 无硬上限的优势。
5 ~> poll 与 select 深度对比 & 技术优劣分析
5.1 poll 相对 select 的核心优势
- 无文件描述符硬上限
select依赖固定长度fd_set位图,fd 数量受系统宏限制;poll使用动态数组,最大监听数量仅受系统内存限制,扩展性更强。 - 入参出参完全解耦
select的fd_set会被内核改写,每次循环必须重新重置监听集合;poll将events(监听事件)与revents(就绪事件)拆分,events一次赋值终身有效,减少代码冗余与运算开销。 - 事件表达更丰富
poll提供细分的读写事件、异常事件、TCP 专属事件,事件粒度比select更精细,适配更多复杂网络场景。 - 超时参数更友好
select使用timeval结构体区分秒、微秒;poll统一使用毫秒整型,参数配置更简洁。
5.2 poll 遗留的共性问题(与 select 一致)
- 全量遍历开销 无论有多少 fd 就绪,应用程序都必须遍历整个
pollfd数组判断就绪状态,fd 数量越大,遍历耗时越高。 - 内核与用户态数据拷贝 每次调用
poll都需要将整个监听数组从用户态拷贝至内核态,fd 数量庞大时,拷贝开销显著。 - 内核轮询检测 内核内部依旧采用轮询方式检测所有 fd 状态,高并发场景下内核效率较低。
上述遗留问题,正是
epoll技术诞生的核心契机。
6 ~> 收尾总结:重难点、面试考点
6.1 核心概念考点
- 本质定位 :
poll是 IO 多路转接实现,核心能力是内核批量监听 fd 事件、就绪后通知应用程序,属于同步 IO 模型。 - 结构体核心 :
pollfd三字段分工必须牢记:fd标识文件描述符,events是用户指定监听事件,revents是内核回填就绪事件,二者解耦是poll最大设计亮点。 - 事件宏规则 :异常类事件(
POLLERR/POLLHUP/POLLNVAL)仅能由内核写入revents,应用程序不可在events中设置。 - timeout 三模式:-1 永久阻塞、0 非阻塞轮询、正数限时阻塞,不同模式对应不同业务场景。
6.2 代码实操考点
- 数组初始化规则 :闲置位置 fd 必须置为
-1,内核会自动跳过该元素。 - 事件判断语法 :固定使用按位与
revents & 事件宏判断事件就绪,不可使用等值判断。 - 连接生命周期管理 :新 fd 加入监听、客户端断开后重置
pollfd元素,是内存与文件描述符泄漏的关键防护点。 - 阻塞特性 :
poll检测到事件就绪后,对应的recv/send/accept不会阻塞,这是多路转接模型的基础前提。
6.3 面试 & 原理高频考点
- select 与 poll 区别:重点回答 fd 上限、参数耦合、事件粒度、超时参数四点差异。
- poll 为什么依然不适合超高并发:全量遍历、数据拷贝、内核轮询三大性能瓶颈。
- poll 能否动态扩容:原生固定数组无法扩容,改用动态堆数组可实现扩容,理论上无 fd 上限。
- POLLHUP 与 POLLERR 区别 :
POLLHUP是连接正常挂起(对方关闭连接),POLLERR是 fd 发生底层错误。
6.4 学习延伸方向
poll 是 select 到 epoll 的过渡技术,掌握 poll 的所有缺陷后,可针对性学习 epoll 的水平触发 / 边缘触发、事件就绪队列、内存映射等优化设计,形成完整的 Linux 多路转接知识链路。
7 ~> 多路转接Poll相关知识图谱

结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!"
"技术之路难免有困惑,但同行的人会让前进更有方向。" |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
