CPU为什么会有段?why segment/segmentation ?

本文探讨了CPU中段(segment)存在的历史原因,源于早期16位微计算机的内存限制。IBM PC在1981年引入了段地址系统,以解决1MB内存的寻址问题,而不需要超过16位的地址。通过使用复合地址,每个包含两个16位数,实现了超过64K的内存访问。这种设计允许通过不同的组合方式来指代同一内存位置,如763可以通过多种段偏移组合表示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CPU为什么会有段?像cs, ds, ss等等。这个问题困扰了我很久,感觉有了段地址后把问题复杂化了,没必要这么做。

后来翻查资料后才发现,段(segment)的存在原来是有历史因素的:http://homepage.smc.edu/morgan_david/cs40/segmentation.htm

以下是摘抄和个人见解

Vintage 1980 microcomputers used physical addressing, and confined themselves to using 4-digit hexadecimal numbers (which is the same thing as 16 bits) as addresses. The highest you can count with a 4-digit hexadecimal number is FFFF in hex, equivalent to 65535 in decimal. So no more than 65536 bytes or 64K of memory could be used. Even if you could have installed more, the computer could not have used it for lack of ability to refer to it.

1980年的程序是只有16bit大小的内存空间的,也就是只支持64k的内存,寻址只能从0000-FFFF,也就是 汇编 里的 

mov edx AFFFF

这样的指令和程序是不会被执行通过的。


The IBM PC appeared in 1981 and was a fundamental redesign of the earlier microcomputer generation. The designers wanted to allow for 1MB of memory, or 16 times as much as the previous 64K limit. However for design reasons they did not wish to use numbers wider than 16 bits in their addressing system. So they overcame the limit by inventing a system of compound addresses. Each compound address contained 2 16-bit numbers, to be interpreted in a special way. These were the first "segmented addresses" in microcomputers. Coinciding with this was the appearance of a new CPU chip design with new registers to facilitate the new addressing method. (The CPU designers at Intel and the PC architects at IBM worked hand-in-glove designing each piece with the other in mind.)

谁知道,一年后的 1981的IBM PC 改版了CPU的设计,是为了让内存可以达到1MB以上,但最关键的一点是又要保持与1980年的程序兼容,所以聪明的设计者们就想出了‘复合地址’这样的概念。用两个 16位的地址复合在一起,来决定实际的物理地址。

在这里有几问题,

问题1. segment地址是谁输入给CPU的?程序自己吗?还是定义宏? 
(待回答)

问题2:

when he wishes to talk about 763. He now has a way to talk about it, but has successfully avoided using 3-digit numbers. Note he could land on 763 several other ways. For example, by starting at 750 instead of 760, then advancing 13 instead of 3. Just as the 43 yard line on the gridiron is equivalently a 3 yard gain from the 40, a 13 yard gain from the 30, or a 23 yard gain from the 10. All, same thing.  So our engineer could write any of the following to refer to 763:

 76:03
 75:13
 74:23
 73:33
 72:43
 71:53
 70:63
 69:73
 68:83
 67:93


问题2:那么,同一个物理地址763,竟然有那么多的表达方式,所以有很多地址组合都指向同一个物理地址,那会产生什么后果吗?


问题3:

That's it. He can't let his first number go any lower than 67, because that would leave him short of 763 by more than 99, and the second number can only raise him 99 beyond his first one. You can make up the following rule for converting one of these compound addresses into a non-compound (i.e., regular 3-digit) one: to find the 3-digit linear address, take the left number of the compound address, shift it left one place (i.e., multiply it by 10), then add the right number.

这里意思是说,第一个两位数(像地址67:93里的93)最多只能给前一个两位数(67)贡献一个99的位移,再大过百了,就不行了,于是为了达到 763的地址,这里的第一个两位数不能低于67,如果用的是 66:99这样的组合,最大也只是660+99=759这样的组合,产生不了763地址。

结论是:段地址是有限制!那在实际的CPU中,这个限制是如何实现的呢?是通过硬件呢?还是通过软件呢?

问题4,在linux里,已经有了paging 这种技术,那么就不用segmentation这样的地址寻址了,是吗?那就给32-bit 寄存器 register们,赋一个 0000 0000 (假设在32位机器里面),剩下的寻址那就让Offset 地址来做不就行了吗?Offset 地址也是32位的,就是从 0000 0000  ~ FFFF FFFF ,那就是linear address 直接等于 Virtual address, 实际的linux是这么实现的吗?

答:被我猜对了!!! 好激动,在Understanding Linux Kernel 第 42页,我们可以看到linux 就是给这些segment descriptor 的 base 值全部赋起了0x0000 0000!!!!


#include "stm32f10x.h" // ???????? #define MAX_SLOTS 99 // ??????(???),??0 - 9????? const uint8_t SEG_CODE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; // ?????? // ??????(??)?????? #define DIGIT1_PORT GPIOC #define DIGIT1_PIN GPIO_Pin_11 // ??????(??)?????? #define DIGIT2_PORT GPIOC #define DIGIT2_PIN GPIO_Pin_12 // ???????????? #define SEGMENT_PORT GPIOC // ???????????(A - G?DP) #define SEGMENT_PINS (GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | \ GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8) // ?????????????? #define BUTTON_DOWN_PORT GPIOA #define BUTTON_DOWN_PIN GPIO_Pin_1 // ?????????????? #define BUTTON_UP_PORT GPIOA #define BUTTON_UP_PIN GPIO_Pin_2 // ???????????? #define BUTTON_RESET_PORT GPIOA #define BUTTON_RESET_PIN GPIO_Pin_3 // ??????????? #define BUZZER_PORT GPIOA #define BUZZER_PIN GPIO_Pin_13 // ?????? // ????????,?????????? uint8_t slotCount = 99; // ??????,0?????,1?????? uint8_t alarmState = 0; // ???????,??????????? void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); } // GPIO?????,?????????????? void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStruct; // ??GPIOA?GPIOC???,?????????????? RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); // ????????????????,???50MHz GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Pin = SEGMENT_PINS; GPIO_Init(SEGMENT_PORT, &GPIO_InitStruct); // ????????????????,???50MHz GPIO_InitStruct.GPIO_Pin = DIGIT1_PIN | DIGIT2_PIN; GPIO_Init(DIGIT1_PORT, &GPIO_InitStruct); // ?????????????,??????,??????? GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Pin = BUTTON_DOWN_PIN | BUTTON_UP_PIN | BUTTON_RESET_PIN; GPIO_Init(BUTTON_DOWN_PORT, &GPIO_InitStruct); // ??????????????,???50MHz GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Pin = BUZZER_PIN; GPIO_Init(BUZZER_PORT, &GPIO_InitStruct); } // ???????,?????????? void Display(uint8_t digit1, uint8_t digit2) { // ?????(??) GPIO_ResetBits(DIGIT1_PORT, DIGIT1_PIN); // ????????(?????) GPIO_SetBits(DIGIT2_PORT, DIGIT2_PIN); // ???????? // ???????,???? GPIO_Write(SEGMENT_PORT, 0x00); Delay(100); // ????,?????? // ??????,????????? GPIO_Write(SEGMENT_PORT, SEG_CODE[digit1]); Delay(500); // ????(?2.5ms @ 72MHz) // ?????(??) GPIO_SetBits(DIGIT1_PORT, DIGIT1_PIN); // ???????? GPIO_ResetBits(DIGIT2_PORT, DIGIT2_PIN); // ???????? // ???????? GPIO_Write(SEGMENT_PORT, 0x00); Delay(100); // ???? // ??????,????????? GPIO_Write(SEGMENT_PORT, SEG_CODE[digit2]); Delay(500); // ???? } // ??????????,???????????? uint8_t ButtonPressed(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == Bit_RESET) { Delay(20000); // ????(?10ms) if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == Bit_RESET) { while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == Bit_RESET); // ?????? return 1; // ????????1 } } return 0; // ????????0 } // ??????,????????????????? void CheckButtons(void) { // ????????(??PA1??) if (ButtonPressed(BUTTON_DOWN_PORT, BUTTON_DOWN_PIN)) { if (slotCount > 0) { slotCount--; alarmState = 0; // ???????,?????? } else { alarmState = 1; // ???0?,?????? } } // ????????(??PA2??) if (ButtonPressed(BUTTON_UP_PORT, BUTTON_UP_PIN)) { if (slotCount < MAX_SLOTS) { slotCount++; alarmState = 0; // ???????,?????? } } // ??????(??PA3??) if (ButtonPressed(BUTTON_RESET_PORT, BUTTON_RESET_PIN)) { slotCount = 99; alarmState = 0; // ?????????,?????? } } // ???????,??????????? void Beep(void) { // ???????????,???? GPIO_SetBits(BUZZER_PORT, BUZZER_PIN); Delay(100000); // ???50ms // ???????????,???? GPIO_ResetBits(BUZZER_PORT, BUZZER_PIN); } int main(void) { GPIO_Configuration(); // ???GPIO while (1) { // ?????????????????? uint8_t digit1 = slotCount / 10; uint8_t digit2 = slotCount % 10; // ???????????,??????? Display(digit1, digit2); Display(digit1, digit2); // ????????? CheckButtons(); // ??????????? if (alarmState) { Beep(); Delay(100000); // ?????? } } }为什么数码管不显示数字显示乱码
最新发布
05-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值