8块大电路板元件焊接视频,每个板子有18个256kb的ram为什么容量是2MB

FPGA开发板剁手,学生狗省钱大法丨吐血资源
作者 Joel Williams
伊瓢 栗子 编译
博主Joel Williams在他的主页中分享了一篇购买便宜的FPGA开发板的攻略,量子位编译本文。
我以前买了安富利( Avnet)49美元的Spartan 3A开发板,但不久之后,就在我决定再需要几十台的时候——它停产了。于是,我谷歌了很多资料,发现了这个省钱大法。
在选择开发板时,需要考虑开发板的元器件和功能,结合自己需要的功能来对比。FPGA非常适合与高速外设一起使用,一般来说,购买包含需要部件的开发板要比后来加一个新板子容易得多,因为你总是不可避免地放弃这种组合形态,然后升级成更强大的电路板。
比如,你可能需要下面这些很难自己添加东西:
千兆以太网
HDMI / DVI
PCI / PCI Express
外部非串行内存(DDR / Flash等)
不过这些倒很容易自己加,不用太考虑:
MMC / SD卡
VGA(低色深)
我喜欢有带有多个(至少8个)SPST开关和LED以及瞬时按钮的电路板。与微控制器相比,调制信息从串行端口或通过单个C函数调用的LCD调出相对容易,调试FPGA设计有点困难。LED提供了一种零操作方式来突破内部信号以实现可视化——如果你正在跟踪复杂状态机的进度,则可以在LED达到某一点时点亮LED,而无需添加任何额外的逻辑。
虽然这些很容易自己额外添加,但我发现最好找一块自带这些元件的电路板,这样你就不会浪费宝贵的I/O接口、或者浪费时间来研究你渣渣的焊接技术导致的故障了。
一些制造商推出了附加产品的标准外形尺寸,比如Digilent家,包括各种各样的Pmod和Papilio Wings。
如果你想将高速设备(10-20 MHz以上)连接到FPGA,请确保你的主板有支持这个速度的接口连接器,在信号线、高速连接器(不仅仅是0.1“接头)、PCB走线长度均衡和阻抗控制之间有接地线,即使是便宜的电路板也不能缺了这些东西。
FPGA可能看起来比较难,因此请检查制造商是否提供:
参考手册,介绍所有板载外设;
如果之前没用过FPGA,就需要看入门指南;
可以运行所有板载外设的参考设计。
参考设计可以是HDL或基于微控制器,但是近年来,大多数制造商似乎都倾向后者。如果没有微控制器和环境的许可证(例如Xilinx EDK / SDK不是免费的),就一定要记住这一点,因为代码很难移植到HDL。
如果是初学者,你可以买带教科书的电路板,专门给配套电路板编写,描述每个外设以及它们的连接方式。一些流行的电路板搞了很大的用户社区,但是这不一定有用,因为社区里的其他用户大部分也是初学者。最流行的主板是赛灵思、Digilent和Avnet。Terasic似乎是最受欢迎的Altera主板。
赛灵思ZYNQ
赛灵思的Zynq器件由其Vivado高级综合设计套件提供支持,包括双核ARM Cortex-A9,USB 2.0和千兆以太网。
DIPFORTy1“软螺旋桨”
DIP-40尺寸的电路板,设计与Parallax Propeller芯片引脚兼容。它有16MB闪存,46个I/O接口,一个RGB LED,一个用户LED,micro SD插槽和一个接近/光传感器。
snickerdoodle
一大波供应链已经开始出售带有154-179个I/O接口,512MB-1GB DDR,16MB闪存,micro SD,802.11n WiFi和蓝牙4.0的Zynq主板。还提供一系列基板和附加板,提供Arduino屏蔽兼容性,千兆以太网,0.1''I/O接口等。
Zynq 7Z007S
包括单个ARM A9,512MB DDR3L,128Mb闪存和8GB eMMC,USB主机,USB-JTAG和USB-UART,802.11b / g / n Wi-Fi,蓝牙4.1和BLE,Arduino屏蔽连接器和两个PMOD( 38个总I/O接口),加速度计,温度和MEMS麦克风传感器,一个按钮,一个开关和两个双色LED
MYIR Z-turn Board
$99或 $119
1GB DDR,16MB闪存,TF插座,千兆以太网,CAN,USB2.0 OTG,USB-UART,HDMI输出,90或106个用户I/O接口(具有39个LVDS对),加速度计和温度传感器,JTGA,两个按钮, 4个开关,4个LED和一个蜂鸣器。“IO Cape”分线板(35美元)提供三个Pmod连接器,摄像头和LCD连接器以及0.1''插头I/O接口引脚。
Parallella-16微服务器
包括双ARM A9。主板上还有Epiphany 16核CPU加速器,1GB RAM,126 Mb闪存,micro SD和千兆以太网。
Parallella-16桌面
扩展了Micro-Server,增加了24个GPIO(和其他Epiphany信号),HDMI和USB 2.0主机的高速扩展端口。
Digilent ZYBO
$189,学生价$125
512MB,HDMI源/接收器,VGA,千兆以太网,USB,音频,6个按钮,4个开关,5个LED和40个I/O接口(5个PMOD),包括模拟输入。
1GB,128 Mb闪存,SD卡,千兆以太网,USB 2.0,100个I/O接口(48个LVDS对)和2个PMOD,1个LED和1个开关
Artix系列在廉价开发板中越来越常见,占据了先前Silinan-6在赛灵思阵容中占据的位置,尽管它们只提供BGA封装。
Digilent Cmod A7
Artix 15T / 45T
带有512KB SRAM,4MB SPI闪存,USB-JTAG和USB串行,3个LED,2个按钮,52个数字I/O接口和2个模拟输入的面包板模块。
入门Artix的廉价途径。它提供256 MB DDR,16MB闪存,10/100以太网,USB-UART / JTAG,四个PMOD,一个Arduino屏蔽连接器(总共62个I/O接口?),4个开关,4个按钮,8个LED(其中4个) RGB),以及Vivado Design Edition的一年许可证。
Digilent Basys 3
USB-UART,12位VGA输出,USB HID主机,16个开关,16个LED,5个按钮,4位7段显示器,4个PMOD,其中一个带有XADC输入。设备锁定的Vivado设计版售价10美元。
Nexys-4 DDR
$320,学生价$159
Artix 100T
拿不到学生价的话略贵。但如果你需要更大的FPGA,这一款就很棒了。包括5个PMOD连接器(40个低速I/O接口),128MB DDR RAM,16MB闪存,10/100以太网,USB HID主机,SD卡,VGA,加速度计,麦克风,音频输出,16个开关,16个LED,8个7-分段显示,5个按钮。Artix的内部ADC可在其中一个端口上使用。
ZTEX USB-FPGA模块2.16
通过赛普拉斯FX2LP,100 I/O接口,256 MB DDR3 SDRAM(16位)和128 Mb闪存的USB 2.0接口。便宜的原型板(17欧元)为您提供30个LED,4个开关和焊接区域。
Numato Neso
249美元,学术可用
Artix 100T
256MB DDR3,128 Mb SPI闪存,用于闪存编程的USB 2.0接口和8个数字I/O接口,JTAG和140个FPGA I/O接口.
4个模拟/数字/ LVDS I/O接口,一个高速MGT和时钟参考输入,带有U.FL连接器,MGT环回,3个LED,1个PCIe通道和板载JTAG编程器,采用NGFF / M.2外形,可以是安装在便携式计算机内用于PCIe开发
$ 99或$119
XC7S25 / 50
256 MB DDR,16MB闪存,USB-UART / JTAG,四个PMOD,一个Arduino屏蔽连接器(总共62个I/O接口?),4个开关,4个按钮,6个LED(其中2个RGB)。
XC6SLX9入门板
一个“无名”板显然只在eBay上可用。它有一个Spartan-6 LX9,4位7段显示器,RS232接口,12位VGA,PS2,8个LED,3个按钮,8位DIP开关,2个PMOD接口,26个数字I/O接口,JTAG和SPI闪存。
澳大利亚设计包含32MB RAM,一个LED和38个I/O接口,使用PCIE 4x连接器确保高速信号完整性
Numato Mimas
16 Mb闪存,100 MHz振荡器,USB编程接口,8个LED,四个开关和70个I/O接口.
Digilent Cmod S6
带有16MB SPI闪存,USB编程和通信(与Digilent Adept兼容),4个LED,2个按钮和46个数字I/O接口的面包板模块。
84个数字I/O接口引脚,8个模拟输入(经由ATmega16U4),8个LED,配置闪存和USB编程(经由ATmega)。该设计获得CC许可。
miniSpartan6 +
LX9 / LX25
两个HDMI端口,可用作输入或输出,USB 2.0编程和通信,8通道8位1 MSPS ADC,32 MB SDRAM,64 Mbit SPI闪存,microSD插槽,立体声音频输出,64位数字I/O接口 ,8个LED和4个DIP开关。
Numato Saturn
一系列具有16 Mb闪存,100 MHz振荡器,512 Mb LPDDR RAM,USB编程接口和GPIO(通过FT2232H)以及118-150 I/O接口的电路板。
Papilio Pro
开源(CC)板,保留与原始Papilio制造的扩展翼的兼容性。它具有48个I/O接口,用于JTAG编程和串行通信的USB 2.0,64 Mbit SDRAM和64 MBit SPI闪存。
Papilio Duo
包含Arduino Leonardo的开源设计,512或2048 KB SRAM,54(或更多?)个I/O接口,包括6个翼和1个PMOD连接器,USB和原理图设计软件。
Avnet Spartan-6微型板
它专为嵌入式处理器开发而设计,并附带设备锁定SDK和ChipScope Pro许可证,这非常好,因为这些单独购买相当昂贵。单独使用ChipScope是非常宝贵的。I/O接口非常简陋,只有两个8位PMOD连接器,但有一个10/100以太网PHY,四个LED,两个SPST按钮,一个4路DIP开关,64 MB SDRAM和128 Mb闪存。许可证实际上是设备锁定到XC6SLX9(任何软件包),因此如果您使用此系列开发自己的电路板,它可能会非常方便。
LX9 / LX16
一块令人困惑的FPGA板阵列。引起我注意的两个是5I25,这是一张PCI卡,Spartan-6 LX9售价89美元,6I25(PCI Express)售价109美元。
74-119欧元
一系列具有96-100个I/O接口的模块,一些采用USB编程,顶部采用64MB DDR RAM。为了开源,所以价格很实惠。
设计用于运行Oberon RISC系统,但也适用于一般开发的电路板。提供1-2 MB SRAM,USB串行/ JTAG,8位VGA输出,两个用于键盘和鼠标的PS / 2连接器,立体声音频输出,micro-SD,8个DIP开关,1个按钮,9个LED,22个I/O和SPI闪存。
XuLA2-LX25
小型面包板PCB,内置USB编程器,32 MB RAM,8 Mb闪存,33个I/O接口和SD卡插槽。该设计完全是开源的。
Pipistrello
包含128 Mbit SPI闪存,64 MB DDR,USB串行/ FIFO和JTAG,HDMI输出,音频输出,USB主机,micro SD,两个LED,PMOD接头,支持具有48个I/O接口的Papilio wings。
EDGE Spartan 6
8,500卢比($132)
开发板,带8MB SPI闪存,USB JTAG编程器,USB UART,WiFi,蓝牙,VGA,8通道12位ADC,12位DAC,温度传感器,LDR,2x16 LCD,4 * 7段显示,蜂鸣器, 16个SPST开关,16个LED,5个按钮,外部CMOS摄像头和TFT显示模块。
Digilent Nexys 3
$270,学生价$189
有一堆I/O接口,一个高速VHDCI连接器(配对,但不幸的是它看起来不像所有迹线长度匹配),10/100以太网,USB主机和USB-RS232,VGA,蜂窝RAM这听起来很可疑,因为它不适用于MIG和16MB的相变非易失性存储器。
Numato Galatea
带有x1 PCIe接口的PCI Express卡。还提供256MB DDR3,2x GTP接口(SATA连接器),micro SD,带有差分对和高速连接器的112个I/O接口,以及用于时钟或数字输入的两个SMA连接器。100baseT以太网可与扩展板一起使用。
250E / 500E
两个模块包括大量数字I/O(80 - 116),配置存储器和四个LED。需要外部JTAG编程器。
miniSpartan3
50A / 200A
具有HDMI端口,41个数字I/O接口,4通道,8位200 KSPS ADC,SPI闪存,32 MHz振荡器,3个LED,2个DIP开关,USB串行和板载USB JTAG的模块。
Papilio One
250E / 500E
48个I/O接口,USB编程器和串行通信。Eagle板文件可免费获得。它使用自定义比特流上传工具,但它是开源和跨平台的。
OHO-Elektronik
250E / 500E
一系列带有SPI闪存的面包板DIL模块,9个LED,两个开关和5V电压转换器。
一块带有1Mbit SPI闪存的小板(其中一半可用于数据存储),8个LED,四个SPST开关,8个DIP开关和26个可访问I/O接口. 板载PIC18F支持使用仅限Windows的配置实用程序通过USB对闪存进行编程。
Open Workbench Logic Sniffer
借用Papilio One的设计,提供16个5V耐受缓冲输入,并兼容Papilio One的“翼”扩展板。
Arduino屏蔽旨在成为Arduino应用的音频和视频协处理器,但可以作为具有Arduino外形的通用FPGA接口板重新用作。VGA和音频输出,带SPI闪存。
带USB的非常小的PCB,PIC18F,8 MB SDRAM,2 Mb闪存和用户IO接头。它可以用作插件模块,或者因为设计是开源的(带有Eagle文件),作为自定义板的基础(只要它也是开源的)。
MicroNova Mercury
面包板友好的64针DIP模块外形。它提供30个5V耐压I/O接口,9个其他I/O接口,一个8通道,200 Ksps ADC,4个LED,一个开关,4 Mb SRAM,以及通过USB编程(带有Windows编程应用程序)。JTAG接口引脚也被分解。
100E / 500E
并行端口编程电缆,RS232,三个LED和32个I/O接口.
Digilent Basys 2
$89,学生价$69
四个PMOD连接器,PS / 2,VGA,8个开关,4个按钮,8个LED,4个7段显示器和板载USB编程器。
Digilent Nexys 2
8个LED和8个SPDT开关,4个瞬时开关和7个段显示器。主扩展连接器略显不同(尽管它并不昂贵且可从Digilent分销商处获得)但适用于高速设计。
并行端口编程电缆,40个5V容限I/O接口,13个LED,256 Mbit SDRAM,用于编程和用户应用的128 Mbit串行闪存,16x2 LCD,USB串行和2个按钮,以及旋转开关。
Aessent aes220
200AN / 400AN
一个小型可堆叠模块,具有赛普拉斯FX2LP USB控制器,128Mb SDRAM,16 KB EEPROM,72个GPIO,5个LED,2个开关,以及通过USB或外部电源供电。
PLDkit为旧的赛灵思器件提供了许多低成本电路板—CPLD,Spartan 3和Virtex 4和5。
TinyFPGA A1/A2
XO2-256/1200
21个I/O,JTAG编程 (USB-JTAG编程器价格$9) 。可插面包板,开源设计。
Gnarly Grey UPDuino v1/v2
$9.95/$15.99
用于堆叠在Arduino Nano或者Arduino Pro Mini上。34个I/O,一个RGBLED。v2板包含一个USB编程器。原理图和布局是开源的,另外还有摄像头和LCD显示器接口板。
Bugblat pif
树莓派的一个附加板,提供17个外部I/O (除了用来和树莓派通信的那些) ,两个LED,以及编程电路。
Bugblat tif
硬币大小,可接面包板,有10个I/O,USB编程 (带有不需要驱动的跨平台开源软件) ,电源端口,两个LED。
MachXO2 Breakout Board
MachXO2-7000ZE
49个I/O。本来是用作I/O扩展器、I/O矩阵,或PWM发生器的,但也可以用作独立的FPGA板。
MachXO3-4300E
8个LED,108个I/O,原型区,JTAG连接器,板载USB编程器。
TinyFPGA B2
23个I/O,一个内置USB编程器,可插面标板,开源设计。
iCEblink40-HX1K Evaluation Kit
iCEblink40-HX1K
USB编程器,4个LED,4个电容式触摸按键,配置PROM,68个数字I/O (0.1英寸接头) ,按说也会兼容PMOD和Arduino扩展板。
LatticeXP2 Brevia
2 Mbit闪存,I Mbit SRAM,USB编程器,2x20扩展接头,2x5扩展接头,按键,4位DIP开关,8个LED。
Nandland Go Board
ICE40 HX1K
4个LED,两个SPST按键,两个7段LED显示器,用于编程的Micro USB,还有USB-UART,VGA,以及PMOD I/O连接器。
MachXO2-7000HE
256 MBit SDRAM,512 Kbit SRAM,USB2主机,USB串口,VGA/复合视频输出,立体声音频,SD卡槽,PS/2键盘或鼠标接口,18个GPIO,两个按键,4个LED,以及内置的USB JTAG。
现正接受预定。提供一个FPGA,拥有Lattic合成工具链,和200个I/O (通过PMOD和FFC连接器) 。用来连树莓派2B/B+的。
LatticeECP3 Versa
有时候也会降到$99,虽然比较罕见。不过,它依然算是比较便宜的PCIe (x1) 开发板了,64 Mbit闪存,1 GbitDDR3,4个SMA连接器 (一个全双工SERDES通道),双千兆以太网,扩展连接器,14段字母数字显示,一些开关,一些LED,还有USB编程器。软件可能年年需要注册。
Actel/Microsemi
Microsemi SmartFusion2 SoC FPGA KickStartDevelopment Kit
M2S010S FPGA
包含一个集成的166 MHz ARM Cortex-M3。这个板子上,有一个BLE4.1模块,有光线、运动、温度传感器,有4个LED,3个PMOD,Arduino shield支持,还有4个RGBLED,两个按键,以及USB串口/编程。
SmartFusion System-On-Module
16 MB PSRAM,16 MB闪存,以太网。还有入门套件。
SmartFusion2 System-On-Module
M2S005-M2S090
这个模块里,有一个集成的166 MHz 32位ARM Cortex-3。板载64 MBRAM,16MB闪存还有10/100的以太网。另外,入门套件售价$179,包含一个FPGA模块,USB/以太网连接器,以及一个原型设计区。
Actel IGLOO nano Starter Kit
板载开关和一些LED,USB串口,USB变成适配器,还有一些低速I/O。
Microsemi SmartFusion Evaluation Kit
集成的1000 MHz ARM Cortex-M3,10/100以太网PHY,还有片上MAC,USB串口,板载USB编程接口,OLED显示,8个LED,两个用户开关,还有一些模拟量输出,和数字输出。对混合FPGA/微控制器应用的开发者来说,可能是个很好玩的板子。
Altera Cyclone V
Arrow BEMICRO CV
两个振荡器,1 Gbit DDR,串行EEPROM,microSD卡槽,8个LED,3个DIP开关,两个按键,80个PIO,以及板载USB编程器。
DE0-Nano-SoC
$99, $90 academic
集成双核ARM Cortex-A9,内有1GB DDR3,micro SD,USBOTG,USB-UART,USB编程器,千兆以太网,60-80个数字I / O,Arduino Shield兼容,ADC,3个按钮,4个开关,9个LED,还有加速度计。
Arrow BEMICRO CV A9
两个振荡器,1 Gbit DDR,串行EEPROM,microSD插槽,8个LED,4个DIP开关,2个按健,80个GPIO,以及板载USB编程器。
Cyclone V GX Starter Kit
包含4 Gb DDR RAM,4 Mb SRAM,高速夹层连接器 (带有4个3.125 Gbp收发器),40个GPIO,带模拟输入的Arduino兼容接头,配置闪存,USB编程器,HDMI输出,音频,18个LED,10个滑动开关,4个去抖按键,CPU重置按键,4个七段显示器,microSD插口,以及USB UART。
$199, $150 academic
包含集成双核ARM Cortex-A9。 64 MB SDRAM,1GB DDR3,micro SD,双USB2.0主机,千兆以太网,PS/2,IR发射器和接收器,大约80个数字I/O,8个12位1MSPSADC输入,VGA,音频编解码器,模拟电视视频输入,四个按键,10个开关,11个LED,6个7段显示器,加速度计,USB串口,以及USB编程器。
Altera Cyclone IV
$59 academic
16 Mbit闪存,USB编程器,3轴加速度计,8通道12位ADC,3个扩展接头上的106个引脚,32 MB SDRAM,2 KbEEPROM,8个LED,4个DIP开关和两个按键。
Arrow BeMicro SDK
新版BeMicro有了microSD,10/100以太网,温度传感器,512Mb移动DDR,集成编程器,8个LED,一些按键和开关,以及80针边缘连接器。 配套的protoboard卖30美元。
devboards DB_START_4CE10
16Mb SDRAM,19个I/O,5个输入引脚,6个LED,2个按键,以及内置USB编程器。
Ordb2a-ep4ce22
专为OpenRISC处理器设计的开发板,带有一个Linux端口。 包含32 MB SDRAM,1 MBSPI闪存,SDIO连接器,快速以太网,USB OTG,USB串口/JTAG,以及拥有62个GPIO的扩展连接器。另外,还有SO-DIMM板,内含ARM处理器和以太网交换机的。
devboards DB4CGX15
EP4CGX15BF
是个价格合理的PCIe开发板,有32Mb SDRAM,20个I/O,4个输入引脚,2个LED,高速收发器I/O (通过MMCX连接器),以及内置USB编程器。
Altera Cyclone III
Terasic Altera DE0
$119, 学生价$89
8 MB SDRAM,4 MB闪存,SD卡槽,USB编程器,3个按键,10个开关,10个LED,4个七段显示器,16x2LCD接口,VGA输出,RS232和PS/2接口,以及72个I/O。
Max 10,Cyclone II,及其他
10M08/10M16
这块小板子,有9个模拟输入(一些共用引脚),15个I / O,2个按键,8个LED,8MBSDRAM,8MB闪存,3轴加速度计,以及板载USB编程器。已由JV::Store审核 。
Wayengineer
Cyclone II
来自深圳的一系列廉价电路板,大部分都有RAM和各种I/O,包括LCD,7段LED,VGA,开关等。如果,你已经有些FPGA的经验,原理图读起来很顺畅,不需要任何供应商支持的话,买这些就好了。
EP2C20 core board
同样来自中国,几乎没有在线文档可读。不过看起来很好用,有一个比较大的Cyclone II部件 (18752 LE) ,256 Mb SDRAM,2Mb SRAM,一条USB编程电缆,四个开关,I/O超过30个。
这是一个Arduino Shield,有74个数字I/O,两个PMOD接口,一个JTAG端口,一个按键和一个LED。设计经CC许可。
这是另一款MAX 10板,采用Arduino外形。 它有5V I/O,一些ADC,HDMI和VGA输出,四个LED,以及micro SD。还有一个扩展盾,带有额外的7段LED,RGB LED和按键。
Arrow DECA
10M50DAF484C6G
它有50000个LE,512MB DDR3SDRAM,加速度计,9个ADC,两个带SMA输入,温度、湿度、光和电容传感器,8个LED,2个按键,2个滑动开关,10/100以太网,micro SD,64MB QSPI闪存,HDMI输出,音频I/O,MIPI CSI-2摄像头输入,92个数字I/O (BeagleBone cape pinout),以及板载USB编程器。
Alorium XLR8
Arduino Uno的直接替代产品。它带有(可从Arduino草图中使用的)FPGA加速的硬件组件,有5VI/O,有Arduino兼容的ADC,以及USB串口。
FTDI Morph-IC-II
Cyclone II
包含FT2232H USB接口芯片,提供高速数据传输。 有80到96个I/O (FPGA和FT2232H不同),间距为0.1英寸。
严格意义上讲,并不是FPGA,是一些可重构的混合信号设备,也很有趣。
CY8CKIT-059
把ARM Cortex-M3集成在,与面包板兼容的板子上,配有快速编程器/调试工具 (Debugger) 。
CY8CKIT-043
PSoC 4200M
包含ARM Cortex-M0,可插面包板,可插蓝牙低功耗模块,拥有快速编程器/调试工具。
PSoC 4 Pioneer
Cypress自己提供的廉价开发板。 集成了Cortex-M0,支持Arduino Shield和DigilentPMod,拥有CapSense滑块,RGB LED和一个按键。
还有什么?
其实,FPGA-FAQ里,还有一个非常详尽的列表,包含了更多省钱之选。
大家可以前往传送门:
http://www.fpga-faq.com/FPGA_Boards.shtml
本表链接:
https://joelw.id.au/FPGA/CheapFPGADevelopmentBoards
量子位AI社群18群开始招募啦,欢迎对AI感兴趣的同学,加小助手微信qbitbot9入群;
进群请加小助手微信号qbitbot7,并务必备注相应群的关键词~通过审核后我们将邀请进群。(专业群审核较严,敬请谅解)
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
今日搜狐热点下面是网上看到的一些关于内存和CPU方面的一些很不错的文章. 整理如下:
转: CPU的等待有多久?
原文标题:
原文地址:
注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 本文以一个现代的、实际的个人电脑为对象,分析其中()以及各类子系统的运行速度——延迟和数据吞吐量。通过粗略的估算各个组件的相对运行速度,希望能给大家留下一个比较直观的印象。本文中的数据来自实际应用,而非理论最大值。时间的单位是纳秒(,十亿分之一秒),毫秒(,千分之一秒),和秒()。吞吐量的单位是兆字节()和千兆字节()。让我们先从和内存开始,下图是北桥部分: &&
&& 第一个令人惊叹的事实是:快得离谱。在上,大部分简单指令的执行只需要一个时钟周期,也就是纳秒。即使是真空中传播的光,在这段时间内也只能走厘米(约英寸)。把上述事实记在心中是有好处的。当你要对程序做优化的时候就会想到,执行指令的开销对于当今的而言是多么的微不足道。 &&
当运转起来以后,它便会通过和对系统中的主存进行读写访问。使用的是静态存储器)。相对于系统主存中使用的动态存储器(),读写速度快得多、造价也高昂得多。一般被放置在芯片的内部,加之使用昂贵高速的存储器,使其给带来的延迟非常低。在指令层次上的优化(),其效果是与优化后代码的大小息息相关。由于使用了高速缓存技术(),那些能够整体放入中的代码,和那些在运行时需要不断调入调出()的代码,在性能上会产生非常明显的差异。
&& 正常情况下,当操作一块内存区域时,其中的信息要么已经保存在,要么就需要将之从系统主存中调入,然后再处理。如果是后一种情况,我们就碰到了第一个瓶颈,一个大约个时钟周期的延迟。在此期间如果没有其他事情要做,则往往是处在停机状态的()。为了给大家一个直观的印象,我们把的一个时钟周期看作一秒。那么,从读取信息就好像是拿起桌上的一张草稿纸(秒);从读取信息则是从身边的书架上取出一本书(秒);而从主存中读取信息则相当于走到办公楼下去买个零食(分钟)。
&& 主存操作的准确延迟是不固定的,与具体的应用以及其他许多因素有关。比如,它依赖于列选通延迟以及内存条的型号,它还依赖于指令预取的成功率。指令预取可以根据当前执行的代码来猜测主存中哪些部分即将被使用,从而提前将这些信息载入。
&& 看看的性能,再对比主存,就会发现:配置更大的或者编写能更好的利用的应用程序,会使系统的性能得到多么显著的提高。如果想进一步了解有关内存的诸多信息,读者可以参阅所写的一篇经典文章《》。
&& 人们通常把与内存之间的瓶颈叫做()。当今系统的前端总线带宽约为,看起来很令人满意。在这个速度下,你可以在秒内从内存中读取的信息,或者纳秒内读取字节。遗憾的是,这个吞吐量只是理论最大值(图中其他数据为实际值),而且是根本不可能达到的,因为主存控制电路会引入延迟。在做内存访问时,会遇到很多零散的等待周期。比如电平协议要求,在选通一行、选通一列、取到可靠的数据之前,需要有一定的信号稳定时间。由于主存中使用电容来存储信息,为了防止因自然放电而导致的信息丢失,就需要周期性的刷新它所存储的内容,这也带来额外的等待时间。某些连续的内存访问方式可能会比较高效,但仍然具有延时。而那些随机的内存访问则消耗更多时间。所以延迟是不可避免的。
图中下方的南桥连接了很多其他总线(如:)和外围设备:
令人沮丧的是,南桥管理了一些反应相当迟钝的设备,比如硬盘。就算是缓慢的系统主存,和硬盘相比也可谓速度如飞了。继续拿办公室做比喻,等待硬盘寻道的时间相当于离开办公大楼并开始长达一年零三个月的环球旅行。这就解释了为何电脑的大部分工作都受制于磁盘,以及为何数据库的性能在内存缓冲区被耗尽后会陡然下降。同时也解释了为何充足的(用于缓冲)和高速的磁盘驱动器对系统的整体性能如此重要。
虽然磁盘的"连续"存取速度确实可以在实际使用中达到,但这并非故事的全部。真正令人头疼的瓶颈在于寻道操作,也就是在磁盘表面移动读写磁头到正确的磁道上,然后再等待磁盘旋转到正确的位置上,以便读取指定扇区内的信息。(每分钟绕转次数)用来指示磁盘的旋转速度:越大,耽误在寻道上的时间就越少,所以越高的意味着越快的磁盘。这里有一篇由两个的研究生写的很酷的文章,其中讲述了寻道时间对系统性能的影响:《》
&& 当磁盘驱动器读取一个大的、连续存储的文件时会达到更高的持续读取速度,因为省去了寻道的时间。文件系统的碎片整理器就是用来把文件信息重组在连续的数据块中,通过尽可能减少寻道来提高数据吞吐量。然而,说到计算机实际使用时的感受,磁盘的连续存取速度就不那么重要了,反而应该关注驱动器在单位时间内可以完成的寻道和随机操作的次数。对此,固态硬盘可以成为一个很棒的选择。
&& 硬盘的也有助于改进性能。虽然的只能覆盖整个磁盘容量的,可别看只有这么一点大,其效果十分明显。它可以把一组零散的写入操作合成一个,也就是。同样的,为了提高效率,一系列读取操作也可以被重组,而且操作系统和驱动器固件都会参与到这类优化中来。
最后,图中还列出了网络和其他总线的实际数据吞吐量。火线仅供参考,芯片组并不直接支持火线。我们可以把看作是计算机之间的总线。去访问那些速度很快的网站(比如),延迟大约毫秒,与硬盘驱动器带来的延迟相当。事实上,尽管硬盘比内存慢了个数量级,它的速度与是在同一数量级上的。目前,一般家用网络的带宽还是要落后于硬盘连续读取速度的,但"网络就是计算机"这句话可谓名符其实。如果将来比硬盘还快了,那会是个什么景象呢?
我希望这些图片能对您有所帮助。当这些数字一起呈现在我面前时,真的很迷人,也让我看到了计算机技术发展到了哪一步。前文分开的两个图片只是为了叙述方便,我把包含南北桥的整张图片也贴出来,供您参考。
转: CPU如何操作内存
原文标题:
原文地址:
&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 在你试图理解一个复杂的系统时,如果能揭去表面的抽象并专注于最低级别的概念,往往会有不小的收获。在这个精神的指导下,让我们看看对于内存和端口操作来说最简单、最基础的概念,即与总线之间的接口。其中的细节是很多上层概念的基础,比如线程同步。当然了,既然我是个程序员,就暂且忽略那些只有电子工程师才会去关注的东西吧。下图是我们的老朋友,:
&& 处理器有个管脚,其中约半数仅仅用于供电而不参与数据传输。当你把这些管脚按照功能分类后,就会发现这个处理器的物理接口惊人的简单。本图展示了参与内存和端口操作的重要管脚:地址线,数据线,请求线。这些操作均发生在前端总线的事务上下文结构中。前端总线事务的执行包含五个阶段:仲裁,请求,侦听,响应,数据操作。在执行事务的过程中,前端总线上的各个部件扮演着不同的角色。这些部件称之为。通常,就是全部的处理器外加北桥。
本文只分析请求阶段。在此阶段中,发出请求的往往是一个处理器,它输出两个数据包。下图列出了第一个数据包中最为重要的位,这些数据位通过处理器的地址线和请求线输出:
&& 地址线输出指定了事务发生的物理内存起始地址。我们有条地址线,他们指定了数据包的第至第位,第至第位为。因此,实际上这条地址线构成了一个位的、以字节对齐的地址,正好覆盖的物理内存。这种设定从奔腾就开始了。请求线指定了事务的类型。当事务类型为请求时,地址线指出的是端口地址而不是内存地址。当第一个数据包被发送以后,同样由这组管脚,在下一个总线时钟周期发送第二个数据包:
&& 属性信号()很有趣,它反映了处理器所支持的种内存缓冲功能。把这些信息发布到前端总线后,发出请求的就可以让其他处理器知道如何根据当前事务处理他们自己的,以及让内存控制器(也就是北桥)知道该如何应对。一块指定内存区域的缓存类型由处理器通过查询页表()来决定,页表由内核维护。
&& 典型的情况是,内核把全部内存都视为"回写"类型(),从而获得最好的性能。在回写模式下,内存的最小访问单元为一个(),在中是字节。当程序想读取内存中的一个字节时,处理器会从读取包含此字节的整条缓存线的内容。当程序做写入内存操作时,处理器只是修改中的对应缓存线,而不会更新主存中的信息。之后,当真的需要更新主存时,处理器会把那个被修改了的缓存线整体放到总线上,一次性写入内存。所以大部分的请求事务,其数据长度字段都是(),对应字节。下图展示了当中没有对应数据时,内存读取访问的过程:
在计算机上,有些物理内存范围被而不是实际的存储器地址,比如硬盘和网卡。这使得驱动程序可以像读写内存那样,方便的与设备通信。内核会在页表中标记出这类内存映射区域为不可缓存的()。对不可缓存的内存区域的访问操作会被总线原封不动的按顺序执行,其操作与应用程序或驱动程序所发出的请求完全一致。因此,这时程序可以精确控制读写单个字节、字、或其它长度的信息。这都是通过设置第二个数据包中的字节使能掩码()来完成的。
前面讨论的这些基本知识还包含很多关联的内容。比如:
、如果应用程序想要尽可能高的运行速度,就应该把会被一起访问的数据尽量组织在同一条缓存线中。一旦这条缓存线被载入,之后的读取操作就会,不再需要额外的内存访问了。
、对于回写式内存访问,作用于一条缓存线的任何内存操作都一定是原子的()。这种能力是由处理器的提供的,所有数据被同时读写,中途不会被其他处理器或线程打断。特别的,位和位的内存操作,只要不跨越缓存线的边界,就都是原子操作。
、前端总线是被所有的所共享的。这些在开启一个事务之前,必须先进行总线使用权的仲裁。而且,每一个都需要侦听总线上所有的事务,以便维持的一致性。因此,随着部署更多的、多核的处理器到计算机,总线竞争问题会变得越来越严重。为解决这个问题,将处理器直接连接于内存,并以点对点的方式通信,取代之前的广播方式,从而减少总线竞争。
本文讲述的都是有关物理内存请求的重要内容。当涉及到内存锁定、多线程、缓存一致性的问题时,总线这个角色又将浮出水面。当我第一次看到前端总线数据包的描述时,会有种恍然大悟的感觉,所以我希望您也能从本文中获益。下一篇文章,我们将从底层爬回到上层去,研究一个抽象概念:虚拟内存。
[转]: 主板芯片组与内存映射
原文标题:
原文地址:
&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 我打算写一组讲述计算机内幕的文章,旨在揭示现代操作系统内核的工作原理。我希望这些文章能对电脑爱好者和程序员有所帮助,特别是对这类话题感兴趣但没有相关知识的人们。讨论的焦点是,,和处理器。钻研系统内幕是我的一个爱好。我曾经编写过不少内核模式的代码,只是最近一段时间不再写了。这第一篇文章讲述了现代主板的布局,如何访问内存,以及系统的内存映射。
&& 作为开始,让我们看看当今的计算机是如何连接各个组件的吧。下图展示了主板上的主要组件:
现代主板的示意图,北桥和南桥构成了芯片组。
&& 当你看图时,请牢记一个至关重要的事实:一点也不知道它连接了什么东西。仅仅通过一组与外界交互,它并不关心外界到底有什么。可能是一个电脑主板,但也可能是烤面包机,网络路由器,植入脑内的设备,或测试工作台。主要通过种方式与外界交互:内存地址空间,地址空间,还有中断。
眼下,我们只关心主板和内存。安装在主板上的与外界沟通的门户是前端总线(),前端总线把与北桥连接起来。每当需要读写内存时,都会使用这条总线。通过一部分管脚来传输想要读写的物理内存地址,同时另一些管脚用于发送将被写入或接收被读出的数据。一个有个针脚用于传输物理内存地址(可以表示个地址位置),个针脚用于接收发送数据(所以数据在位通道中传输,也就是字节的数据块)。这使得可以控制的物理内存(个地址乘以字节),尽管大多数的芯片组只能支持的。
现在到了最难理解的部分。我们可能曾经认为内存指的就是,被各式各样的程序读写着。的确,大部分发出的内存请求都被北桥转送给了管理器,但并非全部如此。物理内存地址还可能被用于主板上各种设备间的通信,这种通信方式叫做。这类设备包括显卡,大多数的卡(比如扫描仪或卡),以及中的存储器等。
&& 当北桥接收到一个物理内存访问请求时,它需要决定把这个请求转发到哪里:是发给?抑或是显卡?具体发给谁是由内存地址映射表来决定的。映射表知道每一个物理内存地址区域所对应的设备。绝大部分的地址被映射到了,其余地址由映射表来通知芯片组该由哪个设备来响应此地址的访问请求。这些被映射为设备的内存地址形成了一个经典的空洞,位于内存的到之间。当内存地址被保留用于显卡和设备时,就会形成更大的空洞。这就是为什么位的操作系统。中,这个文件简明的列举了这些空洞的地址范围。下图展示了低端物理内存地址形成的一个典型的内存映射:
系统中,低端内存地址空间的布局。
实际的地址和范围依赖于特定的主板和电脑中接入的设备,但是对于大多数系统,情形都跟上图非常接近。所有棕色的区域都被设备地址映射走了。记住,这些在主板总线上使用的都是物理地址。在内部(比如我们正在编写和运行的程序),使用的是逻辑地址,必须先由翻译成物理地址以后,才能发布到总线上去访问内存。
这个把逻辑地址翻译成物理地址的规则比较复杂,而且还依赖于当时的运行模式(实模式,位保护模式,位保护模式)。不管采用哪种翻译机制,的运行模式决定了有多少物理内存可以被访问。比如,当工作于位保护模式时,它只可以寻址物理地址空间(当然,也有个例外叫做,但暂且忽略这个技术吧)。由于顶部的大约物理地址被映射到了主板上的设备,实际能够使用的也就只有大约的(有时甚至更少,我曾用过一台安装了的电脑,它只有可用)。如果工作于,那么它将只能寻址的物理地址空间(这是早期的处理器所支持的唯一模式)。如果工作于位保护模式,则可以寻址的地址空间(虽然很少有芯片组支持这么大的)。处于位保护模式时,就有可能访问到空间中被主板上的设备映射走了的区域了(即访问空洞下的)。要达到这种效果,就需要使用比系统中所装载的地址区域更高的地址。这种技术叫做回收,而且还需要芯片组的配合。
这些关于内存的知识将为下一篇文章做好铺垫。下次我们会探讨机器的启动过程:从上电开始,直到准备跳转执行操作系统内核为止。如果你想更深入的学习这些东西,我强烈推荐手册。虽然我列出的都是第一手资料,但手册写得很好很准确。这是一些资料:
?《》描述了一个支持处理器的有代表性的芯片组。这也是本文的主要信息来源。
?《》是一个处理器数据手册。它记载了处理器上每一个管脚的作用(当你把管脚按功能分组后,其实并不算多)。很棒的资料,虽然对有些位的描述比较含糊。
?《》是杰出的文档。它优美的解释了体系结构的各个部分,一点也不会让人感到含糊不清。第一卷和第三卷部很值得一读(别被"卷"字吓倒,每卷都不长,而且您可以选择性的阅读)。
?Pádraig Brady建议我链接到的一篇。确实是个好东西。我本打算把这个链接放到讨论存储器的文章中的,但此处列出的越多越好啦。
转: 计算机的引导过程
原文标题:
原文地址:
&&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。&
&& 前一篇文章介绍了计算机的,从而为本文设定了一个系统引导阶段的场景。引导()是一个复杂的,充满技巧的,涉及多个阶段,又十分有趣的过程。下图列出了此过程的概要:
引导过程概要
当你按下计算机的电源键后(现在别按!),机器就开始运转了。一旦主板上电,它就会初始化自身的固件芯片组和其他零零碎碎的东西并尝试启动。如果此时出了什么问题(比如坏了或根本没装),那么很可能出现的情况是电脑没有任何动静,除了风扇在转。一些主板会在故障或缺失时发出鸣音提示,但以我的经验,此时大多数机器都会处于僵死状态。一些或其他设备也可能导致机器启动时僵死。对于那些以前工作正常,突然出现这种症状的电脑,一个可能的解决办法是拔除所有不必要的设备。你也可以一次只断开一个设备,从而发现哪个是罪魁祸首。
如果一切正常,就开始运行了。在一个多处理器或多核处理器的系统中,会有一个被动态的指派为引导处理器(简写),用于执行全部的和内核初始化代码。其余的处理器,此时被称为应用处理器(简写),一直保持停机状态直到内核明确激活他们为止。虽然经历了很多年的发展,但他们一直保持着完全的向后兼容性,所以现代的可以表现得跟原先年的完全一样。其实,当上电后,它就是这么做的。在这个基本的上电过程中,处理器工作于,功能是无效的。此时的系统环境,就像古老的一样,只有内存可以寻址,任何代码都可以读写任何地址的内存,这里没有保护或特权级的概念。
上电后,大部分寄存器的都具有定义良好的初始值,包括指令指针寄存器(),它记录了下一条即将被执行的指令所在的内存地址。尽管此时的还只能寻址的内存,但凭借一个奇特的技巧,一个隐藏的基地址(其实就是个偏移量)会与相加,其结果指向第一条将被执行的指令所处的地址(长字节,在内存空间的尾部,远高于)。这个特殊的地址叫做,而且是现代的标准。
主板保证在复位向量处的指令是一个跳转,而且是跳转到执行入口点所在的。这个跳转会顺带清除那个隐藏的、上电时的基地址。感谢芯片组提供的内存映射功能,此时的内存地址存放着初始化所需的真正内容。这些内容全部是从包含有的闪存映射过来的,而此时的模块还只有随机的垃圾数据。下面的图例列出了相关的内存区域:
引导时的重要内存区域
随后,开始执行的代码,初始化机器中的一些硬件。之后开始执行(),检测计算机中的各种组件。如果找不到一个可用的显卡,就会失败,导致进入停机状态并发出鸣音提示(因为此时无法在屏幕上输出提示信息)。如果显卡正常,那么电脑看起来就真的运转起来了:显示一个制造商定制的商标,开始内存自检,天使们大声的吹响号角。另有一些失败的情况,比如缺少键盘,会导致停机,屏幕上显示出错信息。其实即是检测又是初始化,还要枚举出所有设备的资源——中断,内存范围,端口。现代的会遵循()协议,创建一些用于描述设备的数据表,这些表格将来会被操作系统内核用到。
完毕后,就准备引导操作系统了,它必须存在于某个地方:硬盘,光驱,软盘等。搜索引导设备的实际顺序是用户可定制的。如果找不到合适的引导设备,会显示出错信息并停机,比如""没有系统盘或驱动器故障。一个坏了的硬盘可能导致此症状。幸运的是,在这篇文章中,成功的找到了一个可以正常引导的驱动器。
现在,会读取硬盘的第一个(扇区),内含个字节。这些数据叫做(简称)。一般说来,它包含两个极其重要的部分:一个是位于开头的操作系统相关的引导程序,另一个是紧跟其后的磁盘分区表。丝毫不关心这些事情:它只是简单的加载的内容到内存地址处,并跳转到此处开始执行,不管里的代码是什么。
主引导记录
这段在内的特殊代码可能是引导装载程序,引导装载程序(比如或),甚至可能是病毒。与此不同,分区表则是标准化的:它是一个字节的区块,包含个字节的记录项,描述磁盘是如何被分割的(所以你可以在一个磁盘上安装多个操作系统或拥有多个独立的卷)。传统上,的代码会查看分区表,找到一个(唯一的)标记为活动()的分区,加载那个分区的引导扇区(),并执行其中的代码。引导扇区是一个分区的第一个扇区,而不是整个磁盘的第一个扇区。如果此时出了什么问题,你可能会收到如下错误信息:""无效分区表或""操作系统缺失。这条信息不是来自的,而是由从磁盘加载的程序所给出的。因此这些信息依赖于的内容。
随着时间的推移,引导装载过程已经发展得越来越复杂,越来越灵活。的引导装载程序和可以处理很多种类的操作系统,文件系统,以及引导配置信息。他们的代码不再需要效仿上述"从活动分区来引导"的方法。但是从功能上讲,这个过程大致如下:
、MBR本身包含有第一阶段的引导装载程序。称之为阶段一。
、由于很小,其中的代码仅仅用于从磁盘加载另一个含有额外的引导代码的扇区。此扇区可能是某个分区的引导扇区,但也可能是一个被硬编码到中的扇区位置。
、MBR配合第步所加载的代码去读取一个文件,其中包含了下一阶段所需的引导程序。这在中是"阶段二"引导程序,在中是。如果第步失败了,在中你会收到错误信息,比如""缺失。阶段二的代码进一步读取一个引导配置文件(比如在中是,在中是)。之后要么给用户显示一些引导选项,要么直接去引导系统。
、此时,引导装载程序需要启动操作系统核心。它必须拥有足够的关于文件系统的信息,以便从引导分区中读取内核。在中,这意味着读取一个名字类似""的含有内核镜像的文件,将之加载到内存并跳转去执行内核引导代码。在中,一部份内核启动代码是与内核镜像本身分离的,事实上是嵌入到了当中。在完成一些初始化工作以后,从""文件加载内核镜像,就像所做的那样,跳转到内核的入口点去执行。
这里还有一个复杂的地方值得一提(这也是我说引导富于技巧性的原因)。当前内核的镜像就算被压缩了,在实模式下,也没法塞进的可用里。我的内核压缩后有。然而,引导装载程序必须运行于实模式,以便调用代码去读取磁盘,所以此时内核肯定是没法用的。解决之道是使用一种倍受推崇的""。它并非一个真正的处理器运行模式(希望的工程师允许我以此作乐),而是一个特殊技巧。程序不断的在实模式和保护模式之间切换,以便访问高于的内存同时还能使用。如果你阅读了的源代码,你就会发现这些切换到处都是(看看目录下的程序,对和函数的调用)。在这个棘手的过程结束时,装载程序终于千方百计的把整个内核都塞到内存里了,但在这后,处理器仍保持在实模式运行。
至此,我们来到了从"引导装载"跳转到"早期的内核初始化"的时刻,就像第一张图中所指示的那样。在系统做完热身运动后,内核会展开并让系统开始运转。下一篇文章将带大家一步步深入内核的初始化过程,读者还可以参考的资源。我没办法对也这么做,但我会把要点指出来。
转: 内核引导过程
原文标题:
原文地址:
&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 上一篇文章解释了计算机的,正好讲到引导装载程序把系统内核镜像塞进内存,准备跳转到内核入口点去执行的时刻。作为引导启动系列文章的最后一篇,就让我们深入内核,去看看操作系统是怎么启动的吧。由于我习惯以事实为依据讨论问题,所以文中会出现大量的链接引用内核版的源代码(源自)。如果你熟悉的语法,这些代码就会非常容易读懂;即使你忽略一些细节,仍能大致明白程序都干了些什么。最主要的障碍在于对一些代码的理解需要相关的背景知识,比如机器的底层特性或什么时候、为什么它会运行。我希望能尽量给读者提供一些背景知识。为了保持简洁,许多有趣的东西,比如中断和内存,文中只能点到为止了。在本文的最后列出了的引导过程的要点。
&& 当的引导程序运行到此刻时,处理器处于实模式(可以寻址的内存),(针对现代的系统)的内容大致如下:
引导装载完成后的内容
&& 引导装载程序通过的磁盘服务,已经把内核镜像加载到内存当中。这个镜像只是硬盘中内核文件(比如)的一份完全相同的拷贝。镜像分为两个部分:一个较小的部分,包含实模式的内核代码,被加载到内存边界以下;另一部分是一大块内核,运行在保护模式,被加载到低端内存地址以上。
&& 如上图所示,之后的事情发生在实模式内核的头部()。这段内存区域用于实现引导装载程序与内核之间的引导协议。此处的一些数据会被引导装载程序读取。这些数据包括一些令人愉快的信息,比如包含内核版本号的可读字符串,也包括一些关键信息,比如实模式内核代码的大小。引导装载程序还会向这个区域写入数据,比如用户选中的引导菜单项对应的命令行参数所在的内存地址。之后就到了跳转到内核入口点的时刻。下图显示了内核初始化代码的执行顺序,包括源代码的目录、文件和行号:
与体系结构相关的内核初始化过程
对于体系结构,内核启动前期会执行文件中的程序。它是用汇编语言书写的。一般说来汇编代码在内核中很少出现,但常见于引导代码。这个文件的开头实际上包含了引导扇区代码。早期的不需要引导装载程序就可以工作,这段代码是从那个时候留传下来的。现今,如果这个引导扇区被执行,它仅仅给用户输出一个""之后就会重启系统。现代的引导装载程序会忽略这段遗留代码。在引导扇区代码之后,我们会看到实模式内核头部()最开始的字节;这两部分合起来是字节,正好是硬件平台上一个典型的磁盘扇区的大小。
&& 在这字节之后,偏移量处,我们会发现内核的第一条指令,也就是实模式内核的入口点。具体的说,它在,是一个字节的跳转指令,直接写成了机器码的形式。你可以通过对内核镜像运行,并查看偏移量处的内容来验证这一点——这仅仅是一个对神志清醒程度的检查,以确保这一切并不是在做梦。引导装载程序运行完毕时就会跳转执行这个位置的指令,进而跳转到执行一个普通的用汇编写成的子程序,叫做。这个短小的子程序初始化栈空间(),把实模式内核的段清零(这个区域包含静态变量,所以用来初始化它们),之后跳转执行一段又老又好的语言程序:。
&& 会处理一些登记工作(比如检测内存布局),设置显示模式等。然后它会调用。然而,在把置于保护模式之前,还有一些工作必须完成。有两个主要问题:中断和内存。在实模式中,处理器的总是从内存的地址开始的,然而在保护模式中,这个中断向量表的位置是保存在一个叫的寄存器当中的。与此同时,从逻辑内存地址(在程序中使用)到线性内存地址(一个从连续编号到内存顶端的数值)的翻译方法在实模式和保护模式中是不同的。保护模式需要一个叫做的寄存器来存放内存的地址。所以调用了 和,用于装载临时的中断描述符表和全局描述符表。
现在我们可以转入保护模式啦,这是由另一段汇编子程序来完成的。这个子程序通过设定的寄存器的位来使能保护模式。此时,功能还处于关闭状态;分页是处理器的一个可选的功能,即使运行于保护模式也并非必要。真正重要的是,我们不再受制于的内存边界,现在可以寻址高达的了。这个子程序进而调用压缩状态内核的位内核入口点。会做一些简单的寄存器初始化工作,并调用一个语言编写的函数,用于实际的解压缩工作。
&& 会打印一条大家熟悉的信息""(正在解压缩)。解压缩过程是原地进行的,一旦完成内核镜像的解压缩,第一张图中所示的压缩内核镜像就会被覆盖掉。因此解压后的内核也是从位置开始的。之后,会显示""(完成)和令人振奋的""(正在引导内核)。这里""的意思是跳转到整个故事的最后一个入口点,也是保护模式内核的入口点,位于的第二个开始处(偏移量,此值是由芬兰山巅之上的神灵授意给的)。在这个神圣的位置含有一个子程序调用,名叫呃。但你会发现这一位是在另一个目录中的。
这位的第二个化身也是一个汇编子程序,但它包含了位模式的初始化过程:
、它清理了保护模式内核的段。(这回是真正的内核了,它会一直运行,直到机器重启或关机。)
、为内存建立最终的全局描述符表。
、建立页表以便可以开启分页功能。
、使能分页功能。
、初始化栈空间。
、创建最终的中断描述符表。
、最后,跳转执行一个体系结构无关的内核启动函数:。
下图显示了引导最后一步的代码执行流程:
与体系结构无关的内核初始化过程
&& 看起来更像典型的内核代码,几乎全用语言编写而且与特定机器无关。这个函数调用了一长串的函数,用来初始化各个内核子系统和数据结构,包括调度器(),内存分区(),计时器()等等。之后,调用,此时几乎所有的东西都可以工作了。会创建一个内核线程,并以另一个函数作为此线程的入口点。之后,会调用来激活任务调度功能,然后调用使自己进入睡眠()状态,成为内核中的一个空闲线程()。会在号进程()中永远的运行下去。一旦有什么事情可做,比如有了一个活动就绪的进程(),号进程就会激活去执行这个任务,直到没有活动就绪的进程后才返回。
&& 但是,还有一个小麻烦需要处理。我们跟随引导过程一路走下来,这个漫长的线程以一个空闲循环()作为结尾。处理器上电执行第一条跳转指令以后,一路运行,最终会到达此处。从复位向量()引导装载程序实模式内核保护模式内核,跳转跳转再跳转,经过所有这些杂七杂八的步骤,最后来到引导处理器()中的空闲循环。看起来真的很酷。然而,这并非故事的全部,否则计算机就不会工作。
&& 在这个时候,前面启动的那个内核线程已经准备就绪,可以取代号进程和它的空闲线程了。事实也是如此,就发生在开始运行的时刻(此函数之前被作为线程的入口点)。的职责是初始化系统中其余的,这些从引导过程开始到现在,还一直处于停机状态。之前我们看过的所有代码都是在一个单独的上运行的,它叫做引导处理器()。当其他——称作应用处理器()——启动以后,它们是处于实模式的,必须通过一些初始化步骤才能进入保护模式。大部分的代码过程都是相同的,你可以参考,但对于应用处理器,还是有些细微的不同。最终,会调用,后者会尝试启动一个用户模式()的进程,尝试的顺序为:,,,。如果都不行,内核就会报错。幸运的是经常就在这些地方的,于是号进程()就开始运行了。它会根据对应的配置文件来决定启动哪些进程,这可能包括,控制台登陆程序,网络后台程序等。从而结束了引导进程,同时另一个程序开始在某处运行。至此,让我祝福您的电脑可以一直正常运行下去,不出毛病。
&& 在同样的体系结构下,的启动过程与有很多相似之处。它也面临同样的问题,也必须完成类似的初始化过程。当引导过程开始后,一个最大的不同是,把全部的实模式内核代码以及一部分初始的保护模式代码都打包到了引导加载程序(:)当中。因此,使用的二进制镜像文件就不一样了,内核镜像中没有包含两个部分的代码。另外,把引导装载程序与内核完全分离,在某种程度上自动的形成不同的开源项目。下图显示了内核主要的启动过程:
内核初始化过程
&& 自然而然的,用户模式的启动就非常不同了。没有程序,而是运行和。会启动(它会启动所有的服务程序)、和本地安全认证子系统。经典的登陆对话框就是运行在的上下文中的。
&& 本文是引导启动系列话题的最后一篇。感谢每一位读者,感谢你们的反馈。我很抱歉,有些内容只能点到为止;我打算把它们留在其他文章中深入讨论,并尽量保持文章的长度适合的风格。下次我打算定期的撰写关于""的文章,就像本系列一样。最后,给大家一些参考资料:
?最好也最重要的资料是实际的内核代码,或的都成。
?Intel出版的杰出的,你可以免费下载到。
?《内核》是本好书,其中讨论了大量的内核代码。这书也许有点过时有点枯燥,但我还是将它推荐给那些想要与内核心意相通的人们。《设备驱动程序》读起来会有趣得多,讲的也不错,但是涉及的内容有些局限性。最后,网友推荐所写的《内核开发》,我曾听过一些对此书的正面评价,所以还是值得列出来的。
?对于,目前最好的参考书是《》,作者是和,后者是的知名专家。这是本特棒的书,写的很好而且讲解全面。主要的缺点是缺少源代码的支持。
转: 内存地址转换与分段
原文标题:
原文地址:
&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 本文是兼容计算机()的内存与保护系列文章的第一篇,延续了系列文章的主题,进一步分析操作系统内核的工作流程。与以前一样,我将引用内核的源代码,但对只给出示例(抱歉,我忽略了,等系统,但大部分的讨论对它们一样适用)。文中如果有错误,请不吝赐教。
在支持的上,对内存的访问是通过连接着和北桥芯片的前端总线来完成的。在前端总线上传输的内存地址都是物理内存地址,编号从开始一直到可用物理内存的最高端。这些数字被北桥映射到实际的内存条上。物理地址是明确的、最终用在总线上的编号,不必转换,不必分页,也没有特权级检查。然而,在内部,程序所使用的是逻辑内存地址,它必须被转换成物理地址后,才能用于实际内存访问。从概念上讲,地址转换的过程如下图所示:
开启分页功能后的内存地址转换过程
此图并未指出详实的转换方式,它仅仅描述了在的分页功能开启的情况下内存地址的转换过程。如果关闭了分页功能,或运行于位实模式,那么从分段单元()输出的就是最终的物理地址了。当要执行一条引用了内存地址的指令时,转换过程就开始了。第一步是把逻辑地址转换成线性地址。但是,为什么不跳过这一步,而让软件直接使用线性地址(或物理地址呢?)其理由与:"人类为何要长有阑尾?它的主要作用仅仅是被感染发炎而已"大致相同。这是进化过程中产生的奇特构造。要真正理解分段功能的设计,我们就必须回溯到年。
最初的处理器的寄存器是位的,其指令集大多使用位或位的操作数。这使得代码可以控制个字节(或)的内存。然而的工程师们想要让可以使用更多的内存,而又不用扩展寄存器和指令的位宽。于是他们引入了段寄存器(),用来告诉一条程序指令将操作哪一个的内存区块。一个合理的解决方案是:你先加载段寄存器,相当于说"这儿!我打算操作开始于处的内存区块";之后,再用位的内存地址来表示相对于那个内存区块(或段)的偏移量。总共有个段寄存器:一个用于栈(),一个用于程序代码(),两个用于数据(,)。在那个年代,大部分程序的栈、代码、数据都可以塞进对应的段中,每段长,所以分段功能经常是透明的。
&& 现今,分段功能依然存在,一直被处理器所使用着。每一条会访问内存的指令都隐式的使用了段寄存器。比如,一条跳转指令会用到代码段寄存器(),一条压栈指令()会使用到堆栈段寄存器()。在大部分情况下你可以使用指令明确的改写段寄存器的值。段寄存器存储了一个位的段选择符();它们可以经由机器指令(比如)被直接加载。唯一的例外是代码段寄存器(),它只能被影响程序执行顺序的指令所改变,比如或指令。虽然分段功能一直是开启的,但其在实模式与保护模式下的运作方式并不相同的。
&& 在实模式下,比如在,段选择符是一个位的数值,指示出一个段的开始处的物理内存地址。这个数值必须被以某种方式放大,否则它也会受限于当中,分段就没有意义了。比如,可能会把这个段选择符当作物理内存地址的高位(只需将之左移位,也就是乘以)。这个简单的规则使得:可以按的段为单位,一块块的将的内存都寻址到。遗憾的是,做了一个很诡异的设计,让段选择符仅仅乘以(或),一举将寻址范围限制在了,还引入了过度复杂的转换过程。下述图例显示了一条跳转指令,的值是:
实模式分段功能
实模式的段地址以个字节为步长,从开始编号一直到(即)。你可以将一个从到的位偏移量(逻辑地址)加在段地址上。在这个下,对于同一个内存地址,会有多个段地址偏移量的组合与之对应,而且物理地址可以超过的边界,只要你的段地址足够高(参见臭名昭著的线)。同样的,在实模式的语言代码中,一个()既包含了段选择符又包含了逻辑地址,用于寻址的内存范围。真够"远"的啊。随着程序变得越来越大,超出了的段,分段功能以及它古怪的处理方式,使得平台的软件开发变得非常复杂。这种设定可能听起来有些诡异,但它却把当时的程序员推进了令人崩溃的深渊。
在位保护模式下,段选择符不再是一个单纯的数值,取而代之的是一个索引编号,用于引用段描述符表中的表项。这个表为一个简单的数组,元素长度为字节,每个元素描述一个段。看起来如下:
有三种类型的段:代码,数据,系统。为了简洁明了,只有描述符的共有特征被绘制出来。基地址()是一个位的线性地址,指向段的开始;段界限()指出这个段有多大。将基地址加到逻辑地址上就形成了线性地址。是描述符的特权级(),其值从(最高特权,内核模式)到(最低特权,用户模式),用于控制对段的访问。
这些段描述符被保存在两个表中:全局描述符表()和局部描述符表()。电脑中的每一个(或一个处理核心)都含有一个叫做的寄存器,用于保存的首个字节所在的线性内存地址。为了选出一个段,你必须向段寄存器加载符合以下格式的段选择符:
对,位为;对,位为;指出想要表中哪一个段描述符(译注:原文是段选择符,应该是笔误)。对于,请求特权级(),以后我们还会详细讨论。现在,需要好好想想了。当运行于位模式时,不管怎样,寄存器和指令都可以寻址整个线性地址空间,所以根本就不需要再去使用基地址或其他什么鬼东西。那为什么不干脆将基地址设成,好让逻辑地址与线性地址一致呢?的文档将之称为"扁平模型"(),而且在现代的系统内核中就是这么做的(特别指出,它们使用的是基本扁平模型)。基本扁平模型()等价于在转换地址时关闭了分段功能。如此一来多么美好啊。就让我们来看看位保护模式下执行一个跳转指令的例子,其中的数值来自一个实际的用户模式应用程序:
保护模式的分段
段描述符的内容一旦被访问,就会被(缓存),所以在随后的访问中,就不再需要去实际读取了,否则会有损性能。每个段寄存器都有一个隐藏部分用于缓存段选择符所对应的那个段描述符。如果你想了解更多细节,包括关于的更多信息,请参阅《》卷的第三章。和卷讲述了每一个指令,同时也指明了寻址时所使用的各种类型的操作数:位,位加段描述符(可被用于实现远指针),位,等等。
在上,只有个段描述符在引导启动过程被使用。他们使用宏来定义并存储在数组中。其中两个段是扁平的,可对整个位空间寻址:一个是代码段,加载到中,一个是数据段,加载到其他段寄存器中。第三个段是系统段,称为任务状态段()。在完成引导启动以后,每一个都拥有一份属于自己的。其中大部分内容是相同的,只有少数表项依赖于正在运行的进程。你可以从看到的布局以及其。这里有个主要的表项:个是扁平的,用于内核模式的代码和数据,另两个用于用户模式。在看这个时,请留意那些用于确保数据与缓存线对齐的填充字节——目的是克服。最后要说说,那个经典的错误信息""(分段错误)并不是由风格的段所引起的,而是由于分页单元检测到了非法的内存地址。唉呀,下次再讨论这个话题吧。
巧妙的绕过了他们原先设计的那个拼拼凑凑的分段方法,而是提供了一种富于弹性的方式来让我们选择是使用段还是使用扁平模型。由于很容易将逻辑地址与线性地址合二为一,于是这成为了标准,比如现在在位模式中就强制使用扁平的线性地址空间了。但是即使是在扁平模型中,段对于的保护机制也十分重要。保护机制用于抵御用户模式进程对系统内核的非法内存访问,或各个进程之间的非法内存访问,否则系统将会进入一个狗咬狗的世界!在下一篇文章中,我们将窥视保护级别以及如何用段来实现这些保护功能。
转: CPU的运行环, 特权级与保护
原文标题:
原文地址:
&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 可能你凭借直觉就知道应用程序的功能受到了计算机的某种限制,有些特定的任务只有操作系统的代码才可以完成,但是你知道这到底是怎么一回事吗?在这篇文章里,我们会接触到的特权级(),看看操作系统和是怎么一起合谋来限制用户模式的应用程序的。特权级总共有个,编号从(最高特权)到(最低特权)。有种主要的资源受到保护:内存,端口以及执行特殊机器指令的能力。在任一时刻,都是在一个特定的特权级下运行的,从而决定了代码可以做什么,不可以做什么。这些特权级经常被描述为保护环(),最内的环对应于最高特权。即使是最新的内核也只用到其中的个特权级:和。
&& 在诸多机器指令中,只有大约条指令被限制只能在执行(其余那么多指令的操作数都受到一定的限制)。这些指令如果被用户模式的程序所使用,就会颠覆保护机制或引起混乱,所以它们被保留给内核使用。如果企图在以外运行这些指令,就会导致一个一般保护错(),就像一个程序使用了非法的内存地址一样。类似的,对内存和端口的访问也受特权级的限制。但是,在我们分析保护机制之前,先让我们看看是怎么记录当前特权级的吧,这与前篇文章中提到的()有关。如下所示:
数据段和代码段的段选择符
数据段选择符的整个内容可由程序直接加载到各个段寄存器当中,比如(堆栈段寄存器)和(数据段寄存器)。这些内容里包含了请求特权级(,简称)字段,其含义过会儿再说。然而,代码段寄存器()就比较特别了。首先,它的内容不能由装载指令(如)直接设置,而只能被那些会改变程序执行顺序的指令(如)间接的设置。而且,不像那个可以被代码设置的字段,拥有一个由自己维护的当前特权级字段(,简称),这点对我们来说非常重要。这个代码段寄存器中的位宽的字段的值总是等于的当前特权级。的文档并未明确指出此事实,而且有时在线文档也对此含糊其辞,但这的确是个硬性规定。在任何时候,不管内部正在发生什么,只要看一眼中的,你就可以知道此刻的特权级了。
记住,特权级并不会对操作系统的用户造成什么影响,不管你是根用户,管理员,访客还是一般用户。所有的用户代码都在上执行,所有的内核代码都在上执行,跟是以哪个用户的身份执行无关。有时一些内核任务可以被放到用户模式中执行,比如上的用户模式驱动程序,但是它们只是替内核执行任务的特殊进程而已,而且往往可以被直接删除而不会引起严重后果。
由于限制了对内存和端口的访问,用户模式代码在不调用系统内核的情况下,几乎不能与外部世界交互。它不能打开文件,发送网络数据包,向屏幕打印信息或分配内存。用户模式进程的执行被严格限制在一个由之神所设定的沙盘之中。这就是为什么从设计上就决定了:一个进程所泄漏的内存会在进程结束后被统统回收,之前打开的文件也会被自动关闭。所有的控制着内存或打开的文件等的数据结构全都不能被用户代码直接使用;一旦进程结束了,这个沙盘就会被内核拆毁。这就是为什么我们的服务器只要硬件和内核不出毛病,就可以连续正常运行天,甚至一直运行下去。这也解释了为什么那么容易死机:这并非因为微软差劲,而是因为系统中的一些重要数据结构,出于兼容的目的被设计成可以由用户直接访问了。这在当时可能是一个很好的折中,当然代价也很大。
会在两个关键点上保护内存:当一个段选择符被加载时,以及,当通过线形地址访问一个内存页时。因此,保护也反映在的过程之中,既包括分段又包括分页。当一个数据段选择符被加载时,就会发生下述的检测过程:
的分段保护
因为越高的数值代表越低的特权,上图中的用于挑出和中特权最低的一个,并与描述符特权级(,简称)比较。如果的值大于等于它,那么这个访问就获得许可了。背后的设计思想是:允许内核代码加载特权较低的段。比如,你可以使用的段描述符来确保给定的操作所使用的段可以在用户模式中访问。但堆栈段寄存器是个例外,它要求,和这个值必须完全一致,才可以被加载。
事实上,段保护功能几乎没什么用,因为现代的内核使用扁平的地址空间。在那里,用户模式的段可以访问整个线形地址空间。真正有用的内存保护发生在分页单元中,即从线形地址转化为物理地址的时候。一个内存页就是由一个页表项()所描述的字节块。页表项包含两个与保护有关的字段:一个超级用户标志(),一个读写标志()。超级用户标志是内核所使用的重要的内存保护机制。当它开启时,内存页就不能被访问了。尽管读写标志对于实施特权控制并不像前者那么重要,但它依然十分有用。当一个进程被加载后,那些存储了二进制镜像(即代码)的内存页就被标记为只读了,从而可以捕获一些指针错误,比如程序企图通过此指针来写这些内存页。这个标志还被用于在调用创建子进程时,实现写时拷贝功能()。
&& 最后,我们需要一种方式来让切换它的特权级。如果的程序可以随意的将控制转移到(即跳转到)内核的任意位置,那么一个错误的跳转就会轻易的把操作系统毁掉了。但控制的转移是必须的。这项工作是通过门描述符()和指令来完成的。一个门描述符就是一个系统类型的段描述符,分为了个子类型:调用门描述符(),中断门描述符(),陷阱门描述符()和任务门描述符()。调用门提供了一个可以用于通常的和指令的内核入口点,但是由于调用门用得不多,我就忽略不提了。任务门也不怎么热门(在上,它们只在处理内核或硬件问题引起的双重故障时才被用到)。
&& 剩下两个有趣的:中断门和陷阱门,它们用来处理硬件中断(如键盘,计时器,磁盘)和异常(如缺页异常,除数异常)。我将不再区分中断和异常,在文中统一用"中断"一词表示。这些门描述符被存储在中断描述符表(,简称)当中。每一个中断都被赋予一个从到的编号,叫做中断向量。处理器把中断向量作为表项的索引,用来指出当中断发生时使用哪一个门描述符来处理中断。中断门和陷阱门几乎是一样的。下图给出了它们的格式。以及当中断发生时实施特权检查的过程。我在其中填入了一些内核的典型数值,以便让事情更加清晰具体。
伴随特权检查的中断描述符
&& 门中的和段选择符一起控制着访问,同时,段选择符结合偏移量()指出了中断处理代码的入口点。内核一般在门描述符中填入内核代码段的段选择符。一个中断永远不会将控制从高特权环转向低特权环。特权级必须要么保持不变(当内核自己被中断的时候),或被提升(当用户模式的代码被中断的时候)。无论哪一种情况,作为结果的必须等于目的代码段的。如果发生了改变,一个堆栈切换操作就会发生。如果中断是被程序中的指令所触发的(比如),还会增加一个额外的检查:门的必须具有与相同或更低的特权。这就防止了用户代码随意触发中断。如果这些检查失败,正如你所猜测的,会产生一个一般保护错()。所有的中断处理器都以特权退出。
&& 在初始化阶段,内核首先在中建立,并忽略全部中断。之后它使用的函数来填充普通的表项(参见)。在代码中,名字中包含""字样的门描述符是可以从用户模式中访问的,而且其设置函数使用。""是的陷阱门,也可以从用户模式访问。除此之外,术语名词都与本文对得上号。然而,硬件中断门并不是在这里设置的,而是由适当的驱动程序来完成。
&& 有三个门可以被用户模式访问:中断向量和分别用于调试和检查数值运算溢出。剩下的是一个系统门,被设置为。对于体系结构,它等于。它曾被作为一种机制,用于将进程的控制转移到内核,进行一个系统调用(),然后再跳转回来。在那个时代,我需要去申请""这个没用的牌照J。从奔腾开始,引入了指令,从此可以用这种更快捷的方式来启动系统调用了。它依赖于上的特殊目的寄存器,这些寄存器存储着代码段、入口点及内核系统调用处理器所需的其他零散信息。在执行后,不再进行特权检查,而是直接进入,并将新值加载到与代码和堆栈有关的寄存器当中(,,和)。只有的代码可以加载设置寄存器。
&& 最后,当需要跳转回时,内核发出一个或指令,分别用于从中断和系统调用中返回,从而离开并恢复的用户代码的执行。噢!提示我已经接近字了,所以端口的保护只能下次再谈了。这样我们就结束了的运行环与保护之旅。感谢您的耐心阅读。
转: Cache: 一个隐藏并保存数据的场所
原文标题:
原文地址:
&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 本文简要的展示了现代处理器的是如何组织的。有关的讨论往往缺乏具体的实例,使得一些简单的概念变得扑朔迷离。也许是我可爱的小脑瓜有点迟钝吧,但不管怎样,至少下面讲述了故事的前一半,即的是如何被访问的:
,路组相联,字节缓存线
&&&&&& 由索引拣选缓存组(行)
&& 在中的数据是以缓存线()为单位组织的,一条缓存线对应于内存中一个连续的字节块。这个使用了字节的缓存线。这些线被保存在中,也叫路()。每一路都有一个专门的目录()用来保存一些登记信息。你可以把每一路连同它的目录想象成电子表格中的一列,而表的一行构成了的一组()。列中的每一个单元()都含有一条缓存线,由与之对应的目录单元跟踪管理。图中的有组、每组路,因此有个含有缓存线的单元,合计的存储空间。
&& 在眼中,物理内存被分割成了许多大小的物理内存页()。每一页都含有条缓存线。在一个的页中,第到字节是第一条缓存线,第到字节是第二条缓存线,以此类推。每一页都重复着这种划分,所以第页第条缓存线与第页第条缓存线是不同的。
&& 在全相联缓存()中,内存中的任意一条缓存线都可以被存储到任意的缓存单元中。这种存储方式十分灵活,但也使得要访问它们时,检索缓存单元的工作变得复杂、昂贵。由于和工作在很强的约束之下,包括功耗,芯片物理空间,存取速度等,所以在多数情况下,使用全相联缓存并不是一个很好的折中。
&& 取而代之的是图中的组相联缓存()。意思是,内存中一条给定的缓存线只能被保存在一个特定的组(或行)中。所以,任意物理内存页的第条缓存线(页内第到字节)必须存储到第组,第条缓存线存储到第组,以此类推。每一组有个单元可用于存储它所关联的缓存线(译注:就是那些需要存储到这一组的缓存线),从而形成一个路关联的组()。当访问一个内存地址时,地址的第到位(译注:组索引)指出了在内存页中缓存线的编号,从而决定了即将使用的缓存组。举例来说,物理地址的组索引是,所以此地址的内容一定是在第组中缓存的。
&& 但是还有一个问题,就是要找出一组中哪个单元包含了想要的信息,如果有的话。这就到了缓存目录登场的时刻。每一个缓存线都被其对应的目录单元做了标记();这个标记就是一个简单的内存页编号,指出缓存线来自于哪一页。由于处理器可以寻址的物理,所以总共有个内存页,需要位来保存标记。前例中的物理地址对应的页号为。下面是故事的后一半:
在组中搜索匹配标记
&& 由于我们只需要去查看某一组中的路,所以查找匹配标记是非常迅速的;事实上,从电学角度讲,所有的标记是同时进行比对的,我用箭头来表示这一点。如果此时正好有一条具有匹配标签的有效缓存线,我们就获得一次缓存命中()。否则,这个请求就会被转发的,如果还没匹配上就再转发给主系统内存。通过应用各种调节尺寸和容量的技术,给配置了较大的,但其基本的设计都是相同的。比如,你可以将原先的缓存增加路而获得一个的缓存;再将组数增加到,每路可以存储。经过这两次修改,就得到了一个的。在此情况下,需要位来保存标记,位保存组索引;缓存所使用的物理内存页的大小与其一路的大小相等。(译注:有组,就需要位的组索引,缓存线依然是字节,所以一路有字节;在眼中,内存被分割为许多的块,所以需要位来保存标记。)
&& 如果有一组已经被放满了,那么在另一条缓存线被存储进来之前,已有的某一条则必须被腾空()。为了避免这种情况,对运算速度要求较高的程序就要尝试仔细组织它的数据,使得内存访问均匀的分布在已有的缓存线上。举例来说,假设程序中有一个数组,元素的大小是字节,其中一些对象在内存中相距。这些对象的各个字段都落在同一缓存线上,并竞争同一缓存组。如果程序频繁的访问一个给定的字段(比如,通过调用虚函数),那么这个组看起来就好像一直是被填满的,缓存开始变得毫无意义,因为缓存线一直在重复着腾空与重新载入的步骤。在我们的例子中,由于组数的限制,仅能保存个这类对象的虚函数表。这就是组相联策略的折中所付出的代价:即使在整体缓存的使用率并不高的情况下,由于组冲突,我们还是会遇到缓存缺失的情况。然而,鉴于计算机中各个存储层次的,不管怎么说,大部分的应用程序并不必为此而担心。
一个内存访问经常由一个线性(或虚拟)地址发起,所以需要依赖分页单元()来求出物理内存页的地址,以便用于缓存标记。与此相反,组索引来自于线性地址的低位,所以不需要转换就可以使用了(在我们的例子中为第到位)。因此是物理标记但虚拟索引的(),从而帮助进行并行的查找操作。因为的一路绝不会比的一页还大,所以可以保证一个给定的物理地址位置总是关联到同一组,即使组索引是虚拟的。在另一方面必须是物理标记和物理索引的,因为它的一路比的一页要大。但是,当一个请求到达时,物理地址已经被准备()完毕了,所以会工作得很好。
最后,目录单元还存储了对应缓存线的状态()。在代码缓存中的一条缓存线要么是无效的()要么是共享的(,意思是有效的,真的J)。在数据缓存和缓存中,一条缓存线可以为个状态之一:被修改的(),独占的(),共享的(),无效的()。缓存是包容式的():缓存的内容会被复制到缓存中。在下一篇讨论线程(),锁定()等内容的文章中,这些缓存线状态将发挥作用。下一次,我们将看看前端总线以及内存访问到底是怎么工作的。这将成为一个内存研讨周。
(在回复中提到了直接映射缓存()。它们基本上是一种特殊的组相联缓存,只是只有一路而已。在各种折中方案中,它与全相联缓存正好相反:访问非常快捷,但因组冲突而导致的缓存缺失也非常多。)
译者小结:
&&&&&&&& 内存层次结构的意义在于利用引用的空间局部性和时间局部性原理,将经常被访问的数据放到快速的存储器中,而将不经常访问的数据留在较慢的存储器中。
&&&&&&&& 一般情况下,除了寄存器和缓存可以操作指定字长的数据,下层的内存子系统就不会再使用这么小的单位了,而是直接移动数据块,比如以缓存线为单位访问数据。
&&&&&&&& 对于组冲突,可以这么理解:与上文相似,假设一个缓存,由条缓存线组成,每条线字节,容量。
&&&&&&&& 假如它是直接映射缓存,由于它往往使用地址的低位直接映射缓存线编号,所以所有的倍数的地址(,,等)都会映射到同一条线上(即第线)。假如程序的内存组织不当,交替的去访问布置在这些地址的数据,则会导致冲突。从外表看来就好像缓存只有条线了,尽管其他缓存线一直是空闲着的。
&&&&&&& 如果是全相联缓存,那么每条缓存线都是独立的,可以对应于内存中的任意缓存线。只有当所有的条缓存线都被占满后才会出现冲突。
&&&&&&& 组相联是前两者的折中,每一路中的缓存线采用直接映射方式,而在路与路之间,缓存控制器使用全相联映射算法,决定选择一组中的哪一条线。
&&&&&&& 如果是路组相联缓存,那么这条缓存线就被分为了路,每路条线,一路。此时所有为整数倍的地址(,,等)都会映射到第线,但由于路是关联的,所以可以同时有个这种地址的内容被缓存,不会发生冲突。当然了,如果要访问第三个这种地址,还是要先腾空已有的一条才行。所以极端情况下,从外表看来就好像缓存只有条线了,尽管其他缓存线一直是空闲着的。
&&&&&&&& 如果是路组相联缓存(与文中示例相同),那么这条缓存线就被分为了路,每路条线,一路。所以如果数组中元素地址是对齐的,并且程序交替的访问这些元素,就会出现组冲突。从外表看来就好像缓存只有条线了,尽管其他缓存线一直是空闲着的。
转: 剖析程序的内存布局
原文标题:
原文地址:
&& 注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。
&& 内存管理模块是操作系统的心脏;它对应用程序和系统管理非常重要。今后的几篇文章中,我将着眼于实际的内存问题,但也不避讳其中的技术内幕。由于不少概念是通用的,所以文中大部分例子取自位平台的和系统。本系列第一篇文章讲述应用程序的内存布局。
&& 在多任务操作系统中的每一个进程都运行在一个属于它自己的内存沙盘中。这个沙盘就是虚拟地址空间(),在位模式下它总是一个的内存地址块。这些虚拟地址通过页表()映射到物理内存,页表由操作系统维护并被处理器引用。每一个进程拥有一套属于它自己的页表,但是还有一个隐情。只要虚拟地址被使能,那么它就会作用于这台机器上运行的所有软件,包括内核本身。因此一部分虚拟地址必须保留给内核使用:
这并不意味着内核使用了那么多的物理内存,仅表示它可支配这么大的地址空间,可根据内核需要,将其映射到物理内存。内核空间在页表中拥有较高的(或以下),因此只要用户态的程序试图访问这些页,就会导致一个页错误()。在中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存。内核代码和数据总是可寻址的,随时准备处理中断和系统调用。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化:
蓝色区域表示映射到物理内存的虚拟地址,而白色区域表示未映射的部分。在上面的例子中,使用了相当多的虚拟地址空间,因为它是传说中的吃内存大户。地址空间中的各个条带对应于不同的内存段(),如:堆、栈之类的。记住,这些段只是简单的内存地址范围,与处理器的段没有关系。不管怎样,下面是一个进程的标准的内存段布局:
当计算机开心、安全、可爱、正常的运转时,几乎每一个进程的各个段的起始虚拟地址都与上图完全一致,这也给远程发掘程序安全漏洞打开了方便之门。一个发掘过程往往需要引用绝对内存地址:栈地址,库函数地址等。远程攻击者必须依赖地址空间布局的一致性,摸索着选择这些地址。如果让他们猜个正着,有人就会被整了。因此,地址空间的随机排布方式逐渐流行起来。通过对、、的起始地址加上随机的偏移量来打乱布局。不幸的是,位地址空间相当紧凑,给随机化所留下的空当不大,。
进程地址空间中最顶部的段是栈,大多数编程语言将之用于存储局部变量和函数参数。调用一个方法或函数会将一个新的栈桢()压入栈中。栈桢在函数返回时被清理。也许是因为数据严格的遵从的顺序,这个简单的设计意味着不必使用复杂的数据结构来追踪栈的内容,只需要一个简单的指针指向栈的顶端即可。因此压栈()和退栈()过程非常迅速、准确。另外,持续的重用栈空间有助于使活跃的栈内存保持在缓存中,从而加速访问。进程中的每一个线程都有属于自己的栈。
&& 通过不断向栈中压入的数据,超出其容量就有会耗尽栈所对应的内存区域。这将触发一个页故障(),并被的处理,它会调用来检查是否还有合适的地方用于栈的增长。如果栈的大小低于(通常是),那么一般情况下栈会被加长,程序继续愉快的运行,感觉不到发生了什么事情。这是一种将栈扩展至所需大小的常规机制。然而,如果达到了最大的栈空间大小,就会栈溢出(),程序收到一个段错误()。当映射了的栈区域扩展到所需的大小后,它就不会再收缩回去,即使栈不那么满了。这就好比联邦预算,它总是在增长的。
&& 动态栈增长是唯一一种访问未映射内存区域(图中白色区域)而被允许的情形。其它任何对未映射内存区域的访问都会触发页故障,从而导致段错误。一些被映射的区域是只读的,因此企图写这些区域也会导致段错误。
&& 在栈的下方,是我们的内存映射段。此处,内核将文件的内容直接映射到内存。任何应用程序都可以通过的系统调用()或的请求这种映射。内存映射是一种方便高效的文件方式,所以它被用于加载动态库。创建一个不对应于任何文件的匿名内存映射也是可能的,此方法用于存放程序的数据。在中,如果你通过请求一大块内存,运行库将会创建这样一个匿名映射而不是使用堆内存。'大块'意味着比还大,缺省是,可以通过调整。
&& 说到堆,它是接下来的一块地址空间。与栈一样,堆用于运行时内存分配;但不同点是,堆用于存储那些生存期与函数调用无关的数据。大部分语言都提供了堆管理功能。因此,满足内存请求就成了语言运行时库及内核共同的任务。在语言中,堆分配的接口是系列函数,而在具有垃圾收集功能的语言(如)中,此接口是关键字。
&& 如果堆中有足够的空间来满足内存请求,它就可以被语言运行时库处理而不需要内核参与。否则,堆会被扩大,通过系统调用()来分配请求所需的内存块。堆管理是很的,需要精细的算法,应付我们程序中杂乱的分配模式,优化速度和内存使用效率。处理一个堆请求所需的时间会大幅度的变动。实时系统通过来解决这个问题。堆也可能会变得零零碎碎,如下图所示:
最后,我们来看看最底部的内存段:,数据段,代码段。在语言中,和数据段保存的都是静态(全局)变量的内容。区别在于保存的是未被初始化的静态变量内容,它们的值不是直接在程序的源代码中设定的。内存区域是匿名的:它不映射到任何文件。如果你写,则的内容就会保存在中。
另一方面,数据段保存在源代码中已经初始化了的静态变量内容。这个内存区域不是匿名的。它映射了一部分的程序二进制镜像,也就是源代码中指定了初始值的静态变量。所以,如果你写,则的内容就保存在数据段中了,而且初始值为。尽管数据段映射了一个文件,但它是一个私有内存映射,这意味着更改此处的内存不会影响到被映射的文件。也必须如此,否则给全局变量赋值将会改动你硬盘上的二进制镜像,这是不可想象的。
下图中数据段的例子更加复杂,因为它用了一个指针。在此情况下,指针(字节内存地址)本身的值保存在数据段中。而它所指向的实际字符串则不在这里。这个字符串保存在代码段中,代码段是只读的,保存了你}

我要回帖

更多关于 电路板焊导线 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信