[^0]: Additionally, the name “Utopia” translates to 乌托邦
in Chinese.
[^0]: Additionally, the name “Utopia” translates to 乌托邦
in Chinese.
TLB的介绍,请看
大体上是应用访问越随机, 数据量越大,pgw开销越大。
ISCA 2013 shows the pgw overhead in big memory servers.
or ISCA 2020 Guvenilir 和 Patt - 2020 - Tailored Page Sizes.pdf
1 | # shaojiemike @ snode6 in ~/github/hugoMinos on git:main x [11:17:05] |
default there is no hugopage(usually 4MB) to use.
1 | $ cat /proc/meminfo | grep huge -i |
explained is here.
cat /sys/kernel/mm/transparent_hugepage/enabled
but achieve this needs some details.echo 20 > /proc/sys/vm/nr_hugepages
. And you need to write speacial C++ code to use the hugo page1 | # using mmap system call to request huge page |
But there is a blog using unmaintained tool hugeadm
and iodlr
library to do this.
1 | sudo apt install libhugetlbfs-bin |
So meminfo
is changed
1 | $ cat /proc/meminfo | grep huge -i |
using iodlr
library
1 | git clone |
Measurement tools from code
1 | # shaojiemike @ snode6 in ~/github/PIA_huawei on git:main x [17:40:50] |
平均单次开销(开始到稳定):
dtlb miss read need 2450 cycle ,itlb miss read need 4027 cycle
案例的时间分布:
65000 100000
超内存前,即使是全部在计算,都是0.24% the gemm
‘s core line is
1 | for(int i=0; i<N; i++){ |
and real time breakdown is as followed. to do
manual code to test if tlb entries is run out
1 | $ ./tlbstat -c '../../test/manual/bigJump.exe 1 10 100' |
In this case, tlb miss rate up to 47/53 = 88.6%
using big hash table
Any algorithm that does random accesses into a large memory region will likely suffer from TLB misses. Examples are plenty: binary search in a big array, large hash tables, histogram-like algorithms, etc.
暂无
暂无
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无
Intel SDM(Software Developer's Manual)
This set consists of
volume | Descriptions | pages(size) |
---|---|---|
volume 1 | Basic Architecture | 500 pages(3MB) |
volume 2 (combined 2A, 2B, 2C, and 2D) | full instruction set reference | 2522 pages(10.8MB) |
volume 3 (combined 3A, 3B, 3C, and 3D) | system programming guide | 1534 pages(8.5MB) |
volume 4 | MODEL-SPECIFIC REGISTERS (MSRS) | 520 pages |
volume3: Memory management(paging), protection, task management, interrupt and exception handling, multi-processor support, thermal and power management features, debugging, performance monitoring, system management mode, virtual machine extensions (VMX) instructions, Intel® Virtualization Technology (Intel® VT), and Intel® Software Guard Extensions (Intel® SGX).
more graph and easier to read.
暂无
暂无
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
GLIBC(GNU C Library)是Linux系统中的标准C库,它提供了许多与程序执行和系统交互相关的功能。GLIBC是应用程序与操作系统之间的接口,提供了许多系统调用的包装函数和其他基础功能,使应用程序能够访问操作系统提供的服务和资源。
GLIBC的主要功能包括:
上下文切换与GLIBC之间没有直接关系。上下文切换是操作系统的概念,是在进程或线程之间切换执行权的过程。GLIBC作为C库,封装了一些系统调用和基础功能,但并不直接参与上下文切换的过程。
然而,GLIBC的性能和效率可以影响上下文切换的开销。GLIBC的实现方式、性能优化以及与操作系统内核的协作方式,可能会对上下文切换的效率产生影响。例如,GLIBC的线程库(如pthread)的设计和性能特性,以及对锁、条件变量等同步原语的实现方式,都可能会影响多线程上下文切换的开销。
因此,尽管GLIBC本身不直接执行上下文切换,但它的设计和实现对于多线程编程和系统性能仍然具有重要的影响。
在PPA。改系统的glibc十分的危险,ssh连接,ls命令等,都需要用到。会导致ssh连接中断等问题。
不推荐,可能会遇到库依赖。比如缺少bison
和gawk
。详细依赖见
1 | mkdir $HOME/glibc/ && cd $HOME/glibc |
您可以使用以下方法来查找libstdc++库的位置:
g++
或gcc
命令查找:如果您的系统上安装了g++或gcc编译器,您可以使用以下命令来查找libstdc++库的位置:1 | g++ -print-file-name=libstdc++.so |
或者
1 | gcc -print-file-name=libstdc++.so |
ldconfig
命令查找:ldconfig
是Linux系统中用于配置动态链接器的命令。您可以运行以下命令来查找libstdc++库的路径:1 | ldconfig -p | grep libstdc++.so |
/usr/lib
或/usr/lib64
。您可以在这些目录中查找libstdc++的库文件。如果您找到了libstdc++库的路径,您可以将其添加到CMakeLists.txt中的CMAKE_CXX_FLAGS
变量中,如之前的回答中所示。
请注意,如果您正在使用的是Clang编译器(clang++
),则默认情况下它将使用libc++作为C++标准库,而不是libstdc++。如果您确实希望使用libstdc++,需要显式指定使用-stdlib=libstdc++
标志。例如:
1 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++") |
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无
According to 2007 paper
进程或线程的上下文切换可以在多种情况下发生,下面列举了一些常见的情况:
需要注意的是,上下文切换是操作系统内核的责任,它根据调度策略和内核的算法来管理进程和线程的切换。上下文切换的具体发生时机和行为取决于操作系统的设计和实现。
保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。
包括以下几个方面:
相对于进程上下文切换,线程上下文切换通常更快,这是因为线程共享相同的地址空间和其他资源,因此上下文切换只需要切换线程的执行状态和部分寄存器,省去了一些额外的开销。
以下是线程上下文切换相对于进程上下文切换的一些优势和省去的时间开销:
尽管线程上下文切换相对较快,但仍然需要一些时间和开销,包括以下方面:
需要注意的是,线程上下文切换的快速性是相对于进程上下文切换而言的,具体的开销和时间取决于系统的设计、硬件的性能和操作系统的实现。不同的操作系统和硬件平台可能会有不同的上下文切换开销。
如果要能清晰的回答这一点,需要对OS的页表管理和上下午切换的流程十分了解。
Page Table Isolation(页面表隔离)是一种为了解决Meltdown等CPU安全漏洞而提出的硬件优化机制。
其主要思想是将操作系统内核和用户空间的页面表隔离开来,实现内核地址空间与用户地址空间的隔离。
具体来说,Page Table Isolation 主要包括以下措施:
这种机制可以阻止用户程序直接读取内核内存,防止Meltdown类攻击获得内核敏感信息。
当前主流的x86处理器通过在TLB中添加PTI(Page Table Isolation)位实现了此机制,来隔离内核地址空间。这成为了重要的安全优化之一。
由于PTI的存在,内核维护了两套 页表。
用户态切换的额外开销包括:
进程上下文标识符(PCID) 是一项 CPU 功能,它允许我们在切换页表时通过在 CR3 中设置一个特殊位来跳过刷新整个 TLB。这使得切换页表(在上下文切换或内核进入/退出时)更便宜。但是,在支持 PCID 的系统上,上下文切换代码必须将用户和内核条目都从 TLB 中清除。用户 PCID TLB 刷新将推迟到退出到用户空间,从而最大限度地降低成本。有关PCID / INVPCID详细信息,请参阅 intel.com/sdm。
在没有 PCID 支持的系统上,每个 CR3 写入都会刷新整个 TLB。这意味着每个系统调用、中断或异常都会刷新 TLB
对于同一个进程的不同线程,当它们运行在不同的物理核心上时,其Intel PCID (进程上下文ID)是相同的。
主要原因如下:
PCID是用于区分不同进程地址空间的标识符。同一进程的线程共享相同的地址空间。
所以操作系统会为同一进程的所有线程分配相同的PCID,无论它们运行在哪个物理核心上。
当线程在物理核心之间迁移时,不需要改变PCID,因为地址空间没有改变。
线程迁移后,新的核心会重新使用原有的PCID加载地址翻译表,而不是分配新的PCID。
这确保了同进程不同线程使用统一的地址映射,TLB内容可以直接重用,无需刷新。
相反,不同进程之间必须使用不同的PCID,才能隔离地址映射,避免TLB冲突。
所以操作系统只会在进程切换时改变PCID,而线程切换保持PCID不变。
综上,对于同一进程的不同线程,无论运行在哪个物理核心,其PCID都是相同的。这使线程可以重用TLB项,是多线程架构的重要优化手段。同进程线程使用统一PCID,不同进程必须使用独立PCID。
PCID(Process Context Identifier)和 ASID(Address Space Identifier)都是用于优化页表切换的技术
Quantifying the cost of context switch
sched setaffinity()
and sched setscheduler()
SCHED FIFO
and give them the maximum priority.1 | # shaojiemike @ hades0 in ~/github/contextSwitch2007 on git:master x [15:59:39] C:10 |
us microseconds
, 论文里是3.8 us
,我们的机器是0.85 us
。Tsuna的2010年的博客
code in https://github.com/tsuna/contextswitch 机器配置在实验结果后。
gettid()
futex
来切换。包含futex system calls.开销sched_yield
让出CPU使用权,强制发生进程切换.futex
.只不过线程通过 pthread_create
创建来执行函数, 而不是fork
shed_yield()
.并且设置SCHED_FIFO
和 sched_priority
sched_yield()
函数的作用是使当前进程放弃CPU使用权,将其重新排入就绪队列尾部。但是如果就绪队列中只有这一个进程,那么该进程会立即再次获得CPU时间片而继续执行。必须有其他等待进程且满足调度条件才会真正发生切换。1 | # snode6 |
如上,snode6应该是550ns
machine | system calls | process context switches | thread context switches | thread context switches 2 |
---|---|---|---|---|
snode6 | 428.6 | 2520.3 | 2606.3(1738.9) | 277.8 |
snode6 | 427.7 | 2419.8 | 2249.0(2167.9) | 401.1 |
snode6 | 436.0 | 2327.1 | 2358.8(1727.8) | 329.3 |
hades | 65.8 | 1806.4 | 1806.4 | 64.6 |
hades | 65.5 | 1416.4 | 1311.6 | 282.7 |
hades | 80.8 | 2153.1 | 1903.4 | 64.3 |
icarus | 74.1 | 1562.3 | 1622.3 | 51.0* |
icarus | 74.1 | 1464.6 | 1274.1 | 232.6* |
icarus | 73.9 | 1671.8 | 1302.1 | 38.0* |
vlab | 703.4 | 5126.3 | 4897.7 | 826.1* |
vlab | x | x | x | x |
vlab | 697.1 | 10651.4 | 4476.0 | 843.9* |
docker |||| | ||||
docker |||| | ||||
docker |||| |
说明:
No CPU affinity
和 With CPU affinity
和 With CPU affinity to CPU 0
()
内为。额外添加设置SCHED_FIFO
和 sched_priority
的结果。*
意味着没有sudo权限。报错sched_setscheduler(): Operation not permitted
x
报错taskset: 设置 pid 30727 的亲和力失败: 无效的参数
问题:
shed_yield()
.并且设置SCHED_FIFO
和 sched_priority
运行strace -ff -tt -v taskset -a 1 ./timetctxsw2
. 应该是不需要strace的,为什么需要记录syscall的信息呢?
1 | # snode6 |
猜想:
1 | shaojiemike @ snode6 in ~/github/contextswitch on git:master o [19:46:27] |
machine | OS | linux kernel | compile | glibc |
---|---|---|---|---|
snode6 | Ubuntu 20.04.6 LTS | 5.4.0-148-generic | gcc 9.4.0 | GLIBC 2.31 |
hades | Ubuntu 22.04.2 LTS | 5.15.0-76-generic | gcc 11.3.0 | GLIBC 2.35-0ubuntu3.1 |
icarus | Ubuntu 22.04.2 LTS | 5.15.0-75-generic | gcc 11.3.0 | GLIBC 2.35-0ubuntu3.1 |
vlab | Ubuntu 22.04.2 LTS | 5.15.81-1-pve | gcc 11.3.0 | GLIBC 2.35-0ubuntu3.1 |
glic 版本使用ldd --version
获得。OS影响调度算法,内核影响切换机制,编译器影响代码优化,GLIBC影响系统调用开销。
sched_setscheduler()
是一个用于设置进程调度策略的函数。它允许您更改进程的调度策略以及与之相关的参数。具体来说,sched_setscheduler()
函数用于将当前进程(通过 getpid()
获取进程ID)的调度策略设置为实时调度策略(SCHED_FIFO)。实时调度策略是一种优先级调度策略,它将进程分配给一个固定的时间片,并且仅当进程主动释放 CPU 或者其他高优先级的实时进程出现时,才会进行上下文切换。/sys/bus/node/devices/node0/cpumap
存储了与特定 NUMA 节点(NUMA node)关联的 CPU 核心映射信息。cpumap
文件中的内容表示与 node0
相关的 CPU 核心的映射。每个位置上的值表示相应 CPU 核心的状态,常见的取值有:node0
。node0
。1 | # shaojiemike @ snode6 in ~/github/contextswitch on git:master x [22:37:41] C:1 |
根据1996的论文,需要考虑几个方面的内容:
http://lmbench.sourceforge.net/cgi-bin/man?keyword=lmbench§ion=8
#include <unistd.h> /pipe()
的父子进程的write
和read
system calls实验环境:
根据Light-weight Contexts的数据:
注意,括号内为十次执行的标准差
解释与组成
在测量上下文切换开销时,进程和线程的切换开销可能会相对接近,这可能是由于以下几个原因:
TLB(Translation Lookaside Buffer)的刷新:TLB是用于高速缓存虚拟地址到物理地址映射的硬件结构。当发生进程或线程切换时,TLB中的缓存项可能需要刷新,以确保新的地址映射有效。虽然线程切换只涉及部分的TLB刷新,但刷新的开销相对较小,因此在总的上下文切换开销中可能没有明显拉开差距。
寄存器和上下文切换:无论是进程切换还是线程切换,都需要保存和恢复一部分寄存器的状态和执行上下文。这部分的开销在进程和线程切换中是相似的。
内核操作和调度开销:无论是进程还是线程切换,都需要涉及内核的操作和调度。这包括切换内核栈、更新调度信息、切换上下文等操作,这些开销在进程和线程切换中也是相似的。
需要注意的是,实际上下文切换的开销是受到多个因素的影响,如处理器架构、操作系统的实现、硬件性能等。具体的开销和差距会因系统的不同而有所差异。在某些情况下,线程切换的开销可能会稍微小一些,但在其他情况下,可能会存在较大的差距。因此,一般情况下,不能简单地将进程和线程的上下文切换开销归为相同或明显不同,具体的测量和评估需要结合实际系统和应用场景进行。
暂无
PIM 最优调度遇到的问题
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
Quantifying The Cost of Context Switch 2007,ExpCS
lmbench: Portable tools for performance analysis,1996 USENIX ATC
Light-weight Contexts: An OS Abstraction for Safety and Performance
参考 kernel 文档
线程在核间切换的开销是极小的(Java uses lots of threads but threads have become significantly faster and cheaper with the NPTL in Linux 2.6.),与其考虑切换开销,不如注意不同优先级线程同一个核竞争的问题。
在C++中,有几种方式可以实现线程的创建。下面是一些常见的方法:
std::thread
类这是C++11标准引入的线程库,使用起来非常方便。你可以直接创建一个 std::thread
对象并传递一个函数或可调用对象(如lambda表达式)。
示例代码:
1 |
|
std::async
和 std::future
std::async
可以用于异步执行任务,返回一个 std::future
对象,你可以通过这个对象获取任务的执行结果。
示例代码:
1 |
|
在使用较早的C++版本或在一些特定的操作系统(如Linux)下,pthread
是创建和管理线程的常用方式。需要包含 <pthread.h>
头文件。
示例代码:
1 |
|
Boost库提供了丰富的线程管理功能,使用起来与标准库类似,但需要安装Boost库。
示例代码:
1 |
|
可以直接调用操作系统提供的API来创建线程,例如在Windows上使用 CreateThread
,在Unix/Linux上使用 pthread_create
。
Windows上的示例代码:
1 |
|
std::thread
是最常用和推荐的方式,简单易用且跨平台。std::async
更适合用于需要返回值的异步操作。pthread
适合在类Unix系统上使用,适应性强但需要更多的低层管理。线程独占的信息很少,一般就是线程名的获取和设置。
1 |
|
taskset -c 0-4 {command}
,可以实现命令绑定在编号0-3
核上。c++
内通过pthread_setaffinity_np
函数实现。* `htop -p pid` 里 `a` 选项可以显示已有的亲和性设置
* `ps -p pid` 可以看见CMD相同。
计算机文件系统是操作系统的一个重要组成部分,它管理计算机存储设备上的文件,负责文件存储、读取和组织等功能。文件系统的主要作用包括:
常见的文件系统包括Windows上的FAT、NTFS,Unix/Linux上的ext、XFS、Btrfs等。
文件系统的设计对操作系统的性能、安全性有很大影响。一个优秀的文件系统应提供高效的IO访问、良好的安全控制和数据完整性保障。选择正确的文件系统对不同场景也很重要。
文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。
但是就像内存读取也不会只读一个字节, 硬盘的存储和读取都是按照Block进行的(比如,4KB即连续八个 sector组成一个 block。)
早期文件分配表(File Allocate Table,FAT)
链表结构解决了文件和物理块映射的问题。
基于 inode(index node的数据结构) 的传统文件系统。文件数据被存储不同块里面,文件的元数据信息就会被存储在inode里面。
由于inode号码与文件名分离
find ./* -inum 节点号 -delete
为了解决数据变化问题,它引入了3个存储指针设计。
1 | $ df -hi . |
58605568/256 = 222928 个 inode节点
1 | $ fdisk -l |
1875378176/228928 = 8192(8K) 个 sector 对应一个inode节点
一个inode节点对应 4MB的空间?
ln -s /etc/nginx/config link_config
ln main.c link_main.c
在大部分常见场景下,硬链接是更优的选择:
硬链接也有一些限制:
前两者是磁盘分区格式,后两者是文件系统格式。
MBR、GPT是两种比较常见的磁盘分区格式,而且对于磁盘分区而言,目前也主要是这两种格式。一个分区是一个存储设备上的一块独立区域,用户可以针对这块区域进行单独管理。
New Technology File System (NTFS):Windows NT引入的文件系统,使用主文件表(MFT)来管理文件,支持高级功能如权限控制等。
NTFS的同层目录采用B+树结构,按文件(夹)名保持有序,通过文件号指向文件夹内的文件。文件夹的目录项较少时可以直接存储在文件夹的FILE记录中,目录项较多时占用数据簇,建立INDX记录,存放各目录项的属性。
NTFS文件系统一共由16个“元文件”构成
Nas 防止碎片,开启预分配
估计是有一层转换,类似的软件有 UFS Explorer
对于磁盘的一次读请求,
常见的I/O调度算法包括
Linux系统中请求到达磁盘的一次完整过程,期间Linux一般会通过Cache以及排序合并I/O请求来提高系统的性能。
其本质就是由于磁盘随机读写慢、顺序读写快的磁盘I/O特性。
在进行系统设计时,良好的读性能和写性能往往不可兼得。在许多常见的开源系统中都是优先在保证写性能的前提下来优化读性能。那么如何设计能让一个系统拥有良好的写性能呢?
目前的大多数文件系统,如XFS/Ext4、GFS、HDFS,在元数据管理、缓存管理等实现策略上都侧重大文件。
像 FAT 和 FAT32 这类文件系统中,文件紧挨着写入到磁盘中。文件之间没有空间来用于增长或者更新:
NTFS 中在文件之间保留了一些空间,因此有空间进行增长。但因块之间的空间是有限的,碎片也会随着时间出现。
Linux 的日志型文件系统采用了一个不同的方案。与文件相互挨着不同,每个文件分布在磁盘的各处,每个文件之间留下了大量的剩余空间。这就给文件更新和增长留下了很大的空间,碎片很少会发生。
此外,碎片一旦出现了,大多数 Linux 文件系统会尝试将文件和块重新连续起来。
在已经挂载的分区中运行 fsck 将会严重危害到你的数据和磁盘。
1 | umount /path |
大于20%需要整理。批评fsck不准的文章:大文件碎片少才是最重要的
NEC 的 Akira Fujita 和 Takashi Sato 在十年前就为 ext4 写了在线整理碎片的工具,因此我们直接拿来用就好了。
1 | e4defrag -v /path |
方法一:整个磁盘文件备份,格式化,重新搬运。(Liunx 会自动将文件进行连续分布排列。)
方法二:
1 | # 不要中断,很危险 |
坏道分为逻辑坏道,和物理坏道。如果是物理坏道,由于异物或者碰撞导致磁头,盘面受损,会导致物理损坏扩散,应该今早备份数据。
场景问题:
1.同时向机械硬盘拷贝多个文件夹,每个文件夹里都有多个小文件。
2.同时解压多个压缩包,每个压缩包有大量的小文件。
3.同时安装多个游戏安装包。
会不会导致交叉存储?会不会导致碎片增加?会不会导致游玩游戏时读取速度变慢?
一般不用担心,有些文件系统会做碎片整理。然后操作系统的缓存写和预读策略也会优化。
对于机械硬盘来讲,反复读写、多线程读写等情况对磁盘使用寿命的影响很低,
但“高温”、“低温”、“尘土”、“外力冲击(跌落)”等情况,会对磁盘的寿命造成较大的影响。
暂无
https://www.ruanyifeng.com/blog/2011/12/inode.html
https://tech.meituan.com/2017/05/19/about-desk-io.html
在进程/线程并发执行的过程中,进程/线程之间存在协作的关系,例如有互斥、同步的关系。
为了实现进程/线程间正确的协作,操作系统必须提供实现进程协作的措施和方法,主要的方法:
这两个都可以方便地实现进程/线程互斥,而信号量比锁的功能更强一些,它还可以方便地实现进程/线程同步。
Test-and-Set 和 Compare-and-Swap (CAS) 都是并发编程中的重要原子操作指令,通常用于同步和处理多线程或多进程环境中的竞争条件。它们的作用是确保某些操作在执行时不会被中断,以保持数据一致性和避免并发冲突。我们来详细了解这两个命令。
它的操作过程如下:
操作是原子的,即不会被中断或干扰,保证在并发环境下,多个线程或进程不能同时修改同一位置。
用途:
它的操作步骤如下:
CAS 也具有原子性,这意味着它在操作期间不会被中断。CAS 是实现无锁编程和避免线程同步问题的常用工具,尤其在需要高性能的并发程序中。
用途:
Test-and-Set
指令,XCHG
被用作实现类似功能的原子操作。CMPXCHG
指令,ARM 等其他架构也有类似的原子操作指令(如 LDREX 和 STREX)。std::atomic
是对 Test-and-Set
和 Compare-and-Swap
等底层原子操作的高级封装,使得 C++ 开发者可以在多线程环境中更方便、更安全地使用这些原子操作。
原子操作(std::atomic
)有如下特点:
std::atomic
只能用于一些简单的类型(如整数、指针和布尔类型),对于复杂的数据结构,仍然需要其他同步机制(如锁)。std::memory_order
对于一个线程里的同一个代码块内的无依赖关系的指令,处理器在运行时会重排序乱序执行。这对原子指令也是一样的。但实际生产中,多个原子操作之间,可能会有依赖关系,比如一个线程读取另一个线程写入的数据。为了确保这些依赖关系的正确性,需要使用内存顺序来控制处理器对指令的重排序。
std::atomic
的操作支持指定内存顺序,这控制了编译器和硬件对操作的优化和重排序。常见的内存顺序选项有:
std::memory_order_relaxed
:没有限制,运行时会乱序。std::memory_order_acquire
和 std::memory_order_release
:分别用于“获取”和“释放”同步,确保操作顺序。std::memory_order_seq_cst
:默认的内存顺序,保证在多线程程序中,所有原子操作按顺序执行,不允许重排序。但性能可能会略受影响。store
在原子操作中,store
用来将一个值存储到原子变量中。与普通赋值不同,store
会确保在多线程环境下,赋值操作是原子性的(即不会被打断)。
1 | std::atomic<int> counter(0); |
store
会保证在指定的内存顺序下将值存储到原子变量中,因此它是线程安全的。load
load
用于从原子变量中读取值。它会确保在多线程环境中,读取操作是线程安全的,并且可以指定内存顺序。
1 | int value = counter.load(std::memory_order_acquire); // 从原子变量读取 |
load
会确保读取的是正确同步后的值,避免在多线程场景下出现读取错误。compare_exchange_strong
compare_exchange_strong
(以及 compare_exchange_weak
)广泛应用于实现锁和无锁算法。它会尝试将原子变量的值与一个预期值进行比较,如果相同,则将其更新为新值。如果比较失败,原子变量的值不会更改,并且返回 false
。1 | std::atomic<int> value(0); |
exchange
exchange
是一种常见的原子操作,类似于 store
,但它会返回原子变量的先前值。
1 | int oldValue = counter.exchange(5); // 返回更新前的值,并将 counter 设置为 5 |
exchange
在多线程环境下是安全的,并且可以返回修改前的值,适用于某些需要获取旧值的场景。多线程同步的一种忙等待锁,线程反复检查锁变量是否可用。
1 | std::atomic_flag lock = ATOMIC_FLAG_INIT; |
把自己阻塞起来(内核态和用户态之间的切换进入阻塞状态,可能上下文切换),等待重新调度请求。
适用于大段的修改和同步。
eventfd
事件通知eventfd
是 Linux 提供的一种用于线程安全的事件通知机制,类似于文件描述符,可以通过读写操作来实现跨线程或跨进程的同步。通过 eventfd
,线程或进程可以通过某些事件(例如计数器增减、通知等)来触发其他线程或进程的动作。
eventfd
可以通过两种方式工作:
eventfd_write
增加计数器,其他线程或进程可以通过 eventfd_read
来读取这些计数器的值。eventfd
进行事件通知,线程或进程通过 eventfd_read
来等待并响应这些事件。eventfd_read
的功能 : eventfd_read
是一个用于从 eventfd
文件描述符读取事件的系统调用。它会从 eventfd
中读取一个 64 位无符号整数,并返回这个值。这个值通常表示某个事件的状态或计数器的值。
https://www.cswiki.top/pages/f398f1/#blocking-i-o
原文链接:https://blog.csdn.net/qq_15437629/article/details/79116590