アドレス空間の領域、たとえばアドレスAからアドレスBへのすべてのキャッシュエントリに対してのみキャッシュ(L1、L2、およびL3)をフラッシュすることに興味があります。Linuxでユーザーまたはカーネル空間からフラッシュするメカニズムはありますか? ?
Linuxカーネルで使用可能なフラッシュ方法のリストについては、次のページを確認してください: https://www.kernel.org/doc/Documentation/cachetlb.txt
LinuxでのキャッシュとTLBフラッシング。デビッドS.ミラー
レンジフラッシング機能のセットがあります
2) flush_cache_range(vma, start, end);
change_range_of_page_tables(mm, start, end);
flush_tlb_range(vma, start, end);
3)void flush_cache_range(struct vm_area_struct * vma、unsigned long start、unsigned long end)
Here we are flushing a specific range of (user) virtual
addresses from the cache. After running, there will be no
entries in the cache for 'vma->vm_mm' for virtual addresses in
the range 'start' to 'end-1'.
関数の実装を確認することもできます- http://lxr.free-electrons.com/ident?a=sh;i=flush_cache_range
たとえば、腕の中- http://lxr.free-electrons.com/source/Arch/arm/mm/flush.c?a=sh&v=3.13#L67
67 void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
68 {
69 if (cache_is_vivt()) {
70 vivt_flush_cache_range(vma, start, end);
71 return;
72 }
73
74 if (cache_is_vipt_aliasing()) {
75 asm( "mcr p15, 0, %0, c7, c14, 0\n"
76 " mcr p15, 0, %0, c7, c10, 4"
77 :
78 : "r" (0)
79 : "cc");
80 }
81
82 if (vma->vm_flags & VM_EXEC)
83 __flush_icache_all();
84 }
これはARM用です。
GCCは __builtin___clear_cache
これ しますかsyscall cacheflush
を実行する必要があります。ただし、 警告 の場合があります。
ここで重要なのは、Linuxがキャッシュをフラッシュするためのシステムコール(ARM固有)を提供することです。このシステムコールの使用方法については、Android/Bionic flushcache を確認できます。ただし、Linuxを呼び出すときにどのような保証が提供されるのか、またはLinuxが内部動作を通じてどのように実装されるのかはわかりません。
このブログ投稿 キャッシュと自己変更コード はさらに役立つかもしれません。
Linuxのx86バージョンでは、キャッシュ範囲をフラッシュする目的で使用される関数void clflush_cache_range(void *vaddr, unsigned int size)
もあります。この関数は、 CLFLUSH
または CLFLUSHOPT
命令に依存しています。理論的にはオプションであるため、プロセッサが実際にそれらをサポートしていることを確認することをお勧めします。
CLFLUSHOPT
の順序は弱くなります。 CLFLUSH
は元々MFENCE
によってのみ順序付けられるように指定されていましたが、それを実装するすべてのCPUは強力な順序付けでそうします。書き込みおよびその他のCLFLUSH
命令。 Intelは、CLFLUSHOPT
の動作を変更する代わりに、新しい命令(CLFLUSH
)を追加し、将来のCPUが強力な順序でCLFLUSH
を実装することを保証するためにマニュアルを更新することを決定しました。この用途では、いずれかを使用した後にMFENCE
して、ベンチマークからのロード(ストアだけでなく)の前にフラッシュが実行されるようにする必要があります。
実際、x86は、役立つ可能性のあるもう1つの命令を提供します: CLWB
。 CLWB
は、データを(必要に応じて)削除せずにキャッシュからメモリにフラッシュし、クリーンなままでキャッシュされたままにします。 SKXのclwb
はclflushopt
のように立ち退きますが
これらの命令はキャッシュコヒーレントであることにも注意してください。それらの実行は、システム内のすべてのプロセッサ(プロセッサコア)のすべてのキャッシュに影響します。
これら3つの命令はすべて、ユーザーモードで使用できます。したがって、アセンブラ(または__mm_clflushopt
_などの組み込み関数)を使用して、ユーザースペースアプリケーションで独自のvoid clflush_cache_range(void *vaddr, unsigned int size)
を作成できます(ただし、実際に使用する前に、それらの可用性を確認することを忘れないでください)。
私が正しく理解していれば、この点に関してARMについて推論することははるかに困難です。 ARMプロセッサのファミリは、IA-32プロセッサのファミリよりもはるかに一貫性がありません。 1つはフル機能のキャッシュを備えたARMで、もう1つは完全にキャッシュを備えていないものです。さらに、多くのメーカーはカスタマイズされたMMUおよびMPUを使用できます。したがって、特定のARMプロセッサモデルについて推論することをお勧めします。
残念ながら、一部のデータをフラッシュするために必要な時間の合理的な見積もりを実行することはほとんど不可能であるように思われます。この時間は、フラッシュされるキャッシュラインの数、命令の順序付けられていない実行、TLBの状態(命令は仮想アドレスを引数として取りますが、キャッシュは物理アドレスを使用するため)、システム内のCPUの数など、非常に多くの要因の影響を受けます。システム内の他のプロセッサでのメモリ操作に関する実際の負荷、および範囲内の行数が実際にプロセッサによってキャッシュされ、最後にCPU、メモリ、メモリコントローラ、およびメモリバスのパフォーマンスによってキャッシュされます。その結果、実行時間は環境や負荷によって大きく異なると思います。唯一の合理的な方法は、システム上で、ターゲットシステムと同様の負荷でフラッシュ時間を測定することです。
最後に、メモリキャッシュとTLBを混同しないでください。これらは両方ともキャッシュですが、さまざまな方法で編成され、さまざまな目的を果たします。 TLBは、仮想アドレスと物理アドレスの間で最近使用された変換をキャッシュしますが、そのアドレスが指すデータはキャッシュしません。
また、メモリキャッシュとは対照的に、TLBはコヒーレントではありません。 TLBエントリのフラッシュは、メモリキャッシュからの適切なデータのフラッシュにはつながらないため、注意してください。
何人かの人々がclear_cache
について不安を表明しています。以下は、非効率的ですが、任意のユーザースペースタスク(任意のOS)から可能であるキャッシュを削除するための手動プロセスです。
mis-pld
命令を使用して、キャッシュを削除することができます。 pld
はキャッシュラインをフェッチします。特定のメモリアドレスを削除するには、キャッシュの構造を知る必要があります。たとえば、cortex-a9には、1行あたり8ワードの4ウェイデータキャッシュがあります。キャッシュサイズは、16KB、32KB、または64KBに構成できます。つまり、512、1024、または2048行です。方法は常に下位アドレスビットにとって重要ではありません(したがって、シーケンシャルアドレスは競合しません)。したがって、memory offset + cache size / ways
にアクセスして新しい方法を入力します。つまり、cortex-a9の場合は4KB、8KB、16KBごとです。
'C'または 'C++'でldr
を使用するのは簡単です。配列のサイズを適切に設定してアクセスするだけです。
たとえば、0x12345を削除する場合、行は0x12340で始まり、16KBのラウンドロビンキャッシュの場合はpld
on0x13340、0x14340、0x15340、および0x16340は、その方法で任意の値を削除します。同じプリンシパルをエビクトL2(多くの場合統合されている)に適用できます。すべてのキャッシュサイズを繰り返すと、キャッシュ全体が削除されます。キャッシュ全体を削除するには、キャッシュのサイズの未使用のメモリを割り当てる必要があります。これはL2ではかなり大きいかもしれません。 pld
を使用する必要はありませんが、フルメモリアクセス(ldr/ldm
)を使用します。複数のCPU(スレッド化されたキャッシュエビクション)の場合、各CPUでエビクションを実行する必要があります。通常、L2はすべてのCPUに対してグローバルであるため、実行する必要があるのは1回だけです。
NB:このメソッドは[〜#〜] lru [〜#〜](最近使用されていない)またはラウンドロビンキャッシュ。疑似ランダム置換の場合、エビクションを確実にするために、より多くのデータを読み書きする必要があります。正確な量はCPU固有です。 ARMランダム置換は、CPUに応じて8〜33ビットのLFSRに基づいています。一部のCPUでは、デフォルトでround-robinなどはデフォルトでpseudo-randomモードになります。一部のCPUでは、Linuxカーネル構成がモードを選択します。 参照: CPU_CACHE_ROUND_ROBIN ただし、新しいCPUの場合、Linuxはブートローダーやシリコンのデフォルトを使用します。言い換えると、完全に汎用的である必要がある場合、またはキャッシュを確実にクリアするために多くの時間を費やす必要がある場合は、clear_cache
OS呼び出しを機能させるように試みる価値があります(他の回答を参照)。
一部のMMU CPUおよび特定のOSではARM)を使用してOSをだますことにより、キャッシュを回避することができます。* nixシステムでは、複数のプロセスが必要です。 。プロセスを切り替える必要があり、OSはキャッシュをフラッシュする必要があります。通常、これは、OSがキャッシュをフラッシュする必要がある古いARM CPU(pld
をサポートしないCPU)でのみ機能します)プロセス間で情報が漏洩しないようにするため。移植性がなく、OSについて十分に理解している必要があります。
ほとんどの明示的なキャッシュフラッシュレジスタは、プロセス間のサービス拒否タイプの攻撃を防ぐためにシステムモードに制限されています。一部のエクスプロイトは、他のプロセスによって削除された行を確認することで情報を取得しようとする可能性があります(これにより、別のプロセスがアクセスしているアドレスに関する情報が得られます)。これらの攻撃は、疑似ランダム置換を使用するとより困難になります。
X86では、キャッシュ階層全体をフラッシュするためにこれを使用できます
native_wbinvd()
これはArch/x86/include/asm /special_insns.hで定義されています。その実装を見ると、WBINVD命令を呼び出すだけです。
static inline void native_wbinvd(void)
{
asm volatile("wbinvd": : :"memory");
}
[〜#〜] wbinvd [〜#〜] X86命令を実行するには、特権モードである必要があることに注意してください。これは、単一のキャッシュラインをクリアし、呼び出し元が特権モードである必要がない[〜#〜] clflush [〜#〜] x86命令とは対照的です。
X86 Linuxカーネルコードを見ると、この命令のほんの一握り(私がこれを書いているときは6か所)しか見えません。これは、そのシステムで実行されているすべてのエンティティの速度が低下するためです。 100MBLLCのサーバーでこれを実行することを想像してみてください。この命令は、100MB以上をキャッシュからRAMに移動することを意味します。さらに、この命令は中断できないことに気づきました。したがって、その使用法は、RTシステムの決定論に大きな影響を与える可能性があります。
(元の質問は特定のアドレス範囲をクリアする方法について尋ねていますが、キャッシュ階層全体をクリアすることに関する情報も一部の読者にとって役立つと思いました)