-
静态局部变量存在哪里_干货 || 嵌入式实时系统中局部变量和全局变量
2021-01-19 02:33:18转载自公众号「有思考的人」,作者:ksj一、首先简要介绍局部变量和全局变量区别(1)作用域全局变量具有全局作用域,适用于所有源文件。但在不包含全局变量定义的文件中,需使用extern关键字声明这个全局变量后,方可...转载自公众号「有思考的人」,作者:ksj
一、首先简要介绍局部变量和全局变量区别
(1)作用域
全局变量具有全局作用域,适用于所有源文件。但在不包含全局变量定义的文件中,需使用extern关键字声明这个全局变量后,方可正常使用。
静态全局变量也具有全局作用域。它与全局变量的区别是,它仅仅作用于定义它的文件,程序中其他文件不可用。
局部变量只有局部作用域。只在函数执行期间存在,当函数调用结束后,变量将被撤销,其所占用内存被收回。
静态局部变量只有局部作用域。它从初始化到函数运行结束一直存在,在整个程序运行期间一直有效。它与全局变量的区别在于,全局变量对所有函数可见,而静态局部变量只对自己的函数始终可见。
(2)存储空间
全局变量、静态全局变量、静态局部变量都在静态存储区分配空间,而局部变量在栈分配空间。
二、Cortex-M3内核中MSP/PSP
1、在CM3处理器中有两个堆栈指针MSP/PSP,这两个都是R13/SP,R13是Banked。
MSP:它有OS内核、异常服务历程以及所有需要特权访问的应用程序代码访问。程序复位默认使用MSP。
PSP:用于常规的应用程序代码。
通过CM3的CONTROL寄存器可选择当前使用哪个堆栈指针。
2、
图1
上图1所示为未使用OS时,堆栈的使用情况。
图2
上图2所示为使用OS时,堆栈的使用情况。
3、使用OS时,MSP及PSP跳转状态及任务堆栈切换时变化
1)任务切换前的状态
假设系统中有两个任务,Task1和Task2,Task1是当前正在运行的任务(由OSTCBCur指出),Task2处于挂起状态。
那么进入OS_CPU_PendSVHandler中断前,堆栈状态如下图所示。
CPU处于线程状态,使用PSP堆栈工作,PSP指向Task1的堆栈。
CPU中的各寄存器是Task1当前任务的寄存器值。
Task2处于挂起状态,Task2的堆栈指针由TCB2的SP变量保存着。在Task2的堆栈底部,保存有两部分数据,一部分是CPU中断时自动保存到堆栈的寄存器变量(包括xPSR,PC,LR,R12,R0~R3),另一部分是uCOS额外保存的寄存器变量(R4~R11),这些寄存器保存了Task2挂起前的所有数据。
2)任务切换后进入中断例程时的状态
当条件变化导致Task1需要切换到Task2时(OSTCBHighRdy会指向Task2的TCB2),PendSV中断被激发。
进入OS_CPU_PendSVHandler中断时,根据Cortex-M3的中断流程,一部分动作由CPU自动执行:
CPU将xPSR,PC,LR,R12,R0~R3自动保存到当前堆栈,由于PSP是指向Task1的堆栈的,所以这些寄存器会自动保存到Task1的堆栈中。
CPU切换到Handler模式,使用MSP作为中断例程的工作堆栈。
PC指向中断例程,执行中断例程。
进入OS_CPU_PendSVHandler中断时,堆栈状态如下图所示:
3)uCOS保存当前任务现场后的状态
进入OS_CPU_PendSVHandler后,由于CPU只自动保存了部分寄存器值,uCOS需要将其余寄存器也保存下来,以便切回任务时能完整恢复现场。
OS_CPU_PendSVHandler会根据PSP的值得到Task1的堆栈底部,然后将额外的寄存器R4~R11保存到Task1的堆栈底部。
并且将更新后的Task1的堆栈值保存到TCB1的SP变量中。
OS_CPU_PendSVHandler保存完当前任务数据后的堆栈状态如下图所示:
4)uCOS恢复目标任务数据后的状态
之后OS_CPU_PendSVHandler需要恢复Task2任务的现场数据。
OS_CPU_PendSVHandler从OSTCBHighRdy获取需要切换到的任务块(此时其等于TCB2),然后从TCB2的SP变量获取该任务的堆栈指针。
得到Task2的堆栈指针后,OS_CPU_PendSVHandler从其堆栈底部恢复R4~R11寄存器的值(这部分是先前由uCOS保存的),然后调整CPU的PSP指针指向Task2堆栈中先前CPU自动保存数据的地方,如下图所示。
此时CPU的R4~R11寄存器已恢复为Task2挂起前的值,但R0~R3、R12、LR、PC、xPSR这些尚未恢复,后面这些寄存器将在中断返回时由CPU自动恢复。
最后OS_CPU_PendSVHandler调用BX LR执行中断返回(LR中的值是EXC_RETURN值,以通知CPU做中断返回动作)。
OS_CPU_PendSVHandler在中断返回前的堆栈状态如下:
5)uCOS从中断返回,完成任务切换后的状态
OS_CPU_PendSVHandler调用BX LR后,由CPU完成剩余的现场恢复:
CPU从PSP堆栈中恢复xPSR,PC,LR,R12,R0~R3这些寄存器的值,由于PSP已指向了Task2的堆栈,所以这些寄存器的值被恢复为Task2堆栈中的值,即Task2任务挂起前的寄存器值。
CPU的PC值也从堆栈中恢复到Task2任务被中断时的PC值。
CPU退出Handler模式,切换到线程模式,重新使用PSP堆栈作为工作堆栈(此时PSP已指向Task2的堆栈),使用Task2的堆栈作为工作堆栈。
CPU已恢复到Task2挂起前的现场,从Task2被中断的PC处继续运行。
对比任务切换前的状态,Task1与Task2的状态完全对调了,所以完成了Task1与Task2的切换。
中断返回后,完成Task2任务切换的堆栈状态如下图所示:
-
变量作用域及局部变量,成员变量,静态变量的区别
2020-12-14 09:16:49变量作用域 变量分为静态变量(类变量)和实例变量(成员...实例变量和静态变量的作用域是整个类,无论变量是在哪里声明的。 在方法中定义的变量成为局部变量,,局部变量使用之前必须声明+赋值,其作用域为方法内 ...变量作用域
变量分为静态变量(类变量)和实例变量(成员变量)。
静态变量从属于类。
实例变量从属于方法。
因为类先于方法而存在,所以静态成员先于实例成员而存在,所以静态方法不能访问实例方法和实例变量,但是可以先创建一个对象,用对象来访问实例成员。
实例变量和静态变量定义在类内方法外,局部变量定义在方法内。
实例变量和静态变量的作用域是整个类,无论变量是在哪里声明的。
在方法中定义的变量成为局部变量,局部变量使用之前必须声明+赋值,其作用域为方法内。
成员变量声明后,系统就会为其分配初始值。
但是方法局部变量和代码块局部变量系统不为其分配初始值。如图输出j时会报错。
值得注意的是,成员变量(类变量和实例变量)和局部变量可以重名,在方法内,优先调用局部变量,若想在定义了同名的局部变量的方法内调用成员变量,可以使用this或者类名来调用实例变量和静态变量。 -
全局变量和局部变量
2020-04-10 21:56:50全局变量和局部变量 全局变量 全局变量是在所有函数体外(包括主函数)进行定义的。程序的所有部分(包括其他文件中的代码)都可以使用。全局变量的生命期是从定义...声明,告诉编译器变量在哪里。 如 //: Global.cp...全局变量和局部变量
全局变量
全局变量是在所有函数体外(包括主函数)进行定义的。程序的所有部分(包括其他文件中的代码)都可以使用。全局变量的生命期是从定义开始到整个程序的结束。
对于当前文件可以直接使用全局变量。
对于其他文件需要使用关键字extern进行声明。这是因为不同的源文件是分段编译的,必须通过
extern int global;
声明,告诉编译器变量在哪里。如
//: Global.cpp #include <iostream> using namespace std; int global; void f(); int main() { global = 1; cout << global << endl; //1 f(); cout << global << endl; //4 return 0; } //: Global.cpp extern int global; void f() { global = 4; }
局部变量
局部变量出现在某个作用域内({}),它限定在某个函数内。在进入函数体内被创建,离开函数体后被销毁。
因此局部变量也被成为自动变量,可以使用关键字auto进行显式声明,但变量默认为局部变量。
参考文献
[1]. C++编程思想
-
深入剖析ThreadLocal线程局部变量
2020-05-21 22:38:13每个线程的变量副本是存储在哪里的3.通过源码分析ThreadLocal具体实怎样工作的3.1set方法3.2get方法4.变量副本【每个线程中保存的那个map中的变量】是怎么声明和初始化的5.不同的线程局部变量,比如说声明了n个(n>...文章目录
1.简介
多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。
2.每个线程的变量副本是存储在哪里的
Thread类中提供了这样两个变量可以使每个线程有属于自己本身线程的变量而不被其他线程共享
我们发现二者都是ThreadLocal
内部类ThreadLocalMap
类型的变量3.通过源码分析ThreadLocal具体实怎样工作的
ThreadLocal提供了set和get访问器用来访问与当前线程相关联的线程局部变量,
3.1set方法
public void set(T value) { //(1)获取当前线程(调用者线程) Thread t = Thread.currentThread(); //(2)以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); //(3)如果map不为null,就直接添加本地变量 if (map != null) map.set(this, value); //(4)如果map为null,说明首次添加,需要首先创建出对应的map else createMap(t, value); }
getMap
方法:ThreadLocalMap getMap(Thread t) { return t.threadLocals; //获取线程自己的变量threadLocals,并绑定到当前调用线程的成员变量threadLocals上 }
createMap方法:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
我们通过分析set方法源码:某个线程通过threadLocal变量设置值的时候会获取自己线程的
threadLocals
变量即getMap(t),通过
ThreadLocalMap
类型来接收,并且通过set(this,value)将threadLocal作为键,value当作值存储在本地线程,到现在为止我们就可以得出下面的结论了:- 每个线程的本地变量不是存放在ThreadLocal实例中,而是放在调用线程的ThreadLocals变量里面
3.2get方法
我们接着来看get方法
public T get() { //(1)获取当前线程 Thread t = Thread.currentThread(); //(2)获取当前线程的threadLocals变量 ThreadLocalMap map = getMap(t); //(3)如果threadLocals变量不为null,就可以在map中查找到本地变量的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //(4)执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量 return setInitialValue(); } private T setInitialValue() { //protected T initialValue() {return null;} T value = initialValue(); //获取当前线程 Thread t = Thread.currentThread(); //以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); //如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值 if (map != null) map.set(this, value); //如果map为null,说明首次添加,需要首先创建出对应的map else createMap(t, value); return value; }
在get方法的实现中,首先获取当前调用者线程,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,以threadLocal作为参数,取出value,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null。
4.变量副本【每个线程中保存的那个map中的变量】是怎么声明和初始化的
从我们分析set和get方法的时候其实已经得到了这个答案,当线程中的
threadLocals
成员是null的时候,会调用ThreadLocal.createMap(Thread t, T firstValue)
创建一个map。同时根据函数参数设置上初始值。也就是说,当前线程的threadlocalmap是在第一次调用set的时候创建map并且设置上相应的值的。
在每个线程中,都维护了一个threadlocals对象,在没有ThreadLocal变量的时候是null的。一旦在ThreadLocal的createMap函数中初始化之后,这个threadlocals就初始化了。以后每次ThreadLocal对象想要访问变量的时候,比如set函数和get函数,都是先通过getMap(Thread t)函数,先将线程的map取出,然后再从这个在线程(Thread)中维护的map中取出数据或者存入对应数据。5.不同的线程局部变量,比如说声明了n个(n>=2)这样的线程局部变量threadlocal,那么在Thread中的threadlocals中是怎么存储的呢?threadlocalmap中是怎么操作的?
我们先来一个Demo:
public class ThreadLocalTest { static ThreadLocal<String> localVar = new ThreadLocal<>(); static void print(String str) { //打印当前线程中本地内存中本地变量的值 System.out.println(str + " :" + localVar.get()); //清除本地内存中的本地变量 localVar.remove(); } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 localVar.set("localVar1"); //调用打印方法 print("thread1"); //打印本地变量 System.out.println("after remove : " + localVar.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 localVar.set("localVar2"); //调用打印方法 print("thread2"); //打印本地变量 System.out.println("after remove : " + localVar.get()); } }); t1.start(); t2.start(); } }
这两个线程都是通过
localVar
这个ThreadLocal
来往本地线程中完成副本的创建和赋值,并且在set函数中,map.set(this, value)
把当前的localVar
传入到map中作为键,也就是说,在不同的线程的threadlocals变量中,都会有一个以你所声明的那个线程局部变量threadlocal作为键的key-value。假设说声明了N个这样的线程局部变量变量,那么在线程的ThreadLocalMap中就会有n个分别以你的线程局部变量作为key的键值对。6.总结
在往本地线程设置线程局部变量时,
ThreadLocal
更像是一个辅助类,这个类本身不含有ThreadLocalMap threadLocals
变量,而是通过set或get方法进而操作调用它的线程的threadLocals
变量
参考博客:
https://www.cnblogs.com/fsmly/p/11020641.html
https://blog.csdn.net/qq_33404395/article/details/82356344 -
Java 内存区域中关于局部变量的值的存放位置
2019-10-14 15:53:38我知道变量名和变量类型是保存在栈中的局部变量表中,可是局部变量表结构并没有字面量成员,那a的值1是存放在哪里? 反编译后的字节码指令是: ```java 0: iconst_1 1: istore_2 ``` 第一条指令是将常量1压... -
针对JS经典题型对全局变量及局部变量的理解浅谈
2016-08-21 21:03:00第一次写博,还蛮激动。。。 看到了三题经典题型,就我目前的认识对此题进行总结。如有错误,敬请指正 ...当看到一个函数或一个变量时,js引擎就会去查询 这个函数或变量是在哪里定义的 查询的方式有两种: ... -
JavaScript的局部变量没有块级作用域
2008-05-15 10:40:59一直以为JavaScript的变量和其他语言(如Java)一样,...显然JavaScript的局部变量作用域应属于函数级作用域,即在一个函数中声明的变量,无论是在哪里声明的,在整个函数中它们都是有定义的(详见这里 )。 ... -
js中理解变量的声明
2011-08-17 18:02:451,函数内声明的变量,无论在哪里声明,作用域都是函数内,无论if块,for循环内等等 2,声明局部变量,必须使用var关键字声明,否则该变量是全局的。谨记 从事例加深理解,你可以使用记事本保存为html,... -
变量作用域
2014-05-08 08:47:31变量作用域 一个程序的所有的变量并不是在哪个位置都可以访问的。访问权限决定于这个变量是在哪里赋值的。 变量的作用域决定了在哪一...局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问 -
java中有哪些变量_Java中都有哪些变量
2021-02-26 12:42:17软件的存在就是处理数据的,处理数据之前需要先将要处理的数据存储起来,那么程序将数据存储在哪里呢?对,存储在变量中。变量是什么?其实变量就是内存中的一块空间,是内存中存储数据的基本单元。在Java语言中,... -
JavaScript在一行上定义变量
2019-01-07 13:05:41时常看到在些定义变量的“非常规”方法,特别是在一行上定义变量赋值的操作,有时看的很懵,所以...a其实是一个全局变量,b则不然,有可能是全局,也有可能是局部的变量,关键看它是在哪里声明的。 下面代码则可以解... -
关于JS中的全局变量的小知识
2020-12-17 21:42:22局部变量在函数被调用的时候分配空间,函数执行结束之后,内存释放 --> <!--js遵循就近原则 变量声明了没有手动赋值,系统默认赋值undefined 变量没有声明直接访问,会报错 如果声明的... -
python-变量作用域
2016-11-18 09:35:00python-变量作用域 变量作用域 Pyhton 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。 变量的作用域决定了在哪一...局部变量只能在其被声明的函数内部访问,而全局变量可以... -
Python函数中的变量作用域
2019-09-12 00:03:23一、变量作用域 一个程序的所有的变量并不是在哪个位置都可以访问的。访问权限决定于这个变量是在哪里赋值的。 变量的作用域决定了在...局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问... -
python作用域,变量作用域
2019-07-02 20:33:02变量作用域 一个程序的所有变量并不是在哪个位置都可以访问的。访问权限取决于这个变量是在哪里赋值的。...局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时... -
作用域,变量与全局变量
2018-09-11 18:18:17全局作用域:在哪里都可以读写(从上到下,从左到右); 全局变量:在函数外面声明的变量, 在函数内部申明,单没有使用关键字var; 全局函数:指的是在script标签内声明的,非函数嵌套 局部作用域:只能在特定... -
python global语句 变量作用域
2018-06-29 17:44:21变量作用域决定了这个变量能够被访问的范围,因为python不需要做变量声明,所以变量出现在哪里,他的作用范围就在哪里。关于全局变量和局部变量,可以这样认为:在函数中的变量是局部变量,而在程序代码主体部分的是... -
java中定义的基本类型存放在哪里
2019-12-16 10:48:32一:在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁... -
js中的全局变量注意点
2020-03-31 10:14:42第一种:var a=5; 这种方式不能在function中声明,否则是局部变量。 第二种:a=5; 这种方式不管在哪里声明都是全局变量。 js中声明变量格式:var(关键字)+变量名(标识符)。 ... -
关于JavaScript 中的变量
2014-08-27 14:46:00JS的变量声明语句永远是在其作用域中最先执行的,不论其定义的位置在哪里; 函数体内部,局部变量的优先级比同名的全局变量高。 Javas作用域分类 未使用var定义的变量不论定义在什么位置都是全局变量,全局变量... -
基本数据类型的值存放在哪里
2018-12-13 10:07:10备注:这里只针对基本数据类型做讨论(int long double float char byte boolean short) ...局部变量 public class Test { public static int a = 1;// 类变量 public int b = 2;// ... -
python自学篇九[ 函数(三):变量作用域+ATM+5个小练习]
2020-01-01 17:18:37变量的作用域 个程序的所有的变量并不是在哪个位置都可以访问的。...局部变量:声明在函数内部的变量是局部变量,局部变量的作用于只在于函数中,外部无法使用 不同的函数,可以定义相同的名字的... -
js-----初步了解变量 作用域
2017-11-29 19:00:25变量的声明用var //显式声明(var i = 100;) 1. 全局变量:在函数外通过var声明的变量。... 没有声明就使用的变量,不管在这句话在哪里,都是默认全局变量的赋值 script> a = 1;//默认就是全局变量 function t() -
彻底明白JS变量作用域
2020-09-22 14:17:51JS变量作用域牢记一段句话下面来理解下这段话作用域(以下均摘自...注意两个点:不加var声明的变量不管在哪里都为全局变量。 2、由于JS不存在块级作用域,对于for和if中定义的变量隶属于函数作用域 下面来理解下 -
堆和栈的区别在哪里?
2013-01-12 11:21:13堆和栈的区别在哪里? 哪些变量会在栈中分配空间,哪些变量会在堆中分配空间?... 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 heap: 需要程序员自己申请,并指明大小,在c中mal -
JavaScript中的变量作用范围
2009-03-23 20:18:10对于JavaScript来说,没有块级作用域的概念,函数中声明的所有变量无论是在哪里声明的,在整个函数中都会有意义的。如: function test(q){ var x=0; if(typeof(q)=="object"){ var y=0.... -
python学习笔记(六) 变量的作用域与异常处理
2019-07-08 11:48:18程序的变量并不是在所有位置都能使用,访问的权限决定于变量在哪里赋值 2、根据变量声明的位置不同,作用域可以分为4类 局部作用域--->函数体中声明的变量 嵌套作用域--->嵌套函数中外层函数体中声明的...