这其实是个不明确的问题,问题的主语应该具体到每一颗不同型号的CPU上,而非一类架构的CPU集合。
众说纷纭的个数
刚开始学习计算机的时候看过如下类似的文章:
总之是说多少个的都有,14个,16个,32个,40个。后来在学习操作系统的时候,发现操作系统要使用都没听说过的寄存器,那x86上到底有多少寄存器呢?
bochs调试
在自己编写操作系统时,经常要用到bochs这个软件,是一个x86计算机的模拟器。其实在我们没有能力研究硬件本身时,比如我们不能把cpu拆开去数里面寄存器的个数时,研究硬件的模拟器是个非常好的选择。使用bochs的调试功能,有如下调试命令,功能均为查看寄存器状态:
info cpu
r
sreg
creg
我把断点打在0x7c00处,然后分别执行以上四种查看寄存器的命令:
<bochs:1> b 0x7c00
<bochs:2> c
<bochs:3> info cpu
CPU0:
rax: 00000000_0000aa55 rcx: 00000000_00090000
rdx: 00000000_00000000 rbx: 00000000_00000000
rsp: 00000000_0000ffd6 rbp: 00000000_00000000
rsi: 00000000_000e0000 rdi: 00000000_0000ffac
r8 : 00000000_00000000 r9 : 00000000_00000000
r10: 00000000_00000000 r11: 00000000_00000000
r12: 00000000_00000000 r13: 00000000_00000000
r14: 00000000_00000000 r15: 00000000_00000000
rip: 00000000_00007c00
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
status word: 0x0000: b c3 TOS0 c2 c1 c0 es sf pe ue oe ze de ie
control word: 0x0040: inf RC_NEAREST PC_32 pm um om zm dm im
tag word: 0x5555
operand: 0x0000
fip: 0x0000000000000000
fcs: 0x0000
fdp: 0x0000000000000000
fds: 0x0000
=>FP0 ST0(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP1 ST1(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP2 ST2(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP3 ST3(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP4 ST4(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP5 ST5(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP6 ST6(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP7 ST7(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
MM[0]: 00000000_00000000
MM[1]: 00000000_00000000
MM[2]: 00000000_00000000
MM[3]: 00000000_00000000
MM[4]: 00000000_00000000
MM[5]: 00000000_00000000
MM[6]: 00000000_00000000
MM[7]: 00000000_00000000
The CPU doesn t support AVX state !
<bochs:4> r
CPU0:
rax: 00000000_0000aa55 rcx: 00000000_00090000
rdx: 00000000_00000000 rbx: 00000000_00000000
rsp: 00000000_0000ffd6 rbp: 00000000_00000000
rsi: 00000000_000e0000 rdi: 00000000_0000ffac
r8 : 00000000_00000000 r9 : 00000000_00000000
r10: 00000000_00000000 r11: 00000000_00000000
r12: 00000000_00000000 r13: 00000000_00000000
r14: 00000000_00000000 r15: 00000000_00000000
rip: 00000000_00007c00
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
<bochs:5> sreg
es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
cs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
gdtr:base=0x00000000000f9a37, limit=0x30
idtr:base=0x0000000000000000, limit=0x3ff
<bochs:6> creg
CR0=0x60000010: pg CD NW ac wp ne ET ts em mp pe
CR2=page fault laddr=0x0000000000000000
CR3=0x000000000000
PCD=page-level cache disable=0
PWT=page-level write-through=0
CR4=0x00000000: pke smap smep osxsave pcid fsgsbase smx vmx osxmmexcpt umip osfxsr pce pge mce pae pse de tsd pvi vme
CR8: 0x0
EFER=0x00000000: ffxsr nxe lma lme sce
原来有这么多的寄存器呀!除了r命令的结果是info cpu命令结果的前面部分,剩下的都是不同的,去重整理如下:
CPU0:
rax: 00000000_0000aa55 rcx: 00000000_00090000
rdx: 00000000_00000000 rbx: 00000000_00000000
rsp: 00000000_0000ffd6 rbp: 00000000_00000000
rsi: 00000000_000e0000 rdi: 00000000_0000ffac
r8 : 00000000_00000000 r9 : 00000000_00000000
r10: 00000000_00000000 r11: 00000000_00000000
r12: 00000000_00000000 r13: 00000000_00000000
r14: 00000000_00000000 r15: 00000000_00000000
rip: 00000000_00007c00
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
status word: 0x0000: b c3 TOS0 c2 c1 c0 es sf pe ue oe ze de ie
control word: 0x0040: inf RC_NEAREST PC_32 pm um om zm dm im
tag word: 0x5555
operand: 0x0000
fip: 0x0000000000000000
fcs: 0x0000
fdp: 0x0000000000000000
fds: 0x0000
=>FP0 ST0(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP1 ST1(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP2 ST2(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP3 ST3(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP4 ST4(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP5 ST5(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP6 ST6(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
FP7 ST7(0): raw 0x0000:0000000000000000 (0.0000000000) (ZERO)
MM[0]: 00000000_00000000
MM[1]: 00000000_00000000
MM[2]: 00000000_00000000
MM[3]: 00000000_00000000
MM[4]: 00000000_00000000
MM[5]: 00000000_00000000
MM[6]: 00000000_00000000
MM[7]: 00000000_00000000
The CPU doesn t support AVX state !
es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
cs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
gdtr:base=0x00000000000f9a37, limit=0x30
idtr:base=0x0000000000000000, limit=0x3ff
CR0=0x60000010: pg CD NW ac wp ne ET ts em mp pe
CR2=page fault laddr=0x0000000000000000
CR3=0x000000000000
PCD=page-level cache disable=0
PWT=page-level write-through=0
CR4=0x00000000: pke smap smep osxsave pcid fsgsbase smx vmx osxmmexcpt umip osfxsr pce pge mce pae pse de tsd pvi vme
CR8: 0x0
EFER=0x00000000: ffxsr nxe lma lme sce
当然可以看到这里的寄存器是x86_64下的,这里查出来是58个。
qemu的结构体
既然说模拟器,那肯定少不了qemu!是这篇文章给我启发:Qemu对内部寄存器的模拟,去找了CPUX86State这个结构体:
typedef struct CPUX86State {
/* standard registers */
target_ulong regs[CPU_NB_REGS];
target_ulong eip;
target_ulong eflags; /* eflags register. During CPU emulation, CC
flags and DF are set to zero because they are
stored elsewhere */
/* emulator internal eflags handling */
target_ulong cc_dst;
target_ulong cc_src;
target_ulong cc_src2;
uint32_t cc_op;
int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */
uint32_t hflags; /* TB flags, see HF_xxx constants. These flags
are known at translation time. */
uint32_t hflags2; /* various other flags, see HF2_xxx constants. */
/* segments */
SegmentCache segs[6]; /* selector values */
SegmentCache ldt;
SegmentCache tr;
SegmentCache gdt; /* only base and limit are used */
SegmentCache idt; /* only base and limit are used */
target_ulong cr[5]; /* NOTE: cr1 is unused */
int32_t a20_mask;
BNDReg bnd_regs[4];
BNDCSReg bndcs_regs;
uint64_t msr_bndcfgs;
uint64_t efer;
/* Beginning of state preserved by INIT (dummy marker). */
struct {} start_init_save;
/* FPU state */
unsigned int fpstt; /* top of stack index */
uint16_t fpus;
uint16_t fpuc;
uint8_t fptags[8]; /* 0 = valid, 1 = empty */
FPReg fpregs[8];
/* KVM-only so far */
uint16_t fpop;
uint64_t fpip;
uint64_t fpdp;
/* emulator internal variables */
float_status fp_status;
floatx80 ft0;
float_status mmx_status; /* for 3DNow! float ops */
float_status sse_status;
uint32_t mxcsr;
ZMMReg xmm_regs[CPU_NB_REGS == 8 ? 8 : 32];
ZMMReg xmm_t0;
MMXReg mmx_t0;
XMMReg ymmh_regs[CPU_NB_REGS];
uint64_t opmask_regs[NB_OPMASK_REGS];
YMMReg zmmh_regs[CPU_NB_REGS];
ZMMReg hi16_zmm_regs[CPU_NB_REGS];
/* sysenter registers */
uint32_t sysenter_cs;
target_ulong sysenter_esp;
target_ulong sysenter_eip;
uint64_t star;
uint64_t vm_hsave;
#ifdef TARGET_X86_64
target_ulong lstar;
target_ulong cstar;
target_ulong fmask;
target_ulong kernelgsbase;
#endif
uint64_t tsc;
uint64_t tsc_adjust;
uint64_t tsc_deadline;
uint64_t tsc_aux;
uint64_t xcr0;
uint64_t mcg_status;
uint64_t msr_ia32_misc_enable;
uint64_t msr_ia32_feature_control;
uint64_t msr_fixed_ctr_ctrl;
uint64_t msr_global_ctrl;
uint64_t msr_global_status;
uint64_t msr_global_ovf_ctrl;
uint64_t msr_fixed_counters[MAX_FIXED_COUNTERS];
uint64_t msr_gp_counters[MAX_GP_COUNTERS];
uint64_t msr_gp_evtsel[MAX_GP_COUNTERS];
uint64_t pat;
uint32_t smbase;
uint64_t msr_smi_count;
uint32_t pkru;
uint32_t tsx_ctrl;
uint64_t spec_ctrl;
uint64_t virt_ssbd;
/* End of state preserved by INIT (dummy marker). */
struct {} end_init_save;
uint64_t system_time_msr;
uint64_t wall_clock_msr;
uint64_t steal_time_msr;
uint64_t async_pf_en_msr;
uint64_t pv_eoi_en_msr;
uint64_t poll_control_msr;
/* Partition-wide HV MSRs, will be updated only on the first vcpu */
uint64_t msr_hv_hypercall;
uint64_t msr_hv_guest_os_id;
uint64_t msr_hv_tsc;
/* Per-VCPU HV MSRs */
uint64_t msr_hv_vapic;
uint64_t msr_hv_crash_params[HV_CRASH_PARAMS];
uint64_t msr_hv_runtime;
uint64_t msr_hv_synic_control;
uint64_t msr_hv_synic_evt_page;
uint64_t msr_hv_synic_msg_page;
uint64_t msr_hv_synic_sint[HV_SINT_COUNT];
uint64_t msr_hv_stimer_config[HV_STIMER_COUNT];
uint64_t msr_hv_stimer_count[HV_STIMER_COUNT];
uint64_t msr_hv_reenlightenment_control;
uint64_t msr_hv_tsc_emulation_control;
uint64_t msr_hv_tsc_emulation_status;
uint64_t msr_rtit_ctrl;
uint64_t msr_rtit_status;
uint64_t msr_rtit_output_base;
uint64_t msr_rtit_output_mask;
uint64_t msr_rtit_cr3_match;
uint64_t msr_rtit_addrs[MAX_RTIT_ADDRS];
/* exception/interrupt handling */
int error_code;
int exception_is_int;
target_ulong exception_next_eip;
target_ulong dr[8]; /* debug registers; note dr4 and dr5 are unused */
union {
struct CPUBreakpoint *cpu_breakpoint[4];
struct CPUWatchpoint *cpu_watchpoint[4];
}; /* break/watchpoints for dr[0..3] */
int old_exception; /* exception in flight */
uint64_t vm_vmcb;
uint64_t tsc_offset;
uint64_t intercept;
uint16_t intercept_cr_read;
uint16_t intercept_cr_write;
uint16_t intercept_dr_read;
uint16_t intercept_dr_write;
uint32_t intercept_exceptions;
uint64_t nested_cr3;
uint32_t nested_pg_mode;
uint8_t v_tpr;
/* KVM states, automatically cleared on reset */
uint8_t nmi_injected;
uint8_t nmi_pending;
uintptr_t retaddr;
/* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields;
/* Fields after this point are preserved across CPU reset. */
/* processor features (e.g. for CPUID insn) */
/* Minimum cpuid leaf 7 value */
uint32_t cpuid_level_func7;
/* Actual cpuid leaf 7 value */
uint32_t cpuid_min_level_func7;
/* Minimum level/xlevel/xlevel2, based on CPU model + features */
uint32_t cpuid_min_level, cpuid_min_xlevel, cpuid_min_xlevel2;
/* Maximum level/xlevel/xlevel2 value for auto-assignment: */
uint32_t cpuid_max_level, cpuid_max_xlevel, cpuid_max_xlevel2;
/* Actual level/xlevel/xlevel2 value: */
uint32_t cpuid_level, cpuid_xlevel, cpuid_xlevel2;
uint32_t cpuid_vendor1;
uint32_t cpuid_vendor2;
uint32_t cpuid_vendor3;
uint32_t cpuid_version;
FeatureWordArray features;
/* Features that were explicitly enabled/disabled */
FeatureWordArray user_features;
uint32_t cpuid_model[12];
/* Cache information for CPUID. When legacy-cache=on, the cache data
* on each CPUID leaf will be different, because we keep compatibility
* with old QEMU versions.
*/
CPUCaches cache_info_cpuid2, cache_info_cpuid4, cache_info_amd;
/* MTRRs */
uint64_t mtrr_fixed[11];
uint64_t mtrr_deftype;
MTRRVar mtrr_var[MSR_MTRRcap_VCNT];
/* For KVM */
uint32_t mp_state;
int32_t exception_nr;
int32_t interrupt_injected;
uint8_t soft_interrupt;
uint8_t exception_pending;
uint8_t exception_injected;
uint8_t has_error_code;
uint8_t exception_has_payload;
uint64_t exception_payload;
uint32_t ins_len;
uint32_t sipi_vector;
bool tsc_valid;
int64_t tsc_khz;
int64_t user_tsc_khz; /* for sanity check only */
#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
void *xsave_buf;
#endif
#if defined(CONFIG_KVM)
struct kvm_nested_state *nested_state;
#endif
#if defined(CONFIG_HVF)
HVFX86EmulatorState *hvf_emul;
#endif
uint64_t mcg_cap;
uint64_t mcg_ctl;
uint64_t mcg_ext_ctl;
uint64_t mce_banks[MCE_BANKS_DEF*4];
uint64_t xstate_bv;
/* vmstate */
uint16_t fpus_vmstate;
uint16_t fptag_vmstate;
uint16_t fpregs_format_vmstate;
uint64_t xss;
uint32_t umwait;
TPRAccess tpr_access_type;
unsigned nr_dies;
} CPUX86State;
查不过来,反正很多…不过可以看出,对于x86架构的CPU,具体到每一颗CPU上,功能不同,其所拥有的寄存器也不同。
不止我问
还是得查英文资料,有人问,也有人答:
- How many registers does an x86_64 CPU actually have?
- https://en.wikipedia.org/wiki/X86#x86_registers
就像如上回答所说,已知寄存器的个数实际很多,而且的确存在没有公开的寄存器。那他们在做着什么不为人知的事呢?