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. RHEL6.1 安装 Oracle10gr2 (图文、解析)

    目录 目录 软件环境 前言 初始化RHEL61 硬件检测 预安装软件包 安装oratoolkit 创建Oracle用户 修改配置文件 系统版本伪装 解压并运行Oracle10gr2安装包 安装rlwr ...

  2. oraToolKit Oracle安装辅助工具的使用方法

    目录 目录 otk使用方式 使用oraToolKit进行检测安装包情况 使用oraToolKit进行检测操作系统情况 最后 otk使用方式 oraToolkit的安装在RHEL6.1 安装 Oracl ...

  3. MySQL高级学习笔记(一):mysql简介、mysq linux版的安装(mysql 5.5)

    文章目录 MySQL简介 概述 mysql高手是怎样炼成的 mysq linux版的安装(mysql 5.5) 下载地址 拷贝&解压缩 检查工作 检查当前系统是否安装过mysql 检查/tmp ...

  4. canvas简单画图板

    <!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <title> ...

  5. sqlserver定时作业,定时执行存储过程

    首先,我想说,我真的是渣了,一个这个玩意弄了半天,算了,直接切入正题吧. 第一步: 先写好存储过程 用了两张表,你们自己建立吧 <br data-filtered="filtered& ...

  6. Rust <9>:启用第三方库的备选 features

  7. python学习笔记:循环语句——while、for

    python中有两种循环,while和for,两种循环的区别是,while循环之前,先判断一次,如果满足条件的话,再循环,for循环的时候必须有一个可迭代的对象,才能循环,比如说得有一个数组.循环里面 ...

  8. Javascript中的相等比较

    在比较相等或不相等之前,会对操作数进行类型转换,然后比较相等性 在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则: 1.如果由一个操作数是布尔值,则在比较相等性之前先将其转换为数值:2.如果 ...

  9. CSUST 集训队选拔赛题解

    选拔赛的题解,~~~ 题目链接:请点击 A题 素数筛 + 线段树(树状数组) 先用素数筛打表,然后线段树更新,遍历求出值,O(1)查询即可 AC代码: /*num数组 是把记录 数是否存在 存在即为1 ...

  10. ArcGis 创建含孔洞面要素AO C#

    IGeometryCollection geometryCollection = new PolygonClass(); IPointCollection pointCollection_Exteri ...