简单封装线程库 + 理解LWP和TID
文章目录
- 前言:
- 简单封装一下C++线程库
- 如何理解tid?
- 理解pthread库:
- 内核视角与用户视角:
前言:
在上一文的线程控制中,我们先是聊了关于为什么我们要在编译链接时将线程库给链接起来,简单回顾一下,其根本原因,就是因为我们理解的线程和Linux中的线程是两个不同的概念,在Linux操作系统中,只认“LWP”即轻量级进程,而为了能够很好的适配我们所理解的线程,线程库pthread就出现了。
然后紧接着我们学习了控制线程的一系列操作,包括线程的创建、线程的等待、线程的终止、线程的分离。
这部分的学习和当年我们学习进程的一系列操作一致。
那我们在这里想要简单回顾一下Linux下,进程和线程之间的关系:
特性 | 进程 | 线程 |
---|---|---|
定义 | 程序的执行实例 | 进程中的执行单元 |
内存 | 每个进程有独立的地址空间 | 线程共享进程的内存空间 |
资源 | 独立的资源(如文件描述符、内存) | 共享进程资源,独立的栈空间 |
调度 | 每个进程是内核调度的基本单位 | 线程是进程内部的调度实体 |
创建开销 | 较大(需要复制资源) | 较小(共享进程资源) |
并发/并行 | 进程可以并行执行,但开销较大 | 线程可以并发执行,开销小 |
简单封装一下C++线程库
为了能向后期学习C++11中的线程库看齐,我们在这里以前先自己模拟手搓一个线程库出来,那么说干就干,首先我们先出个简易的步骤:
- “先描述”,一个简单简易的线程类,应该包含一个线程的名字、tid、运行状态、要执行的回调函数。
- 构造函数直接打印一条消息即可,在main函数中创建线程使用emplace_back直接构造即可。
- 使线程执行操作,直接调用函数指针(这里会存在一个关于this指针的问题,一会聊)。
- 终止线程操作
- join等待回收线程。
-
构造函数及私有成员:
class My_Thread {// 构造函数Thread(std::string name, Func_t func): _name(name), _func(func){std::cout << _name << " have been created!" << std::endl;}// ... 其它操作// 私有成员属性private:std::string _name; // 名字 ("thread-1 ...")pthread_t _tid; // tidFunc_t _func; // 回调函数地址bool _isrunning; // 运行状态 };
-
执行线程
本质还是在这一步使用
pthread_create()
函数创建线程,然后通过其参数里的“函数指针”实现操作!但是!
因为咱们本质是在类里面调用的
pthread_create()
函数的,而若我们通过函数指针调用的函数也定义在该类步,那么就会出现报错!
原因就在于,Threadd_Routine()函数内部,还隐含了一个this指针!
解决办法也很简单,我们可以在该函数前面添加static关键字
简单回顾一下,当年学习C++时我们说过,添加static关键字修饰的函数,会存放在静态区,属于是其它类也可以用,而不是是独属某个类.
因此在这里我们可以修改修改:
void Execute() {_isrunning = true;_func(_name);_isrunning = false; }// 如果不加static修饰的话,该函数就是此类独有的,那这个函数的参数就会隐含一个this指针! // 加上static关键字进行修饰,就能保证该函数是存在于静态区的,不是某个类独有的! static void* Thread_Routine(void *args) {My_Thread *Self = (My_Thread *)args;Self->Execute(); // 在这里当然可以直接调用_func,但是代码健壮性太烂,不雅观return nullptr; }bool Start() {// 创建线程int n = ::pthread_create(&_tid, nullptr, Thread_Routine, this); // 因为Thread_Routine不独属于某个类,所以在这里传递“当前类”this.if (n != 0)return false;return true; }
-
其余部分(终止线程,等待回收线程):
void Stop() {while (!_isrunning){::pthread_cancel(_tid);_isrunning = false;std::cout << _name << " Stop" << std::endl;break;} }void Join() {if(!_isrunning){int n = ::pthread_join(_tid, nullptr);if (n == 0){std::cout << _name << " join suceess!" << std::endl;}} }~My_Thread() {}
main函数编写:
void Print(const std::string &name)
{int cnt = 1;while (true){std::cout << name << " is running... cnt: " << cnt << std::endl;cnt++;sleep(1);}
}int main()
{std::vector<My_Thread> threads;for (int i = 1; i <= 5; ++i){std::string name = "thread-" + std::to_string(i);threads.emplace_back(name, Print);sleep(1);}for (auto &t : threads){t.Start();}sleep(10);// 结束多线程for (auto &t : threads){t.Stop();}// 等待多线程for (auto &t : threads){t.Join();}return 0;
}
运行结果:
如何理解tid?
相信学到这里,你或多或少有对线程的LWP的编号和tid有疑问。那么别担心,这一部分我们会将tid以及LWP彻底讲解清楚!
-
LWP?PCB?
首先我们先来回顾一下,曾经我说过!———— “Linux操作系统,在内核角度来看,只认识LWP(轻量级进程)”
所以其实在Linux内核中,没有我们曾将讲解的PCB,也就是进程控制块,这个是“不存在”的!
取而代之的,其实是一个一个的LWP,也就是轻量级进程。 -
tid?LWP?
我们在谈“创建线程”时,我们介绍了函数
pthread_create()
,他有一个参数名字是pthread_t *thread
,而如果创建成功了,这个thread就会获得值,这个值就代表的是线程的ID。可是,咱们上次创建了线程,查看到LWP也有一个值,类比一下进程的pid,是一个进程的标识。
那你通过pthread_create()
创建后得到的thread,它也是线程的ID,那他是tid吗?如果是,那LWP的值又算什么呢?
理解pthread库:
为了能够较好的理解上述的tid和LWP的关系,我们首先需要先来认识理解pthread库。
还是一样,先回顾库是怎么加载的!
有一天你写好了一段关于创建线程的代码,这个代码会存放在磁盘上。
又有一天,你想跑这个代码了,于是你打开代码编译链接形参可执行程序的过程中,经历了这几个过程:
- 我将你的代码编译之后拿到物理内存中
- 在我进行查看链接时,我发现你需要链接libpthread.so这个库,于是我就从磁盘中把这个库也**拿到物理内存**中
- 接下来为了能让Linux内核也能看到,于是我把它通过页表,映射到虚拟地址空间中。具体的位置就位于堆栈之间的共享区
内核视角与用户视角:
基于上面的理解,我们能得出一个结论,那就是Linux操作系统内核是不维护我们认为的线程的!
本质上,Linux操作系统不认可线程,而是只在乎它自己的LWP,这也就是为什么“线程”也被称为“用户级线程”。
正因为要适配用户的需求,那我们就得来实现维护和封装。
那么我们又该怎么去维护,或者说管理我们的用户级线程的呢? —— 先描述,再组织!
-
所以我们使用
pthread_create()
函数创建的用户级线程的相关属性,就都会存放在了这个动态库里 -
至此,我们可以总结 ———— 记录线程的相关属性和操作都会被记录在这“线程控制块”内,其会被存放在动态库里!
-
而tid是什么? —— tid就是该线程块的起始地址!
-
这个可能有点难理解,就想像我们在学习C++的unordered_map时,我们只是使用并不关心其内部的接口是怎么实现,我们也学过那些哈希表上的节点不也是malloc出来的吗,而这些节点其实就是在它的库里进行维护的!
-
其实新线程执行完后返回,在内部来看,其实LWP是被释放了的,但是库里面的用户级线程还没有被释放,它还记录着返回值信息,如果碰到主线程在join,那就会把返回值信息拷贝给当初的
void* ret
,然后自己再释放,这就是为什么当时我会说可能出现“僵尸线程”这种情况 -
用户级线程通过pthread_create创建,而pthread_create内部又会调用系统调用接口clone来创建系统级线程LWP
-
“线程控制块”里有一个线程栈,这就正好对应上我之前讲解过的,每个线程都有一套独属于自己的栈空间。
而这个线程栈,当然不是库里面的空间,它可能也是个指针,然后记录了多大的范围,告诉线程和Linux说,在虚拟地址空间中,你线程的栈空间就是这么大。
其实就一句话:
用户级线程其实就是一个在库函数里的结构体!
相关文章:
简单封装线程库 + 理解LWP和TID
文章目录 前言:简单封装一下C线程库如何理解tid?理解pthread库:内核视角与用户视角: 前言: 在上一文的线程控制中,我们先是聊了关于为什么我们要在编译链接时将线程库给链接起来,简单回顾一下&…...
VBA批量插入图片到PPT,一页一图
Sub InsertPicturesIntoSlides()Dim pptApp As ObjectDim pptPres As ObjectDim pptSlide As ObjectDim strFolderPath As StringDim strFileName As StringDim i As Integer 设置图片文件夹路径strFolderPath "C:\您的图片文件夹路径\" 请替换为您的图片文件夹路径…...
cjson——excel转json文件(python脚本转换)
excel转json文件 前言应用场景1. 安装必要的库2. 定义 Excel 表格格式3. Python 脚本:将 Excel 转换为 JSON4. 脚本解释5. 生成的 JSON 文件6. 如何使用 JSON 文件7. 扩展功能:处理多个工作表8. 总结 前言 将 Excel 表格的配置参数转换成 JSON 文件是一…...
Keepalived + LVS 搭建高可用负载均衡及支持 Websocket 长连接
一、项目概述 本教程旨在助力您搭建一个基于 Keepalived 和 LVS(Linux Virtual Server)的高可用负载均衡环境,同时使其完美适配 Websocket 长连接场景,确保您的 Web 应用能够高效、稳定地运行,从容应对高并发访问&…...
01-spring-理-beanFactory
需要掌握 拿到容器中的 实例这个可以debug IOC容器SpringBootApplication(exclude {DataSourceAutoConfiguration.class}) public class RuoYiApplication {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {// System.setProp…...
【pytorch】卷积神经网络
1 图像卷积 1.1 互相关运算 在二维互相关运算中,卷积窗口从输入张量的左上角开始,从左到右、从上到下滑动。当卷积窗口滑动到新一个位置时,包含在该窗口中的部分张量与卷积核张量进行按元素相乘,得到的张量再求和得到一个单一的标…...
强大的接口测试可视化工具:Postman Flows
Postman Flows是一种接口测试可视化工具,可以使用流的形式在Postman工作台将请求接口、数据处理和创建实际流程整合到一起。如下图所示 Postman Flows是以API为中心的可视化应用程序开发界面。它提供了一个无限的画布用于编排和串连API,数据可视化来显示…...
RISCV学习(3)HPM5301 MCU芯片学习
RISCV学习(3)HPM5301 MCU芯片学习 1、HPM5301 背景介绍 笔者在RT-Thread开发者大会上领了一个HPM5301EVKLite的盲盒板子,就抽空点个灯介绍一下。主要板子如下图所述,类似于一个最小系统板。 开发厂商:先楫半导体,HPMICRO芯片架构:32位的RISC-V,RV32,支持IMAFDCPB指…...
拆解 | 公募REITs:发售上市流程及细节
Hi,围炉喝茶聊产品的新老朋友好,在国庆假期写了两篇有关公募REITs的文章,先简单回顾下,以达到温故知新的效果。 第一篇:一起探索:公募REITs,它从本质、背景、概念等维度较系统介绍了公募REITs,如:明明是“不动产基金”,为什么叫REITs?说到底,投资REITs的实质是什么…...
嵌入式系统 第七讲 ARM-Linux内核
• 7.1 ARM-Linux内核简介 • 内核:是一个操作系统的核心。是基于硬件的第一层软件扩充, 提供操作系统的最基本的功能,是操作系统工作的基础,它负责管理系统的进程、内存、设备驱动程序、文件和网络系统, 决定着系统的…...
记一次 dockerfile 的循环依赖错误
文章目录 1. 写在最前面1.1 具体循环依赖的例子 2. 报错的位置2.1 代码快速分析2.2 代码总结2.3 关于 parser 的记录 3. 碎碎念 1. 写在最前面 笔者在使用 dockerfile 多阶段构建的功能时,写出了一个「circular dependency detected on stage: xx」的错误。 解决方…...
用css实现瀑布流布局
上效果 知识理解 column-count: 4; column-gap: 15px;实现固定四行瀑布流布局 columns: 200px auto;column-gap: 15px;由浏览器根据容器的宽度自动调整,尽可能一行多个200px宽度的列数 <!DOCTYPE html> <html lang"en"><head><me…...
Spring Bean required a single bean, but 2 were found,发现多个 Bean
问题复现 在使用 Autowired 时,不管你是菜鸟级还是专家级的 Spring 使用者,都应该制造或者遭遇过类似的错误: required a single bean, but 2 were found 顾名思义,我们仅需要一个 Bean,但实际却提供了 2 个ÿ…...
用 Python 从零开始创建神经网络(十八):模型对象(Model Object)
模型对象(Model Object) 引言到目前为止的完整代码: 引言 我们构建了一个可以执行前向传播、反向传播以及精度测量等辅助任务的模型。通过编写相当多的代码并在一些较大的代码块中进行修改,我们实现了这些功能。此时,…...
Springboot 升级带来的Swagger异常
当升级到Springboot 2.6.0 以上的版本后,Swagger 就不能正常工作了, 启动时报如下错误。当然如果你再使用sping boot Actuator 和 Springfox, 也会引起相关的NPE error. (github issue: https://github.com/springfox/springfox/issues/3462) NFO | jvm 1 | 2022/04…...
【蓝桥杯研究生组】第15届Java试题答案整理
D 题 试题 D: 商品库存管理 时间限制: 3.0s 内存限制: 512.0MB 本题总分:10 分 【问题描述】 在库存管理系统中,跟踪和调节商品库存量是关键任务之一。小蓝经营的仓库中存有多种商品,这些商品根据类别和规格被有序地分类并编号,…...
数据结构(链式栈)
链式栈 链式栈(Linked Stack)是一种基于链表的数据结构,用于实现栈(后进先出,LIFO)的特性。与基于数组的栈不同,链式栈通过动态分配内存来存储数据,这使得它更加灵活,能…...
《代码随想录》Day22打卡!
回溯算法 《代码随想录》回溯算法:组合 本题完整题目如下: 本题的完整思路如下: 1.本题使用回溯算法,其实回溯和递归是一样的道理,也是分为三步曲进行: 2.第一步:确定递归函数的返回值和参数&…...
NetSuite Formula(HTML)超链打开Transaction
当Saved Search作为Sublist应用在Form时,如果Document Number是Group过的,则会出现如下超链失效的情况。 解决办法: 可以利用Saved Search中的Formula(HTML)功能来构建超链,用于打开Transaction。 以下图…...
传统听写与大模型听写比对
在快节奏的现代生活中,听写技能仍然是学习语言和提升认知能力的重要环节。然而,传统的听写练习往往枯燥乏味,且效率不高。现在,随着人工智能技术的发展,大模型听写工具的问世,为传统听写带来了革命性的变革…...
本地快速推断的语言模型比较:Apple MLX、Llama.cpp与Hugging Face Candle Rust
本地快速推断的语言模型比较:Apple MLX、Llama.cpp与Hugging Face Candle Rust 在自然语言处理(NLP)部署中,推断速度是一个关键因素,尤其是对于支持大型语言模型(LLM)的应用来说。随着Apple M1…...
Tomcat调优相关理解
什么是QPS? 是Queries Per Second 的缩写,是指服务器每秒查询数,比如定义一个a接口,该接口是10QPS,那么就是指该接口每秒可以处理10个请求 springboot默认并发处理数是多少? springboot并发处理要看serv…...
python爬虫--小白篇【selenium自动爬取文件】
一、问题描述 在学习或工作中需要爬取文件资源时,由于文件数量太多,手动单个下载文件效率低,操作麻烦,采用selenium框架自动爬取文件数据是不二选择。如需要爬取下面网站中包含的全部pdf文件,并将其转为Markdown格式。…...
Flink读写Kafka(DataStream API)
在Flink里,已经预定义了kafka connector,使用该connector我们可以读写kafka,并且能实现exactly once的语义。 要使用需要引入相关的maven依赖,在这里,因为读写kafka,就会涉及一个问题,kafka-client和broker的版本兼容问题,不过因为kafka client和broker的双向兼容的良…...
活动预告 | Microsoft 安全在线技术公开课:通过扩展检测和响应抵御威胁
课程介绍 通过 Microsoft Learn 免费参加 Microsoft 安全在线技术公开课,掌握创造新机遇所需的技能,加快对 Microsoft Cloud 技术的了解。参加我们举办的“通过扩展检测和响应抵御威胁”技术公开课活动,了解如何更好地在 Microsoft 365 Defen…...
nginx核心配置文件及常用功能
华子目录 配置文件说明配置文件格式说明nginx配置文件中的变量默认nginx.conf配置文件格式说明main全局配置events配置段 nginx配置中的root和aliaslocation用法详解虚拟主机配置nginx账户认证功能nginx自定义错误页面nginx自定义日志 配置文件说明 nginx官方帮助文档…...
基于AT89C51单片机的可暂停八路抢答器设计
点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/90196607?spm1001.2014.3001.5503 C15 部分参考设计如下: 摘要 随着社会进步和科技发展,电子设备在各类活动中的应用日益普遍,…...
github加速源配置
访问github速度很慢? 试试一下方法 1: 编辑配置 vim /etc/docker/daemon.json 2:都复制粘贴上 { "registry-mirrors": [ "https://docker.211678.top", "https://docker.1panel.live…...
骑行解压:身心的奇妙之旅,VELO Angel Revo坐垫
在快节奏的都市生活中,骑行不仅是一种健康的生活方式,更是一种心灵的释放。从心理生理学的角度来看,骑行能够促使身体分泌内啡肽,带来愉悦感,同时,它还能转移注意力,缓解焦虑。在这场身心的奇妙…...
(七)- plane/crtc/encoder/connector objects
1,framebuffer/plane Rockchip RK3399 - DRM framebuffer、plane基础知识 - 大奥特曼打小怪兽 - 博客园 2,crtc Rockchip RK3399 - DRM crtc基础知识 - 大奥特曼打小怪兽 - 博客园 3,encoder/connector/bridge Rockchip RK3399 - DRM en…...
从零开始:如何在 .NET Core 中优雅地读取和管理配置文件
在.net中的配置文件系统支持丰富的配置源,包括文件(json、xml、ini等)、注册表、环境变量、命令行、Azure Key Vault等,还可以配置自定义配置源并跟踪配置的改变,然后按照优先级进行覆盖,总之对文件的配置有很多方法,这…...
Python中PDF转Word的技术
Python PDF转Word技术概述 在日常办公和数据处理中,经常需要将PDF文档转换为Word文档,以便进行编辑、修改或格式调整。Python作为一种强大的编程语言,提供了多种库和工具来实现这一功能。以下是对Python中PDF转Word技术的详细介绍。 一、技…...
挑战春招找到java后端实习第一天(1.1)
八股文 1.java中有哪些集合类请简单介绍一下 集合类分为两大类Collection和Map。前者是对象的集合,后者是键值对。 Collection分为List,Set,Queue三个接口。 List有LinkedList,ArrayList,Vector Set(不…...
leetcode hot 小偷
class Solution(object):def rob(self, nums):""":type nums: List[int]:rtype: int"""# 使用动态规划,把之前的给保存起来ans[0,nums[-1]]for i in range(1,len(nums)):ans.append(max(ans[-1],ans[-2]nums[-1*i-1]))return ans[-1]…...
一、Git与GitHub基础说明
Git与GitHub Git与GitHub一、Git1定义2核心功能(1) 版本控制(2) 分支管理(3) 合并操作 二、GitHub1定义2核心功能(1)远程仓库托管(2)Pull Requests(拉取请求)(3) Issue Tracking(问题跟踪)(4) 团队管理(5) 社交功能(6)个人资料和贡…...
Unity-Mirror网络框架-从入门到精通之Room示例
文章目录 前言Room示例场景设置NetworkRoomManagerSpawnerRewardRoomPlayerGamePlayer 最后 前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架,专为多人游戏开发设计。它使得开发者能够轻松实现…...
httpslocalhostindex 配置的nginx,一刷新就报404了
当你的Nginx配置导致页面刷新时报404错误时,通常是由于以下几个原因造成的: 静态文件路径配置错误:Nginx没有正确地指向静态文件的目录。前端路由问题:如果是SPA(单页应用),刷新页面时Nginx没有…...
Java重要面试名词整理(十九):Seata
文章目录 分布式事务概述实现思路:两阶段提交协议(2PC) SeataSeata的三大角色Seata的生命周期Seata解决方案 AT模式一阶段二阶段 XA模式TCC模式如何处理空回滚如何处理幂等如何处理悬挂 SAGA模式四种模式对比 分布式事务概述 在微服务架构中,完成某一个…...
OpenCV和PyQt的应用
1.创建一个 PyQt 应用程序,该应用程序能够: 使用 OpenCV 加载一张图像。在 PyQt 的窗口中显示这张图像。提供四个按钮(QPushButton): 一个用于将图像转换为灰度图一个用于将图像恢复为原始彩色图一个用于将图像进行翻…...
【Linux】进程间通信(一)
目录 一、进程间通信1.1 进程间通信目的1.2 理解进程间通信1.3 进程间通信发展1.4 进程间通信分类 二、管道2.1 什么是管道2.2 管道的原理2.3 匿名管道2.3.1 pipe函数2.3.2 匿名管道的实现2.3.3 匿名管道小结2.3.3.1 匿名管道的四种情况2.3.3.2 匿名管道的五种特性 2.3.4 匿名管…...
Fama MacBeth两步法与多因子模型的回归检验
Fama MacBeth两步法与多因子模型的回归检验 – 潘登同学的因子投资笔记 本文观点来自最近学习的石川老师《因子投资:方法与实践》一书 文章目录 Fama MacBeth两步法与多因子模型的回归检验 -- 潘登同学的因子投资笔记 多因子回归检验时序回归检验截面回归检验Fama–…...
Postman[4] 环境设置
作用:不同的环境可以定义不同的参数,在运行请求时可以根据自己的需求选择需要的环境 1.创建Environment 步骤: Environment-> ->命名->添加环境变量 2.使用Environment 步骤:Collection- >右上角选择需要的环境...
【paddle】初次尝试
张量 张量是 paddlepaddle, torch, tensorflow 等 python 主流机器学习包中唯一通货变量,因此应当了解其基本的功能。 张量 paddle.Tensor 与 numpy.array 的转化 import paddle as paddle import matplotlib.pyplot as plt apaddle.to_t…...
开源架构中的数据库选择优化版
上一篇文章推荐: 开源架构学习指南:文档与资源的智慧锦囊(New) 我管理的社区推荐:【青云交社区】和【架构师社区】 推荐技术圈福利社群:点击快速加入 开源架构中的数据库选择优化版 一、引言二、关系型开源…...
Echarts+vue电商平台数据可视化——webSocket改造项目
websocket的基本使用,用于测试前端能否正常获取到后台数据 后台代码编写: const path require("path"); const fileUtils require("../utils/file_utils"); const WebSocket require("ws"); // 创建WebSocket服务端的…...
【网络安全实验室】SQL注入实战详情
如果额头终将刻上皱纹,你只能做到,不让皱纹刻在你的心上 1.最简单的SQL注入 查看源代码,登录名为admin 最简单的SQL注入,登录名写入一个常规的注入语句: 密码随便填,验证码填正确的,点击登录…...
【信息系统项目管理师】第14章:项目沟通管理过程详解
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 一、规划沟通管理1、输入2、工具与技术3、输出二、管理沟通1、输入2、工具与技术3、输出三、监督沟通1、输入2、工具与技术3、输出一、规划沟通管理 定义:规划沟通管理是基于每个干系人或干系人群体的信息需求…...
YOLOv5部署到web端(flask+js简单易懂)
文章目录 前言最终实现效果图后端实现 主界面检测函数检测结果显示 前端实现 主界面(index.html)显示图片界面 总结 前言 最近,老板让写一个程序把yolov5检测模型部署到web端,在网页直接进行目标检测。经过1个星期的努力,终于实…...
什么是自治系统和非自治系统
自治系统 自治系统的特征是其状态方程不依赖于时间。举个简单的例子,考虑一阶常微分方程: d x d t − x \frac{dx}{dt} -x dtdx−x 这是一个经典的指数衰减过程,其中状态 (x) 随时间 (t) 衰减。这个系统是自治的,因为它的演…...
使用 CSS 的 `::selection` 伪元素来改变 HTML 文本选中时的背景颜色
定义 ::selection 伪元素: 在你的 CSS 文件中,添加 ::selection 伪元素,并设置 background-color 属性来改变选中文本的背景颜色。 示例代码: ::selection {background-color: yellow; /* 你可以根据需要更改颜色 */color: black…...