CPUID读取有关Cache的信息
- 1: void cpuidTest()
- 2: {
- 3: u32 val_eax, val_ebx, val_ecx, val_edx;
- 4: asm("cpuid"
- 5: : "=a" (val_eax),
- 6: "=b" (val_ebx),
- 7: "=d" (val_ecx),
- 8: "=c" (val_edx)
- 9: : "a" (2));
- 10:
- 11: printk("eax: 0x%08X\n", val_eax);
- 12: printk("ebx: 0x%08X\n", val_ebx);
- 13: printk("ecx: 0x%08X\n", val_ecx);
- 14: printk("edx: 0x%08X\n", val_edx);
- 15: }
读出结果如下:
- 1: [190894.986103] ###################################################################
- 2: [190894.986109] eax: 0x76035A01
- 3: [190894.986110] ebx: 0x00F0B0FF
- 4: [190894.986111] ecx: 0x00CA0000
- 5: [190894.986112] edx: 0x00000000
- 6: [190894.986951] ###################################################################
解析出有效的descriptor
- 1: 76H: TLB Instruction TLB: 2M/4M pages, fully associative, 8 entries
- 2: 03H: TLB Data TLB: 4 KByte pages, 4-way set associative, 64 entries
- 3: 5AH:TLB Data TLB0: 2-MByte or 4 MByte pages, 4-way set associative, 32 entries
- 4: F0H:Prefetch 64-Byte prefetching
- 5: B0H:TLB Instruction TLB: 4 KByte pages, 4-way set associative, 128 entries
- 6: FFH: General CPUID leaf 2 does not report cache descriptor information, use CPUID leaf 4 to query cache parameters
- 7: CAH: STLB Shared 2nd-Level TLB: 4 KByte pages, 4-way associative, 512 entries
可以看到,
General CPUID leaf 2 does not report cache descriptor information, use CPUID leaf 4 to query cache parameters
没有返回Cache相关的信息,都是TLB信息。如果需要了解Cache的信息,需要使用4作为EAX的输入。
我们重新组装代码,读取Cache相关的信息:
- 1: void cpuidTest()
- 2: {
- 3: u32 val_eax, val_ebx, val_ecx, val_edx;
- 4: asm("cpuid"
- 5: : "=a" (val_eax),
- 6: "=b" (val_ebx),
- 7: "=d" (val_ecx),
- 8: "=c" (val_edx)
- 9: : "a" (4), "c"(1));
- 10:
- 11: u32 ways,partitions,line_Size, sets;
- 12:
- 13: ways = val_ebx >> 22;
- 14: partitions = (val_ebx >> 12) & 0x3FF;
- 15: line_Size = (val_ebx) & 0xFFF;
- 16: sets = val_ecx;
- 17:
- 18: printk("eax: 0x%08X\n", val_eax);
- 19: printk("ebx: 0x%08X\n", val_ebx);
- 20: printk("ecx: 0x%08X\n", val_ecx);
- 21: printk("edx: 0x%08X\n", val_edx);
- 22:
- 23: printk("ways: %d\n", ways+1);
- 24: printk("partitions: %d\n", partitions+1);
- 25: printk("line_size: %d\n", line_Size+1);
- 26: printk("sets: %d\n", sets+1);
- 27: printk("Cache L1 size: %d\n", (ways + 1)*(partitions + 1)*(line_Size + 1)*(sets + 1));
- 28: }
结果如下:
- 1: [193334.815202] ###################################################################
- 2: [193334.815206] eax: 0x00000021
- 3: [193334.815207] ebx: 0x01C0003F
- 4: [193334.815208] ecx: 0x00000000
- 5: [193334.815209] edx: 0x0000003F
- 6: [193334.815209] ways: 8
- 7: [193334.815210] partitions: 1
- 8: [193334.815211] line_size: 64
- 9: [193334.815211] sets: 1
- 10: [193334.815212] Cache L1 size: 512
- 11: [193334.815672] ###################################################################
可见,L1的Cache是“全相关”,即只有一个cache set,其中有8路,即8个缓存行,每个缓存行里面包含的数据是64bytes,总共512bytes的缓存。
Linux是怎么读取的呢?
- 1: daniel@ubuntu:/mod/pslist$ cat /proc/cpuinfo
- 2: processor : 0
- 3: vendor_id : GenuineIntel
- 4: cpu family : 6
- 5: model : 42
- 6: model name : Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz
- 7: stepping : 7
- 8: cpu MHz : 3269.310
- 9: cache size : 6144 KB
- 10: fdiv_bug : no
- 11: hlt_bug : no
- 12: f00f_bug : no
- 13: coma_bug : no
- 14: fpu : yes
- 15: fpu_exception : yes
- 16: cpuid level : 5
- 17: wp : yes
- 18: flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc up pni monitor ssse3 lahf_lm
- 19: bogomips : 6538.62
- 20: clflush size : 64
- 21: cache_alignment : 64
- 22: address sizes : 36 bits physical, 48 bits virtual
- 23: power management:
- 1: static int show_cpuinfo(struct seq_file *m, void *v)
- 2: {
- 3: struct cpuinfo_x86 *c = v;
- 4: unsigned int cpu;
- 5: int i;
- 6:
- 7: ******
- 8: /* Cache size */
- 9: if (c->x86_cache_size >= 0)
- 10: seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size);
- 11: ******
- 12: }
- 1: unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
- 2: {
- 3: /* Cache sizes */
- 4: unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0;
- 5: unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
- 6: unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
- 7: unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
- 8: #ifdef CONFIG_X86_HT
- 9: unsigned int cpu = c->cpu_index;
- 10: #endif
- 11:
- 12: if (c->cpuid_level > 3) {
- 13: static int is_initialized;
- 14:
- 15: if (is_initialized == 0) {
- 16: /* Init num_cache_leaves from boot CPU */
- 17: num_cache_leaves = find_num_cache_leaves();
- 18: is_initialized++;
- 19: }
- 20:
- 21: /*
- 22: * Whenever possible use cpuid(4), deterministic cache
- 23: * parameters cpuid leaf to find the cache details
- 24: */
- 25: for (i = 0; i < num_cache_leaves; i++) {
- 26: struct _cpuid4_info_regs this_leaf;
- 27: int retval;
- 28:
- 29: retval = cpuid4_cache_lookup_regs(i, &this_leaf);
- 30: if (retval >= 0) {
- 31: switch (this_leaf.eax.split.level) {
- 32: case 1:
- 33: if (this_leaf.eax.split.type ==
- 34: CACHE_TYPE_DATA)
- 35: new_l1d = this_leaf.size/1024;
- 36: else if (this_leaf.eax.split.type ==
- 37: CACHE_TYPE_INST)
- 38: new_l1i = this_leaf.size/1024;
- 39: break;
- 40: case 2:
- 41: new_l2 = this_leaf.size/1024;
- 42: num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
- 43: index_msb = get_count_order(num_threads_sharing);
- 44: l2_id = c->apicid >> index_msb;
- 45: break;
- 46: case 3:
- 47: new_l3 = this_leaf.size/1024;
- 48: num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
- 49: index_msb = get_count_order(
- 50: num_threads_sharing);
- 51: l3_id = c->apicid >> index_msb;
- 52: break;
- 53: default:
- 54: break;
- 55: }
- 56: }
- 57: }
- 58: }
- 59: /*
- 60: * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
- 61: * trace cache
- 62: */
- 63: if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
- 64: /* supports eax=2 call */
- 65: int j, n;
- 66: unsigned int regs[4];
- 67: unsigned char *dp = (unsigned char *)regs;
- 68: int only_trace = 0;
- 69:
- 70: if (num_cache_leaves != 0 && c->x86 == 15)
- 71: only_trace = 1;
- 72:
- 73: /* Number of times to iterate */
- 74: n = cpuid_eax(2) & 0xFF;
- 75:
- 76: for (i = 0 ; i < n ; i++) {
- 77: cpuid(2, ®s[0], ®s[1], ®s[2], ®s[3]);
- 78:
- 79: /* If bit 31 is set, this is an unknown format */
- 80: for (j = 0 ; j < 3 ; j++)
- 81: if (regs[j] & (1 << 31))
- 82: regs[j] = 0;
- 83:
- 84: /* Byte 0 is level count, not a descriptor */
- 85: for (j = 1 ; j < 16 ; j++) {
- 86: unsigned char des = dp[j];
- 87: unsigned char k = 0;
- 88:
- 89: /* look up this descriptor in the table */
- 90: while (cache_table[k].descriptor != 0) {
- 91: if (cache_table[k].descriptor == des) {
- 92: if (only_trace && cache_table[k].cache_type != LVL_TRACE)
- 93: break;
- 94: switch (cache_table[k].cache_type) {
- 95: case LVL_1_INST:
- 96: l1i += cache_table[k].size;
- 97: break;
- 98: case LVL_1_DATA:
- 99: l1d += cache_table[k].size;
- 100: break;
- 101: case LVL_2:
- 102: l2 += cache_table[k].size;
- 103: break;
- 104: case LVL_3:
- 105: l3 += cache_table[k].size;
- 106: break;
- 107: case LVL_TRACE:
- 108: trace += cache_table[k].size;
- 109: break;
- 110: }
- 111:
- 112: break;
- 113: }
- 114:
- 115: k++;
- 116: }
- 117: }
- 118: }
- 119: }
- 120:
- 121: if (new_l1d)
- 122: l1d = new_l1d;
- 123:
- 124: if (new_l1i)
- 125: l1i = new_l1i;
- 126:
- 127: if (new_l2) {
- 128: l2 = new_l2;
- 129: #ifdef CONFIG_X86_HT
- 130: per_cpu(cpu_llc_id, cpu) = l2_id;
- 131: #endif
- 132: }
- 133:
- 134: if (new_l3) {
- 135: l3 = new_l3;
- 136: #ifdef CONFIG_X86_HT
- 137: per_cpu(cpu_llc_id, cpu) = l3_id;
- 138: #endif
- 139: }
- 140:
- 141: c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d));
- 142:
- 143: return l2;
- 144: }
c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d));
- 1: static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
- 2: unsigned int *ecx, unsigned int *edx)
- 3: {
- 4: /* ecx is often an input as well as an output. */
- 5: asm volatile("cpuid"
- 6: : "=a" (*eax),
- 7: "=b" (*ebx),
- 8: "=c" (*ecx),
- 9: "=d" (*edx)
- 10: : "0" (*eax), "2" (*ecx));
- 11: }
为什么x86_cache_size是6144KB,而我们得到的L1缓存为512Bytes,L2也是512Bytes,L3是1536Bytes呢?
上面的程序有个错误,改过来结果
asm("cpuid"
: "=a" (val_eax),
"=b" (val_ebx),
"=d" (val_ecx),
"=c" (val_edx) //c和d写反了,不配对
: "a" (4), "c"(1));
- 1: [261214.698170] ###################################################################
- 2: [261214.698174] eax: 0x00000041
- 3: [261214.698175] ebx: 0x05C0003F
- 4: [261214.698176] ecx: 0x00000FFF
- 5: [261214.698177] edx: 0x00000000
- 6: [261214.698178] ways: 24
- 7: [261214.698178] partitions: 1
- 8: [261214.698179] line_size: 64
- 9: [261214.698180] sets: 4096
- 10: [261214.698181] Cache L3 size: 6144 KB
Intel与缓存有关的总线技术:
Strong Uncacheable:
所有的读和写操作,都按照编码时设定的严格顺序出现在系统总线(System Bus)上,不会发生乱序。
所有可能的硬件优化都被禁止,比如对可能的内存访问进行预测(speculative memory accesses),pagetable walks, prefectches of speculated branch targets.等等。
当系统的IO被映射到物理内存空间时,这种模式是有用的,可以保证对IO设备的操作严格,不会引起歧义。
但是如果用来操作RAM内存,会极大降低性能。
Uncacheable:
和Strong Uncacheable是类似,区别在于:
Uncacheable可以通过对MTRR寄存器进行编程来将该区域类型修改为WC类型,但是Strong Uncacheable区域不可以被修改。
MTRR的意思是“内存类型及范围寄存器(Memory Type & Range Register)”。
Write Combining(WC):
读写不被缓存在缓存中,但是写被缓存在WC Buffer中。而且不强制要求一致性(coherency)。
对内存的写可能被delay并且combine在WC buffer中,以减少对内存的访问次数。
直到一些特定的事件发生时,才被写回到内存中。
主要用于一些对写内存的顺序不感冒的场合,比如video frame buffer等等。
Write Through(WT):写透
读被缓存在Cache中。
写操作会直接写到内存中。
如果缓存命中,则更新缓存,或者使用缓存失效(Invalidate)。
这种方式本身可以保证一致性(Coherency)。
Write Back(WB):
读写都被缓存在Cache中。
写操作会被积累在缓存中,直到有人触发了write-back操作,才被写回到内存中。
或者当缓存行选中让位时,需要先将缓存行中的内容回写到内存中。
Write Protected(WP):
读缓存在Cache中。
写操作首先传到系统总线上,即写到内存中。
然后让所有的processor的缓存中,与写的内存相关的缓存行全部失效。
DMA操作时对缓存的影响与依赖
通过DMA模式,从外部设备读入了一段数据到内存时,需要将该段内存对应的高速缓存行全部清空。注意,DMA操作是外设与内存之间的直接交换数据,不会经过CPU,因此也不会经过缓存。但是DMA操作之后,内存与缓存之间可能出现不一致现象,因此需要使相应的缓存失效。
同样,如果需要进行DMA操作,将内存中的一段数据写出到外部设备上时,也需要先将缓存中的内容回写到内存中,再从内存中回写到外部设备上。
广义上的缓存,大致有三种类型:
Cache:
这是狭义上的缓存,包含数据缓存和指令缓存,通常L1缓存分为数据和指令缓存两种,而L2和L3都是Unified Cache。
指令缓存,CPU基本上已经很好的支持和优化了,比如分支预测等等。
TLB: Translation Look-aside Buffers,快表
为了回忆分页机制的页面映射过程,会将页目录以及页表中的一部分先缓存在CPU内部的Buffer中,这个Buffer就是TLB。
这是专门用于分页机制的缓存。
Write Buffer
就是上文提到的WC Buffer。
CPU对内存进行写操作时,如果当前系统总线已经被锁住,此时可以将内容先写到一个缓存中,狭义上的Cache可以充当这个角色,但是如果对于Write Combining,并没有利用到狭义Cache时,就提供了WC Buffer供CPU来作写缓冲。
可以通过两种方式,控制某段内存区域适用的缓存方式:
1. 通过页表(PAT)中项目的字段,可以以页为粒度,对该页适合的缓存方式进行指定;
2. 通过MTRR进行设置,可以设置任意粒度的内存区域适用的缓存方式。
Order在缓存中和内存中的反应
处理器执行一段程序时,缓存接收到的改变和内存接收到的改变并不完全相同,体现出来的指令的序列也不相同。
指令序,Program Ordering, 指的就是缓存接收到的顺序,与执行的程序中的顺序是一样的;
处理器序,Processor Ordering,指的是出现在系统总线上的顺序,可以与指令序不同。
如果二者相同,称为“强序”, 否则,称为“弱序”
CPUID读取有关Cache的信息的更多相关文章
- JAVA基础-输入输出:1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上。
1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上. package Test03; ...
- Delphi中建立指定大小字体和读取该字体点阵信息的函数(转)
源:Delphi中建立指定大小字体和读取该字体点阵信息的函数 Delphi中建立指定大小字体和读取该字体点阵信息的函数 作者:Thermometer Email: webmaster@daheng- ...
- spring读取数据库的配置信息(url、username、password)时的<bean>PropertyPlaceholderConfigurer的用法
用法1: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://w ...
- 二、Delphi10.3在不下载文件情况下读取网站文件大小等信息
一.上源码 uses TxHttp, Classes, TxCommon, Frm_WebTool, SysUtils; var m_Url: string; m_Http: TTxHttp; m_P ...
- Windows mobile 下读取手机SIM卡信息(转)
Windows mobile 下读取手机SIM卡信息 c#改善 Windows mobile 下读取手机SIM卡信息
- GOEXIF读取和写入EXIF信息
最新版本的gexif,直接基于gdi+实现了exif信息的读取和写入,代码更清晰. /* * File: gexif.h * Purpose: cpp EXIF reader * 3/2/2017 & ...
- 用C#读取图片的EXIF信息的方法
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Dr ...
- PHP读取APK的包信息,包括包名,应用名,权限,LOGO等
[转]PHP读取APK的包信息,包括包名,应用名,权限,LOGO等 声明本文转自: 原文链接:https://www.jb51.net/article/53780.htm: 感谢分享! <?ph ...
- [Xcode 实际操作]九、实用进阶-(8)实现App的Setting设置:添加和读取程序的配置信息
目录:[Swift]Xcode实际操作 本文将演示如何实现添加和读取程序的配置信息. 在项目文件夹[DemoApp]上点击鼠标右键->[New File]创建一个设置束文件 ->[Sett ...
随机推荐
- python 找到列表中满足某些条件的元素
a = [0, 1, 2, 3, 4, 0, 2, 3, 6, 7, 5] selected = [x for x in a if x in range(1, 5)] # 找到a中属于[1,5)中 ...
- MacBook Pro 快捷键2
Mac 键盘快捷键 您可以按下组合键来实现通常需要鼠标.触控板或其他输入设备才能完成的操作. 要使用键盘快捷键,请按住一个或多个修饰键,同时按快捷键的最后一个键.例如,要使用快捷键 Command ...
- 70、saleforce的Json输出
List<Merchandise__c> merchandise = [select Id,Name,Price__c,Quantity__c from Merchandise__c li ...
- python作业/练习/实战:1、简单登录脚本
作业要求 写一个登陆的小程序 username = xiaoming passwd = 123456 1.输入账号密码,输入正确就登陆成功, 提示:欢迎xxxx登陆,今天的日期是xxx. 2.输入错误 ...
- USACO 2014 US Open Odometer /// 枚举
题目大意: 给定区间 l r 求区间包含多少个数 它们各个位的数只有一个不一样 注意 多个位但多个数为0单个数为x的情况 这种情况只有 x000 即把单个数放在首位才是正确的 同样注意 多个位但单个数 ...
- 《构建之法》需求分析 读书笔记 Week6
本周选读<构建之法>第8章——需求分析.由于有团队项目初期调研阶段做调查问卷的经历,这一章节中很多知识点我都比较有体会.对我而言,这一章节最有价值的内容就是厘清了关于需求分析的两个误解和近 ...
- Javascript高级程序设计--读书笔记之理解原型对象
先上一段代码和关系图 function Person(){} Person.prototype.name = "Nic" Person.prototype.age = 22 Per ...
- Javascript高级程序设计--读书笔记之Array类型
1.数组的lenght属性 数组的lenght属性很有特点---他不是只读的,可以同过修改这个属性来向数组的末尾添值加或删除值, 删除值 var color = ["red", & ...
- 【转载】vue install报错run `npm audit fix` to fix them, or `npm audit` for details html
原链接https://www.jianshu.com/p/60591cfc6952 执行npm install 出现如下提醒 added 253 packages from 162 contribut ...
- 2018-2-13-win10-uwp-网络编程
title author date CreateTime categories win10 uwp 网络编程 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 17: ...