目前我正在 linux 上,写一个用户态的一个 c 程序。里面有一个逻辑是,去写了一段 RAM ,但写的动作实际上只发生在了 x86 CPU 的 cache 里面,还没有真正写到 RAM 里,然后这导致了错误发生(可以认为我需要结合 DMA 做一些操作,但 DMA 需要读到的东西还在 CPU 的 cache 里面呢)。
所以我想知道,linux 里有提供这种函数吗?(类似 arm ,都会提供这种函数的)
另外,我在网上搜,搜到的 全是 shell 里执行的命令:sync; echo 1 > /proc/sys/vm/drop_caches
。这个关键词到底该怎么搜索。
![]() | 1 Yourshell 2023-09-05 22:15:53 +08:00 你用 C 也可以写/proc/sys/vm/drop_caches 啊 |
![]() | 2 Yourshell 2023-09-05 22:16:43 +08:00 Linux 文件就是接口 |
![]() | 3 geelaw 2023-09-05 22:17:24 +08:00 via iPhone 关键词是 linux flush cpu cache |
![]() | 4 Yourshell 2023-09-05 22:17:53 +08:00 |
![]() | 5 codehz 2023-09-05 22:21:58 +08:00 我觉得这里有很多概念上的问题啊。。。 /proc/sys/vm/drop_caches 这个显然和 cpu cache 没有关系 你要 bypass cpu cache 的话,直接 volatile 不就好了 |
![]() | 6 amiwrong123 OP |
7 leonshaw 2023-09-05 22:28:36 +08:00 via Android XY 问题 |
8 ljn917 2023-09-05 22:31:34 +08:00 via Android |
![]() | 9 codehz 2023-09-05 22:31:51 +08:00 @amiwrong123 (小寄巧:用 *(volatile int __attribute__((force)) *)&x = 1 写入 内核也在用,不过把 __attribute__((force)) 加了个宏__force 简化) |
![]() | 10 geelaw 2023-09-05 22:32:44 +08:00 via iPhone 读了一下文档,看起来 drop_caches 只能清除文件系统里没有写过的缓存,sync 的作用是刷新所有文件,此外楼主的命令的影响范围是整个系统,而不是自己的进程,这样做几乎永远是错误的。 @codehz #5 可能楼主需要写很多内容后一起刷新? |
![]() | 11 codehz 2023-09-05 22:34:26 +08:00 不过 linux 用内核用是有正当理由的,你这是 x86 带有硬件 dma 或者别的硬件内存映射技术一类的吗,那样用户态也访问不到吧 |
![]() | 12 codehz 2023-09-05 22:35:54 +08:00 @geelaw 我感觉根本不是一个目的啊,drop_caches drop 的是 page cache 这些,和 cpu 缓存一点关系都没,下文提及的也看出不太像是访问文件的问题) |
![]() | 13 amiwrong123 OP @codehz 是的,用到了硬件的 DMA 。 用户态是可以访问的呀,只要最终映射出一个虚拟地址出来给 CPU 用就好了。我的程序逻辑很简单,以 RAM 作为交互,简单来说,CPU 先往 RAM (也就是内存条)里写,然后 DMA 从 RAM 里读。 |
![]() | 14 sujin190 2023-09-05 22:40:07 +08:00 via Android 没听说过还有手动清 cpu cache line 的,并发不一致也仅限于多核并发写,cpu 都自己管理好了和内存一致,时钟周期级别的一致,否则那么多程序都只是 lock 一下就能从内存读到正确值岂不是都要挂了,如果你硬要说有那应该就是 lock 之类的操作触发总线同步来标记 cache line 无效就是了吧 |
15 shimanooo 2023-09-05 22:43:00 +08:00 volatile? |
16 polaa 2023-09-05 22:43:32 +08:00 缓存一致性问题 关键词 Cache coherence 函数 : __clear_cache 指令 clflash wbinvd |
![]() | 17 codehz 2023-09-05 22:45:46 +08:00 @amiwrong123 参考这个 https://github.com/ikwzm/udmabuf mmap 的时候加一个 O_SYNC |
![]() | 18 codehz 2023-09-05 22:52:59 +08:00 虽然理论上这种情况还有编译器优化的问题,也就是如果 a b a 这样写,可能会跳过中间的 b 再用前面说的 volatile 转换方式也不迟( |
![]() | 19 codehz 2023-09-05 22:56:13 +08:00 哦,不对,编译器优化用 asm volatile("" ::: "memory");隔开就够了,O_SYNC 配合合适的用户态 dma 映射实现,应该就足够了 (不过用户态 dma 你还是得考虑调度的问题,没准你循环写入的时候进程被调度走了呢) |
20 polaa 2023-09-05 22:57:09 +08:00 然后你的这种做法的关键词是 self-modifying code ,但是我只接触过 arm 架构的 x86 架构不熟悉 可以参考一下 intel 的 Intel 64 and IA-32 Architectures Optimization Reference Manual @polaa |
![]() | 21 codehz 2023-09-05 23:05:50 +08:00 udmabuf 能 O_SYNC 的原因是加了这个参数后,就会在内核里用 dma_sync_single_for_cpu 来同步 cpu 缓存( 其他普通的实现可不一定有这个) |
![]() | 22 amiwrong123 OP @codehz #19 理解你说的意思,就是说,如果一个进程 malloc 了一片空间,这片空间只是属于这个进程的,但可能进程调度,把 RAM 的东西从内存条上调度走了。 不过我这个应该没有这个担心,因为不是用户态 malloc 的空间,是直接 搞了一块 连续的物理地址出来,再映射到虚拟地址。 |
![]() | 23 amiwrong123 OP @codehz #21 代码是这样的,是用 devmem 获得的 fd ,获得时是使用了 O_SYNC 的(不知道为啥,还是没起作用)。然后用这个 fd 进行 mmap ,获得了一个虚拟地址。 |
![]() | 24 nuk 2023-09-05 23:20:24 +08:00 Memory barrier 吧,硬件不同实现不同 |
![]() | 25 codehz 2023-09-05 23:21:23 +08:00 @amiwrong123 前面说的被调度走指的不是说内存映射也炸了 而是说 dma 硬件处理上可能会有不低概率观察到只进行到一半的修改( 后面补充的 o sync 就是为了应对你说的这个的 dev mem 的问题,正经解决方法就是用 udmabuf 那个模块去映射,效率也高一点 |
![]() | 26 nuk 2023-09-05 23:52:18 +08:00 原来是 DMA ?如果直接 mmap /dev/mem 的话,DMA 的内存区域本身就是 uncache 的,如果 dynamic DMA ,那不能直接用户态 mmap 创建 dma 内存,需要内核模块先分配 dma 内存。 |
![]() | 27 liuminghao233 2023-09-06 00:07:48 +08:00 via iPhone 内存屏障做的是 cache 可见性同步,不能保证刷入 ram 你需要 CLFLUSH ,可以把 l1 l2 l3 的 cache 都刷掉 用 _mm_clflush 应该就可以 |
![]() | 28 ryd994 2023-09-06 00:31:47 +08:00 via Android memory barrier 只是避免编译器优化,缓存一致性仍然由 CPU 自己维护,也就无法解决 dma 的问题。 dma 是设备修改内存,CPU 无法维护缓存一致性 volatile 就是最简单的办法。 正规做法是 dma_map_single(), dma_sync_single_for_device(), dma_unmap_single(), dma_sync_single_for_cpu() 这几个 API 。合理使用的话,它们会处理 dma 一致性问题 |
![]() | 29 ryd994 2023-09-06 00:34:24 +08:00 via Android 顺带一提,我用的关键字是 dma sync cpu cache 你不应该搜 Linux cache 。因为,cache 有很多种。CPU cache 也是 cache ,Page cache 也是 cache 。一般用户是不可能需要清 CPU cache 的,只有写驱动的人才需要。所以直接搜 cache ,大部分内容都是 Page cache |
![]() | 30 feather12315 2023-09-06 00:55:32 +08:00 via Android |
![]() | 31 jimages 2023-09-06 01:05:05 +08:00 WBINVD 指令,不过这个执行只能在特权模式下执行,你需要看一下操作系统有没有提供这个指令的函数,比如 windows 好像提供了 https://learn.microsoft.com/en-us/cpp/intrinsics/wbinvd?view=msvc-170 linux 不太清楚 |
![]() | 32 jimages 2023-09-06 01:08:46 +08:00 这个 repo 写了一个 linux 扩展,你也许可以使用 https://github.com/batmac/wbinvd/blob/master/wbinvd.c |