PC指针的问题

举个小例子,下面是一段ARM汇编代码:

地址

指令

0x3000

BL add

0x3004

MOV r0,#0

0x3008

MOV r1,#1

0x300C

MOV r2,#2

AREA test,CODE,READONLY
    ENTRY
    Start
         MOV r0,#1
         MOV r1,#1
         BL add
         MOV r0,#0
         MOV r1,#1
    Add
         ADD r0,r0,r1
         MOV r0,r0,r1
         END

当0x3000处的BL指令执行时,会把PC(=0x3008)保存到LR寄存器里面,也就是LR=0x3008。接下来处理器会立即对LR进行一个自动的更新动作:LR=LR-0x4,这样,LR里面的地址为0x3008 – 0x4 = 03004,它是指令”MOV r0,#0”的地址,所以当从子程序add返回时,LR里面正好是正确的返回地址。既是下一条要执行的指令的地址。

(2)中断异常处理函数调用
异常就是正在执行的指令,由于各种软件或硬件故障被打断,比如,在读数据或指令时,访问存储器失败、产生了一个外部硬件中断等。当这些情况发生时,在ARM系统里,由异常和中断处理程序做出相应的处理,当处理完成后,要返回到被中止的指令,使被中止的指令能够继续正常执行下去。因此,确定异常和中断处理程序的返回地址是一个非常重要的问题。

1、中断处理

当外部中断IRQ和FIQ(Fast Interrpt Request,快速中断请求)发生时,ARM核完成一部分工作。当然,这些工作是任何异常发生时都必须要做的,所以ARM处理器就会自动带我们完成。 其它重要的工作,必须由程序员来完成。ARM处理器处理的事包括从用户模式切换到IRQ模式、状态寄存器值的变化及跳转。比如说,处理器自动跳转到从0x0地址开始的异常中断向量表的0x18处,在向量表的0x18处,最简单的指令为”B HandlerIRQ”。

那程序员所要关心的就是实现具体的异常处理程序(HandlerIRQ)。当用ARM汇编语言实现HandlerIRQ函数的时候,如何确定HandlerIRQ函数正确地返回地址,使被中止的指令能够继续正常执行下去。

比较常用的中断处理程序结构如下:

HandlerIRQ ;中断响应,从向量表直接跳来

SUB r14,r14,#4 ;计算返回地址

STMFD r13,{r0-r3,r14} ;保护现场,一般只需要保护{r0-r3,lr}

BL irqHandler ;跳到具体的异常处理函数

LDMFD r13,{r0-r3,pc}^ ;恢复现场

有程序可以看出,通过”SUB R14,R14,#4”计算中断函数的返回地址。那有人一定会问,为什么计算返回地址的时候要减去4呢?

地址

指令

0x3000

BL add

0x3004

MOV r0,#0

0x3008

MOV r1,#1

0x300C

MOV r2,#2

我们看上个表,比如在执行地址为0x3004的move指令时,突然来了一个IRQ中断,这个中断打断了move指令的执行,这个时候就要去跳转到异常处理函数,之后还要返回0x3004地址重新执行move指令。当中断发生时,LR里面保存了用户模式下PC的值,那么当执行地址为0x3004的move指令时,PC的值应该是0x300C,前面介绍过,当发生跳转时,处理器会对LR进行一个自动的更新动作:LR=LR-0x4,这样LR里面的地址是0x300C-0x04=0x3008。但是0x3008并不是我们要的地址,因为中断发生在地址为0x3004的move指令执行的时候,所以中断处理完后应该返回这个地址。 这就是在计算返回地址的时候LR减去4的原因。对于FIQ中断和预取指中止异常,计算返回地址方法和IRQ相同。

有关PC的博文可参考:http://blog.youkuaiyun.com/liukun321/article/details/5594342

### PC指针的概念与定义 程序计数器(Program Counter, PC)是一种特殊的寄存器,它的主要功能是存储下一条要执行的指令地址。在JVM中,PC寄存器的作用是对物理处理器中的PC寄存器进行抽象模拟[^1]。它并非真正的硬件寄存器,而是由软件实现的一个小型内存单元。 #### 定义 程序计数器可以被看作是一个“行号指示器”,用于记录当前线程正在执行的字节码指令的位置[^2]。由于每个线程都有自己独立的PC寄存器,因此即使多个线程并发执行,它们也不会相互干扰。 --- ### 程序计数器的主要特性 1. **线程私有** 每个线程拥有自己独立的PC寄存器,这意味着不同线程之间的程序计数器互不影响。这一设计简化了多线程环境下的上下文切换过程[^1]。 2. **存储范围有限** PC寄存器仅能存储指令地址,无法容纳大量数据或其他复杂信息。这使其成为一个高效但简单的组件[^2]。 3. **不可见性** 对于开发者而言,PC寄存器的存在通常是透明的。程序员无需手动操作或管理它,因为这是JVM内部自动完成的工作。 --- ### 使用场景与工作原理 #### 方法调用期间的行为 当一个方法被调用时,新的栈帧会被压入对应线程的虚拟机栈中,此时PC寄存器会更新为指向该方法的第一条字节码指令。随着方法逐步执行,PC寄存器不断递增以指向后续的指令位置。一旦方法返回,对应的栈帧将被弹出,而PC寄存器也会恢复至调用前的状态。 #### 特殊情况处理 如果当前线程处于等待状态(如`Object.wait()`),或者因I/O阻塞等原因暂停,则其PC寄存器不会发生变化,直到线程重新进入可运行状态为止。 --- ### 与其他内存区域的关系 尽管PC寄存器专注于追踪字节码指令的执行流程,但它仍然与其他内存结构存在一定的关联: - **与栈的关系** 虚拟机栈负责保存方法调用过程中所需的数据和局部变量表,而PC寄存器则决定了当前应从哪个位置读取并解析字节码指令[^4]。 - **与堆及方法区的关系** 尽管PC寄存器本身不直接参与对象分配或类元数据管理,但在某些情况下,它可能会间接影响这些行为。例如,在执行涉及对象实例化的字节码指令时,可能触发堆上的内存分配动作;而在加载常量池引用时,则需访问方法区内存储的相关信息[^3]。 --- ### 示例代码说明 以下展示了一个简单的方法调用案例及其背后隐含的PC寄存器变化机制: ```java public class Example { public static void main(String[] args) { int result = add(5, 7); // (1) System.out.println(result); } private static int add(int a, int b) { // (2) return a + b; } } ``` 在此例子中: - 当主线程到达`(1)`处时,会产生一个新的栈帧并将参数传递给`add`函数; - 此刻,PC寄存器调整为目标方法入口地址; - 随后逐条解释执行直至遇到返回语句,最终跳转回原调用点继续剩余逻辑[^1]。 --- ### 总结 综上所述,程序计数器作为JVM架构中的核心组成部分之一,承担着维持程序正常流转的重要职责。通过对每一线程单独配置此资源的方式,不仅实现了高效的多任务调度能力,同时也保障了各进程间的安全隔离效果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值