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, &regs[0], &regs[1], &regs[2], &regs[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的信息的更多相关文章

  1. JAVA基础-输入输出:1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上。

    1.编写TextRw.java的Java应用程序,程序完成的功能是:首先向TextRw.txt中写入自己的学号和姓名,读取TextRw.txt中信息并将其显示在屏幕上. package Test03; ...

  2. Delphi中建立指定大小字体和读取该字体点阵信息的函数(转)

    源:Delphi中建立指定大小字体和读取该字体点阵信息的函数 Delphi中建立指定大小字体和读取该字体点阵信息的函数 作者:Thermometer Email:  webmaster@daheng- ...

  3. spring读取数据库的配置信息(url、username、password)时的<bean>PropertyPlaceholderConfigurer的用法

    用法1: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://w ...

  4. 二、Delphi10.3在不下载文件情况下读取网站文件大小等信息

    一.上源码 uses TxHttp, Classes, TxCommon, Frm_WebTool, SysUtils; var m_Url: string; m_Http: TTxHttp; m_P ...

  5. Windows mobile 下读取手机SIM卡信息(转)

    Windows mobile 下读取手机SIM卡信息 c#改善 Windows mobile 下读取手机SIM卡信息

  6. GOEXIF读取和写入EXIF信息

    最新版本的gexif,直接基于gdi+实现了exif信息的读取和写入,代码更清晰. /* * File: gexif.h * Purpose: cpp EXIF reader * 3/2/2017 & ...

  7. 用C#读取图片的EXIF信息的方法

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Dr ...

  8. PHP读取APK的包信息,包括包名,应用名,权限,LOGO等

    [转]PHP读取APK的包信息,包括包名,应用名,权限,LOGO等 声明本文转自: 原文链接:https://www.jb51.net/article/53780.htm: 感谢分享! <?ph ...

  9. [Xcode 实际操作]九、实用进阶-(8)实现App的Setting设置:添加和读取程序的配置信息

    目录:[Swift]Xcode实际操作 本文将演示如何实现添加和读取程序的配置信息. 在项目文件夹[DemoApp]上点击鼠标右键->[New File]创建一个设置束文件 ->[Sett ...

随机推荐

  1. SVN迁移Gitlab步骤

    概述 公司要求将之前使用SVN进行管理的项目迁移到Gitlab进行项目管理,但是运维连不上我们这边的SVN服务器,于是我们就得自己将SVN项目迁移到Gitlab.Yeah!终于有我表现的机会了. 要求 ...

  2. JAVA学习之面向对象

    面向对象是相对面向过程而言面向过程:强调的是功能行为面向对象:将功能封装进对象,强调具备了功能的对象 不论面向对象还是面向过程都是一种开发思想而已.举一个例子来理解面向对象和面向过程把大象装进冰箱分三 ...

  3. MySQL高级学习笔记(四):索引优化分析

    文章目录 性能下降 SQL慢 执行时间长 等待时间长 查询语句写的烂 查询数据过多 关联了太多的表,太多join 没有利用到索引 单值 复合 服务器调优及各个参数设置(缓冲.线程数等)(不重要DBA的 ...

  4. 03 java语言基础逻辑运算符

    03.01_Java语言基础(逻辑运算符的基本用法) A:逻辑运算符有哪些 &,|,^,! &&,|| B:案例演示 逻辑运算符的基本用法 注意事项: a:逻辑运算符一般用于连 ...

  5. A1082 Read Number in Chinese (25 分)

    1082 Read Number in Chinese (25 分)   Given an integer with no more than 9 digits, you are supposed t ...

  6. PAT甲级——A1143 LowestCommonAncestor【30】

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  7. el-form-item内容过多,及弹窗框宽度属性show-overflow-tooltip设置

    内容过多: :show-overflow-tooltip=true 宽度属性设置: .el-tooltip__popper{ max-width:30% }

  8. web服务器和应用服务器以及web应用框架介绍

    ### web服务器:负责处理http请求,响应静态文件,常见的有Apache,Nginx以及微软的IIS. ### 应用服务器:负责处理逻辑的服务器.比如php.python的代码,是不能直接通过n ...

  9. mySQL学习入门教程——2.创建表

    二.创建表 一.创建数据表的SQL语句模型(弱类型)CREATE TABLE [IF NOT EXISTS] 表名称(字段名1 列的类型[属性][索引],字段名2 列的类型[属性][索引],-字段名n ...

  10. scrollHeight与offsetHeight

    offsetXxx 是 HTMLElement 的属性, HTMLElement 接口表示所有的 HTML 元素,scrollXxx 是 Element 的属性,Element 是一个通用性非常强的基 ...