可重入函数与不可重入函数
c 不可重入函数
c 不可重入函数在C语言中,不可重入函数(non-reentrant function)是指那些不安全的函数,即当多个线程或进程同时调用这些函数时,可能会导致数据竞争、死锁或其他并发问题。
不可重入函数的主要特点如下:1. 使用全局变量或静态变量:不可重入函数通常使用全局变量或静态变量来存储状态信息,这样在函数调用之间可以共享这些变量。
然而,当多个线程或进程同时调用这些函数时,它们可能会修改相同的全局变量或静态变量,导致数据不一致或冲突。
2. 静态内存分配:不可重入函数通常使用静态内存分配来存储局部变量,而不是动态内存分配。
这样做的目的是为了节省内存,但在多线程或多进程环境中,这可能导致问题。
因为静态内存分配的变量在函数调用之间保持存在,其他线程或进程可能会修改这些变量,导致数据不一致。
3. 不安全的函数调用:不可重入函数可能会调用其他不可重入函数,或者使用不安全的函数来操作数据。
这些不安全的函数可能会使用全局变量、静态变量或静态内存分配,从而引入并发问题。
为了编写安全的可重入函数,应该遵循以下原则:1. 使用局部变量:只在函数的局部范围内使用局部变量来存储状态信息,而不是使用全局变量或静态变量。
这样可以在多个线程或进程之间隔离这些变量,避免数据竞争和冲突。
2. 使用动态内存分配:使用动态内存分配来存储局部变量,这样在函数返回时可以释放内存,避免因静态内存分配而引入的问题。
3. 避免使用不安全的函数:只调用安全的可重入函数,避免使用其他不可重入函数或不安全的函数来操作数据。
4. 保护共享资源:如果函数需要访问共享资源(如文件、数据库等),应该使用适当的同步机制(如互斥锁、信号量等)来保护这些资源,以避免数据竞争和冲突。
单片机程序实现可重入和不可重入函数方法
单片机程序实现可重入和不可重入函数方法
可重入函数是指一个函数可以被多个任务同时调用而不会产生冲突的函数,不可重入函数是指一个函数在被同时调用时可能会产生冲突的函数。
实现可重入函数的方法如下:1. 避免使用全局变量和静态变量。
全局变量和静态变量可能会在函数调用期间被修改,导致并发访问时出现冲突。
如果需要使用全局变量,可以使用互斥锁保护对全局变量的访问。
2. 使用局部变量。
局部变量是每次函数调用时创建的,不会被其他任务访问到,因此可以保证并发访问的安全性。
3. 使用栈上的变量。
栈上的变量是每次函数调用时分配的,不会被其他任务访问到,因此可以保证并发访问的安全性。
4. 避免使用非可重入函数。
如果需要使用非可重入函数,可以使用互斥锁保护对非可重入函数的访问。
实现不可重入函数的方法如下:1. 使用全局变量和静态变量。
全局变量和静态变量是多个任务共享的,如果在函数中使用全局变量和静态变量,可能会导致并发访问时出现冲突。
2. 使用非线程安全的库函数。
一些库函数在多任务环境下可能不是线程安全的,如果在函数中使用这些库函数,可能会导致并发访问时出现冲突。
3. 使用非原子操作的指令。
一些指令在执行时可能是非原子的,如果在函数中使用这些指令,可能会导致并发访问时出现冲突。
需要注意的是,可重入函数不一定是线程安全的,因为线程安全还需要考虑共享资源的访问和同步。
而不可重入函数通常也不是线程安全的,因为它可能会对全局资源进行修改,需要使用互斥锁或其他同步
机制来保护对全局资源的访问。
芯动科技笔试题嵌入式工程师_2
芯动科技笔试题嵌入式工程师1、用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题)解答:这一题主要容易错的地方就是:意识到这个表达式将使一个16位机的整型数溢出,因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
2、写一个"标准"宏MIN,这个宏输入两个参数并返回较小的一个。
解答:这一题主要容易错的地方就是:懂得在宏中小心地把参数用括号括起来。
#define MIN(A,B)((A)<=(B)?(A):(B))当然,使用宏也是有副作用的。
就拿这一个例子来说:该宏定义对MIN(*p++,b)的作用结果是:((*p++)<=(b)?(*p++):(b))这个表达式会产生副作用,指针p会作两次++自增操作。
3、用变量a给出下面的定义:一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。
解答:这一道题主要容易错的地方就是:函数指针、指针数组。
int(*a[10])(int);4、关键字static的作用是什么?解答:在C语言中,关键字static有三个明显的作用:在函数体中,一个被声明为静态的变量在这一函数被调用过程中只会被分配一次内存,且整个运行期间不会重新分配;在函数体外、某个源文件内,一个被声明为静态的变量只可被该源文件内的所有函数访问,但不能被其他源文件的函数访问。
它是一个本地的全局变量;在某个源文件内,一个被声明为静态的函数仅仅只可以被这个源文件的其它函数调用。
也就是说,这个函数被限制在声明它的源文件的本地范围之内使用。
5、关键字const的作用是什么?解答:简单地说,const意味着常数。
const定义的变量,它的值不能被改变,在整个作用域中都保持固定;同宏定义一样,可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改;可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。
const是通过编译器在编译的时候执行检查来确保实现的。
linux中信号处理函数为可重入函数
在Linux 中,信号处理函数应该尽量设计为可重入函数。
可重入函数是指能够在多个线程同时调用时保持其行为正确的函数,无论这些线程是同一进程内的还是不同进程内的。
以下是一些编写可重入信号处理函数的指导原则:
1. 避免使用非可重入函数:在信号处理函数中尽量避免使用非可重入的函数,因为这些函数可能会使用全局变量或静态变量,如果多个信号处理函数同时调用它们,可能会导致数据竞争和意外的结果。
2. 使用信号安全的函数:在信号处理函数中只使用被认为是“信号安全”的函数。
信号安全的函数是指在信号处理函数中能够正常工作且不会破坏全局状态的函数。
例如,可以使用`write()` 函数来输出消息,但不可使用`printf()` 函数。
3. 如果必须使用非可重入函数:如果必须在信号处理函数中使用非可重入函数,确保使用保护措施来防止数据竞争。
例如,可以使用互斥锁来保护全局变量的读写操作,以避免多个信号处理函数之间的竞争条件。
4. 确保正确设置信号处理函数:在设置信号处理函数时,使用`sigaction()` 函数而不是`signal()` 函数。
`sigaction()` 函数提供了更精确的信号处理控制,可以指定信号处理函数的选项和属性。
通过遵循这些原则,可以编写出更安全和可靠的可重入信号处理函数,从而提高应用程序在处理信号时的稳定性和可维护性。
KeilC51可重入函数reentrantfunction和递归分析
KeilC51可重⼊函数reentrantfunction和递归分析可重⼊与不可重⼊,reentrant关键字Keil中帮助⽂档对此⼜详细的介绍这⼀段的意思是,在Keil中,正常情况下函数调⽤是通过固定寄存器传递参数。
因此当出现递归和类似的情况时,寄存器中参数可能会被覆盖。
如果想要通过堆栈来传递参数则需要使⽤reentrant参数。
这个⽤来传递参数的堆栈叫⽅针堆栈,与硬件堆栈不同。
这⼀段的意思是,在Keil中,正常情况下函数调⽤是通过固定寄存器传递参数。
因此当出现递归和类似的情况时,寄存器中参数可能会被覆盖。
如果想要通过堆栈来传递参数则需要使⽤reentrant参数。
这个⽤来传递参数的堆栈叫⽅针堆栈,与硬件堆栈不同。
如上图,不同的存储模式下,仿真堆栈的名字是不⼀样的。
因此,系统⾥⾯就会存在2个堆栈。
⼀个是系统⽤来传递参数的堆栈,如图中的?C_XBP,⽤DPTR寄存器进⾏操作。
(再次强调,若没有定义reentrant,参数的传递通过R1-R7寄存器进⾏)。
另外⼀个是⼤家⽐较熟悉的系统⽤来维护函数调⽤与中断调⽤等情况下压栈和出栈操使⽤的堆栈。
如图中的?STACK。
⽤SP寄存器进⾏操作。
下⾯结合具体的实例,分析递归调⽤时两个堆栈的情况。
如果不定义reentrant这个关键字,则编译时会出现如下错误,很显然参数在传递时会因为被覆盖导致表达式计算不是真是想要的。
当n=1时,第⼀次调⽤。
此时keil会将参数n存放⼊ R6和R7中。
调⽤mul函数,此时函数的返回地址会被放⼊?Stack中,该堆栈从0x0A开始(由系统分配,因此在编程时⼀定要注意该堆栈的⼤⼩)进⼊mul函数,此时会将存在R6与R7中的参数保存⼊仿真堆栈中?C_XBP中,该栈从0x80往下⽣长该堆栈的⼯作⽅式为先往下腾出位置,在把值写进去。
⽽框框中的操作数0xFFFE则是补码的形式,跟踪C?ADDXBP可以看出,该语句就是将堆栈往下移动⼀个空间当需要对该参数进⾏操作时,会从堆栈中取出参数进⾏操作⾄此,可以看出增加了reentrant关键字之后,其对局部变量的操作都是在堆栈中完成,这也是能够完成函数重⼊的关键。
可重入函数
占先式(Preemptive)
低优先级任务 中断服务程序使 高优先级任务就绪
(1)
(2)
ISR
(5)
(3)
高优先级任务
TIME
(4)
(6)
高优先级任务得到 CPU使用权
可重入型函数
可以被一个以上的任务调用,而不必担心数据的破坏。可重 入型函数任何时候都可以被中断,一段时间以后又可以运行,而相 应数据不会丢失。可重入型函数只使用局部变量,即变量保存在 CPU寄存器中或堆栈中。 一个不可重入型函数的例子
什么是可重入代码
• 可重入的代码指的是一段代码(比 如:一个函数)可以被多个任务同 时调用,而不必担心会破坏数据。
• 也就是说,可重入型函数在任何时 候都可以被中断执行,过一段时间 以后又可以继续运行,而不会因为 在函数中断的时候被其他的任务重 新调用,影响函数中的数据。
可重入代码举例
程序1:可重入型函数 void swap(int *x, int *y) { int temp; temp=*x; *x=*y; *y=temp; }
占先式(preemptive)
当系统响应时间很重要时,要使用占先式 (preemptive)内核。最高优先级的任务一旦 就绪,总能得到CPU的控制权。 当一个运行着的任务使一个比它优先级高 的任务进入了就绪态,当前任务的CPU使用权 就被剥夺了,或者说被挂起了,那个高优先级 的任务立刻得到了CPU的控制权。 使用占先式内核时,应用程序不应直接使 用不可重入型函数。如果调入不可重入型函数 时,低优先级的任务CPU的使用权被高优先级 任务剥夺,不可重入型函数中的数据有可能被 破坏。
非可重入代码举例
程序2:非可重入型函数 int temp; void swap(int *x, int *y) { temp=*x; *x=*y; *y=temp; 返回 }
可重入函数和不可重入函数
可重入函数和不可重入函数
可重入函数和不可重入函数是计算机编程中重要的概念,它们通常用于多线程编程。
可重入函数是指在多线程环境下可以安全地被多个线程同时调用,而不会出现不同线程之间的相互干扰或冲突。
不可重入函数则相反,不能被多个线程同时调用,因为会出现不同线程之间的相互干扰或冲突。
可重入函数通常具有以下特点:不使用或共享全局变量;不使用或共享静态变量;不使用或共享动态内存分配的内存;不使用或共享文件或设备等共享资源。
这些特点使得可重入函数可以被多个线程同时调用,而不会出现不同线程之间的相互干扰或冲突。
不可重入函数通常具有以下特点:使用或共享全局变量;使用或共享静态变量;使用或共享动态内存分配的内存;使用或共享文件或设备等共享资源。
这些特点使得不可重入函数不能被多个线程同时调用,因为会出现不同线程之间的相互干扰或冲突。
在多线程编程中,使用可重入函数可以更好地保证程序的正确性和稳定性。
因为多个线程同时调用可重入函数不会出现冲突,从而避免了程序崩溃或数据损坏
等问题。
而使用不可重入函数则需要进行同步操作,以避免不同线程之间的相互干扰或冲突。
总之,可重入函数和不可重入函数是多线程编程中非常重要的概念。
了解它们的特点和使用方式可以更好地提高程序的正确性和稳定性,从而保证程序的质量
和性能。
函数的可重入性、线程安全函数、异步信号安全函数
函数的可重⼊性、线程安全函数、异步信号安全函数 重⼊即表⽰重复进⼊,⾸先它意味着这个函数可以被中断,其次意味着它除了使⽤⾃⼰栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重⼊,可以允许有该函数的多个副本在运⾏,由于它们使⽤的是分离的栈,所以不会互相⼲扰。
,常见的情况是,程序执⾏到某个函数foo()时,收到信号,于是暂停⽬前正在执⾏的函数,转到信号处理函数,⽽这个信号处理函数的执⾏过程中,⼜恰恰也会进⼊到刚刚执⾏的函数foo(),这样便发⽣了所谓的重⼊。
此时如果foo()能够正确的运⾏,⽽且处理完成后,之前暂停的foo()也能够正确运⾏,则说明它是可重⼊的。
要确保函数可重⼊,需满⾜⼀下⼏个条件: 1、不在函数内部使⽤静态或全局数据 2、不返回静态或全局数据,所有数据都由函数的调⽤者提供。
3、使⽤本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
4、不调⽤不可重⼊函数。
线程安全:⼀个函数被称为线程安全的,当且仅当被多个并发线程反复的调⽤时,它会⼀直产⽣正确的结果。
要确保函数线程安全,主要需要考虑的是线程之间的共享变量。
在对这些共享变量进⾏访问时,如果要保证线程安全,则必须通过加锁的⽅式。
可重⼊与线程安全并不等同,⼀般说来,可重⼊的函数⼀定是线程安全的,但反过来不⼀定成⽴。
可以被信号控制器安全调⽤的函数被称为"异步信号安全"函数。
信号就像硬件中断⼀样,会打断正在执⾏的指令序列。
信号处理函数⽆法判断捕获到信号的时候,进程在何处运⾏。
如果信号处理函数中的操作与打断的函数的操作相同,⽽且这个操作中有静态数据结构等,当信号处理函数返回的时候(当然这⾥讨论的是信号处理函数可以返回),恢复原先的执⾏序列,可能会导致信号处理函数中的操作覆盖了之前正常操作中的数据。
不可重⼊函数的原因在于: 1. 已知它们使⽤静态数据结构 2.它们调⽤malloc和free.因为malloc通常会为所分配的存储区维护⼀个链接表,⽽插⼊执⾏信号处理函数的时候,进程可能正在修改此链接表。
Linux中可重入函数与不可重入函数详解
Linux中可重⼊函数与不可重⼊函数详解
Linux 中可重⼊函数与不可重⼊函数详解
可重⼊函数和不可重⼊函数说起来有点拗⼝,其实写过多进程(线程)程序的⼈肯定很快就能明⽩这两种函数是个神马东西。
下⾯是我对这两个函数的理解:
可重⼊函数可以理解为是能被中断的函数,并且它被中断返回后也不会出现什么错误。
不可重⼊函数可以理解为如果函数被中断的话,就会出现不可预料的错误。
这是因为函数中使⽤了⼀些系统资源,⽐如全局变量区,中断向量表之类的。
⽐如多个进程同时对⼀个⽂件进⾏写操作,如果没有同步机制的话,对⽂件的写⼊就会变得难以控制。
在多进程(线程)环境中⼀定要考虑到函数的可重⼊性。
例如下⾯的例⼦:
int sum( int count)
{
static int sum = 0;
int i = 0;
for (i= 1; index <= count; i++)
sum += i;
return sum;
}
这段代码中使⽤了static关键字,如果多个进程同时执⾏这⼀段代码的话就会出现不可预测的结果。
在可重⼊函数中,⼀定要避免使⽤static变量。
或者需要使⽤⼀定的同步原则才可以。
要想将上⾯的代码改为可重⼊函数,只要将static变量改为⾮static的变量就可以了。
在编写可重⼊函数时,如果函数中使⽤到了全局变量,则应通过关中断、信号量(即P、V操作)等⼿段对其加以保护,在函数体中尽量使⽤局部变量。
感谢阅读,希望能帮助到⼤家,谢谢⼤家对本站的⽀持!。
可重入问题
一、可重入函数1)什么是可重入性?可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。
相反,不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。
可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。
可重入函数要么使用本地变量,要么在使用全局变量时保护自己的数据。
2)可重入函数:不为连续的调用持有静态数据。
不返回指向静态数据的指针;所有数据都由函数的调用者提供。
使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
绝不调用任何不可重入函数。
3)不可重入函数:函数中使用了静态变量,无论是全局静态变量还是局部静态变量。
函数返回静态变量。
函数中调用了不可重入函数。
函数体内使用了静态的数据结构;函数体内调用了malloc()或者free()函数;函数体内调用了其他标准I/O函数。
函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量。
总的来说,如果一个函数在重入条件下使用了未受保护的共享的资源,那么它是不可重入的。
4)示例在多线程条件下,函数应当是线程安全的,进一步,更强的条件是可重入的。
可重入函数保证了在多线程条件下,函数的状态不会出现错误。
以下分别是一个不可重入和可重入函数的示例://c codestatic int tmp;void func1(int* x, int* y) {tmp=*x;*x=*y;*y=tmp;}void func2(int* x, int* y) {int tmp;tmp=*x;*x=*y;*y=tmp;}func1是不可重入的,func2是可重入的。
因为在多线程条件下,操作系统会在func1还没有执行完的情况下,切换到另一个线程中,那个线程可能再次调用func1,这样状态就错了。
可重入函数的特点
可重入函数的特点及其定义可重入函数是指在多线程环境下,函数可以被多个线程同时调用而不会引起不确定的结果。
它具有以下几个特点:1.线程安全:可重入函数在多线程环境下可以安全地被多个线程同时调用,不会出现数据竞争和不确定的结果。
2.可嵌套调用:可重入函数可以在自身的执行过程中再次调用自身,而不会出现死锁或其他问题。
3.无副作用:可重入函数不会对全局变量或静态变量进行修改,也不会引入全局状态,保证了函数的独立性和可重复使用性。
可重入函数的定义通常有以下几个要求:1.函数内部不使用全局变量:为了保证函数的可重入性,函数内部不应该依赖于全局变量或静态变量,而应该将所有需要的数据作为参数传入函数。
2.不修改静态变量:静态变量在函数调用过程中会被多个线程共享,如果可重入函数修改了静态变量,就会导致不确定的结果。
3.使用局部变量或栈上内存:为了避免线程间的数据竞争,可重入函数应该使用局部变量或栈上内存来保存临时数据。
4.使用信号量或互斥锁:为了保证多个线程之间的同步与互斥,可重入函数可以使用信号量或互斥锁来保护临界区。
可重入函数的用途可重入函数在多线程编程中具有重要的作用,它可以保证多个线程同时调用函数时不会出现竞争条件或数据不一致的问题。
下面是几个可重入函数的常见用途:1.管理共享资源:在多线程环境下,多个线程可能会同时访问共享资源,如内存、文件等。
可重入函数可以用来管理共享资源的访问,保证每个线程能够正确地访问资源而不会引起冲突。
2.实现线程安全的数据结构:可重入函数可以用来实现线程安全的数据结构,如队列、栈、链表等。
多个线程可以同时对数据结构进行操作,而不会导致数据结构的破坏或不一致。
3.处理信号:在多线程程序中,信号的处理通常是一个复杂的问题。
可重入函数可以用来处理信号,保证在信号处理函数中不会引起竞争条件或不确定的结果。
4.实现线程安全的库函数:可重入函数可以用来实现线程安全的库函数,使得库函数可以在多线程环境下被安全地调用。
信号与操作系统软中断
信号与操作系统软中断⼀、什么是信号?1.概述 信号量,是操作系统提供的⼀种⽤来传递特定消息的机制。
通过这种⽅式,操作系统可以将程序运⾏过程中发⽣的各种特殊情况转发给程序,并执⾏相应的服务函数。
信号的出现是随机的,通过轮询的⽅式进⾏判断会消耗⼤量的CPU资源,所以采⽤异步事件处理⽅式,程序告诉操作系统,当某个信号到来时,应该以怎样的⽅式去处理,也就是当检测到信号到来,操作系统调⽤相应的服务函数。
2.信号与软件中断 软件中断则是指由计算机软件,通过 int 等机器指令引起的 CPU 执⾏流程的临时转移过程。
⽐如系统调⽤,⽤户程序通过软件中断从⽤户态进⼊内核态,并使⽤内核提供的系统调⽤函数。
信号与软中断类似,当特定事件发⽣时,操作系统将对应的信号值发送给相关程序。
如果应⽤程序未设置信号对应的处理程序,操作系统会执⾏默认处理程序,通常是终⽌程序。
信号处理过程中,存在着 CPU 从⽤户程序到信号处理程序的执⾏流程转移。
3.在 C 代码中与信号交互 signal 相关信息可以参考。
signal 函数⽤于设置信号对应的处理程序,函数原型为 void (*signal( int sig, void (*handler) (int))) ,其中 sig 是信号类型,handler 是处理程序。
信号类型有以下⼏种:signal 和 raise 作为⼀对函数,分别负责关联信号与处理函数,发送信号。
需要注意的是,操作系统对信号处理函数的调⽤,可能发⽣在程序执⾏的任何时刻,对实际执⾏产⽣未知影响。
4.可重⼊函数 存在这样⼀种情况,当应⽤程序正在执⾏函数 A 时,有个信号出现,使操作系统打断正在执⾏的程序,进⼊到处理函数中,如果处理函数恰好也执⾏了 A 函数。
当处理函数结束后,继续执⾏之前未完成的 A 函数。
处理函数调⽤未执⾏完的函数是否对函数的执⾏产⽣影响呢? 不可重⼊函数:当函数未执⾏完被打断后,在处理函数中再次调⽤,会影响前⼀次的调⽤。
CC++之什么是可重入函数和不可重入函数
CC++之什么是可重⼊函数和不可重⼊函数可重⼊函数在实时系统的设计中,经常会出现多个任务调⽤同⼀个函数的情况。
如果这个函数不幸被设计成为不可重⼊的函数的话,那么不同任务调⽤这个函数时可能修改其他任务调⽤这个函数的数据,从⽽导致不可预料的后果。
那么什么是可重⼊函数呢?所谓可重⼊是指⼀个可以被多个任务调⽤的过程,任务在调⽤时不必担⼼数据是否会出错。
不可重⼊函数在实时系统设计中被视为不安全函数。
满⾜下列条件的函数多数是不可重⼊的:(1)函数体内使⽤了静态的数据结构;(2)函数体内调⽤了malloc()或者free()函数;(3)函数体内调⽤了标准I/O函数。
如何写出可重⼊的函数?在函数体内不访问那些全局变量,不使⽤静态局部变量,坚持只使⽤缺省态(auto)局部变量,写出的函数就将是可重⼊的。
如果必须访问全局变量,记住利⽤互斥信号量来保护全局变量。
或者调⽤该函数前关中断,调⽤后再开中断。
可重⼊函数可以被⼀个以上的任务调⽤,⽽不必担⼼数据被破坏。
可重⼊函数任何时候都可以被中断,⼀段时间以后⼜可以运⾏,⽽相应的数据不会丢失。
可重⼊函数或者只使⽤局部变量,即保存在CPU寄存器中或堆栈中;或者使⽤全局变量,则要对全局变量予以保护。
说法2:⼀个可重⼊的函数简单来说,就是:可以被中断的函数。
就是说,你可以在这个函数执⾏的任何时候中断他的运⾏,在任务调度下去执⾏另外⼀段代码⽽不会出现什么错误。
⽽不可重⼊的函数由于使⽤了⼀些系统资源,⽐如全局变量区,中断向量表等等,所以他如果被中断的话,可能出现问题,所以这类函数是不能运⾏在多任务环境下的。
基本上下⾯的函数是不可重⼊的(1)函数体内使⽤了静态的数据结构;(2)函数体内调⽤了malloc()或者free()函数;(3)函数体内调⽤了标准I/O函数。
把⼀个不可重⼊函数变成可重⼊的唯⼀⽅法是⽤可重⼊规则来重写他。
其实很简单,只要遵守了⼏条很容易理解的规则,那么写出来的函数就是可重⼊的。
01-06-多任务系统中的不可重函数使用注意事项
/**
/**
* @brief Receive in master mode an amount of data in no-blocking mode with Interrupt
* @param hi2c: pointer to a I2C_HandleTypeDef structure that contains
HAL_I2C_Master_Receive_IT(hi2c, Addr, Buf, Size)
{
……
hi2c->pBuffPtr = Buf;
Sensor2 中断
hi2c->XferSize =Size;
}
……
在此处执行时的变量为:
Addr 是 0x1A0 ;
Size 是 5. 地址是对应的,但
Size 已经不是 3 了。
Size=5(sensor2 value) 因此程序处理中会出错。
3.上面的问题解决方法:
上面多任务系统的 I2C 处理程序中出现了不可重入函数的多次调用问题,主要是 I2C_Read(…)为不可重入函数,但对它进行 了多次调用,使变量更改。 下面的办法可以解决多次调用问题,既对调用 I2C_Read(…)的动作加锁,使第二个 I2C 中断发生后不进行传参动作,如下图 所示:
独 享 资 源
4. 程序占用资源后要防止死锁发生
上面 I2C 程序处理中要注意不要发生死锁,假设在 I2C_Read(…) 函数里有一条指令是对 I2C 端口的状态进行判断,如 busy 状态,如果程序一直在等待中,就很容易发生死锁,下面图示是程序发生死锁的情况:
SensorTask1
I2C_Read1 占用资源并加锁,但资源一直没有释放
可重入函数
可重入函数
可重入函数是指:
1、它允许一个函数在已经开始其执行的情况下,再次被其本身调用。
2、把可重入函数等价于递归函数,它们都允许在函数体内通过调用它本身来实现循环。
不同的是,可重入函数的递归调用可以用多种可重入方式交叉进行,而不必等待前一调用结束后再开始新的调用。
3、可重入函数可提高程序实现的灵活性,不必额外定义变量来实现循环,而是在函数内部调用整个函数自身,它也可以实现多种重叠功能。
4、可重入函数可用于提供多种重叠性质编程,例如大量的并发任务处理、fork/join模式或服务器/客户端系统。
5、可重入函数可以更加复杂的编写,可以实现更复杂的类似位运算的实现,也可以通过可重入函数来处理复杂的控制,包括以树形结构实现的控制。
6、此外,可重入函数还可用于实现逐步累加,这样在编译器优化时可以提高运行效率,这也是和普通循环不同的地方。
7、可重入函数的另一个优点是它可以使程序更加稳定,因为也可以用它来进行错误控制,一旦出现错误,则能直接把调用过程重复执行,从而实现异常情况的处理。
最后,可重入函数还可以大大减少函数的代码量,并且使代码可重用性更强,使得程序更小巧、代码更加紧凑,更容易理解和维护。
异步信号安全函数
异步信号安全函数介绍异步信号安全函数是指在信号处理函数中可以安全调用的一类函数。
在程序运行中,当接收到某个信号时,操作系统会中断正在执行的程序,转而执行相应的信号处理函数。
由于信号处理函数运行在一个单独的上下文中,而不是主程序的上下文中,因此需要注意一些函数可能会产生的不安全行为,比如线程不安全、不可重入等。
异步信号安全函数的特点异步信号安全函数需要满足以下特点:1.线程安全:能够在多线程环境中正确地执行,不会导致数据竞争等问题。
2.可重入:能够在信号处理函数中被多次调用,不会产生不可预知的后果。
3.不会阻塞或占用过多资源:由于信号处理函数运行在一个中断上下文中,不能执行会造成阻塞的操作,否则会导致程序的响应延迟或无法响应。
常用的异步信号安全函数以下是一些常用的异步信号安全函数:1.malloc()和free():这两个函数用于动态分配和释放内存,它们在多线程环境下都是线程安全的。
2.getpid()和getppid():这两个函数用于获取当前进程和父进程的ID,它们是线程安全的。
3.snprintf()和sprintf():这两个函数用于将格式化的字符串输出到指定的缓冲区,它们是线程安全的。
4.sigaction():这个函数用于设置信号处理函数,它是可重入的。
5.write()和read():这两个函数用于在文件描述符上写入和读取数据,它们在多线程环境下是线程安全的。
注意事项在编写信号处理函数时,需要注意以下几点:1.使用volatile修饰全局变量:全局变量在信号处理函数中的访问可能与主程序的访问存在竞争条件,通过使用volatile修饰全局变量可以避免编译器对变量的优化,确保变量的值是可见的。
2.使用原子操作:在信号处理函数中对全局变量进行修改时,需要使用原子操作来确保操作的原子性,避免竞争条件。
3.避免使用不可重入函数:不可重入函数是指在函数的执行过程中使用了静态变量或全局变量等会影响函数的执行结果的变量。
可重入程序与不可重入程序的区分
可重⼊程序与不可重⼊程序的区分可重⼊性的英⽂关键词为Reentrancy,这⾥⾸先要区分可抢占这⼀概念。
在计算机⾥⾯,程序的运⾏可以说是⼀堆机器指令被放⼊CPU 进⾏顺序执⾏,但是操作系统为了更好地管理程序,就出现了各式各样的载体概念,⽐如说进程、线程、任务,究其本质,都是相对于“调度”这个操作,它们只是调度的粒度不⼀样。
如果我们在Linux或者windows下运⾏某个程序,它会依托于进程或者任务载体,由操作系统的内核对它进⾏调度执⾏。
可抢占式说明当前你执⾏的程序(任务)可以被其它程序抢占,意味着暂时失去对CPU资源的拥有。
⼀旦涉及到调度,就需要对任务之间进⾏同步和互斥了,⽐如常见的触发调度的技术如中断、任务睡眠、内核抢占等。
我在这⾥举的例⼦是可重⼊型函数,这也是可重⼊性的基础,其它引申出来的概念或者应⽤都是建⽴在它上⾯的。
可重⼊型函数任何时候都可以被中断,⼀段时间以后⼜可以运⾏,⽽相应数据不会丢失。
可重⼊型函数只使⽤局部变量,即变量保存在CPU寄存器中或栈中。
1void strcpy(char *dest, char *src)2 {3while (*dest++ = *src++) {4 (void)0;5 }6 *dest = NUL;7 }strcpy()是属于可重⼊型函数,多个任务或者线程调⽤它之后,并不会带来同步等问题。
如果说某个函数使⽤的变量涉及到全局变量或者堆,这样的函数就可能是不可重⼊型函数了,有可能得解决资源互斥的问题。
在嵌⼊式研发中,可以利⽤开关中断、任务优先级调整,信号量,⾃旋锁等多种⼿段来解决不可重⼊的问题。
举个常见的例⼦,使⽤全局变量的函数也并⾮⼀定是不可重⼊的,例如该全局变量已经被赋值且只读性质。
再举⼀个实际例⼦,某个函数的内部并不是所有代码都是不可重⼊的,如果有相当部分代码并不涉及资源互斥,这时候就可以采⽤加锁解锁的⽅式来进⾏任务之间同步,达到逻辑意义上的可重⼊。
单片机程序实现可重入和不可重入函数方法
单片机程序实现可重入和不可重入函数方法(最新版3篇)篇1 目录一、单片机程序实现可重入和不可重入函数方法的概述1.可重入函数和不可重入函数的概念2.单片机程序实现可重入和不可重入函数方法的意义3.实现可重入和不可重入函数方法的常用方法二、单片机程序实现可重入函数的详细步骤1.函数的可重入性设计原则2.函数参数的传递方式3.函数内部的变量定义和使用4.函数调用时的上下文切换和保存5.函数的返回值和异常处理三、单片机程序实现不可重入函数的详细步骤1.函数的不可重入性设计原则2.函数参数的传递方式3.函数内部的变量定义和使用4.函数调用时的上下文切换和保存5.函数的返回值和异常处理篇1正文一、单片机程序实现可重入和不可重入函数方法的概述1.可重入函数和不可重入函数的概念:可重入函数是指在多线程或多任务环境下,可以被多次调用的函数;不可重入函数则是指不能被多次调用的函数,因为多次调用会导致死锁或数据竞争等问题。
2.单片机程序实现可重入和不可重入函数方法的意义:在单片机系统中,多线程或多任务的情况时有发生,因此实现可重入和不可重入函数方法可以提高系统的稳定性和可靠性。
3.实现可重入和不可重入函数方法的常用方法:可采用同步机制(如锁、信号量等)来控制函数的访问权限,避免多线程或多任务之间的冲突。
同时,对于不可重入函数,需要注意避免使用共享变量或使用原子操作来保护临界区代码。
二、单片机程序实现可重入函数的详细步骤:1.函数的可重入性设计原则:可重入性设计原则包括以下几点:避免共享变量、使用同步机制、避免递归调用、避免使用全局变量等。
2.函数参数的传递方式:参数的传递方式会影响函数的可重用性和性能,因此需要选择合适的参数传递方式。
例如,如果参数是复杂数据结构,可以考虑使用传递指针的方式;如果参数是基本数据类型,可以考虑使用寄存器传递。
3.函数内部的变量定义和使用:在函数内部,需要使用局部变量来存储数据,避免使用全局变量或静态变量。
不可重入函数
不可重入函数
不可重入函数是指,当函数被调用后,调用者无法期望函数在结束后能继续工作,且不管它会产生什么效果还是是否被用户更新变量的值,这函数在结束后就不可能再被调用。
在互联网上,不可重入函数也非常常见,在很多软件和网络应用程序中,均包
含许多不可重入的函数,比如HTTP请求和TCP连接构建。
当用户发起HTTP请求或TCP连接时,服务器端就处于一种不可重入的函数状态,它无法同时处理两个以上
任务,只能处理当前任务,处理完成后进入不可重入函数状态,无法处理其他任务,直到下一个请求到达才能再次处理。
此外,不可重入函数还可用于实现多线程的同步和互斥,即线程池中的线程之
间要通过不可重入函数来达到同步和互斥的目的,以让多线程程序能够正确执行。
最后,不可重入函数也可以用于提高安全性。
不可重入函数能够有效地防止拒
绝服务攻击,当用户大量并发请求而带来的数据处理负荷过大时,不可重入函数可以限制用户对服务器的访问,从而防止用户恶意攻击,确保了服务器安全。
总之,不可重入函数是一类非常重要、多用的函数,它不仅能够用于实现多线
程的同步和互斥,也能够有效地防止拒绝服务攻击,保障用户的安全和服务器的稳定。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
可重入函数与不可重入函数
主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。
如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。
可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。
编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V 操作)等手段对其加以保护。
说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。
示例:假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。
那么如下函数不具有可重入性。
unsigned int example( int para )
{
unsigned int temp;
Exam = para; // (**)
temp = Square_Exam( );
return temp;
}
此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。
此函数应如下改进。
unsigned int example( int para ) {
unsigned int temp;
[申请信号量操作] //(1)
Exam = para;
temp = Square_Exam( );
[释放信号量操作]
return temp;
}
(1)若申请不到“信号量”,说明另外的进程正处于给Exam赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。
若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能
再使用本信号。
保证函数的可重入性的方法:
在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量),对于要使用的全局变量要加以保护(如采取关中断、信号量等方法),这样构成的函数就一定是一个可重入的函数。
VxWorks中采取的可重入的技术有:
* 动态堆栈变量(各子函数有自己独立的堆栈空间)
* 受保护的全局变量和静态变量
* 任务变量
--------------------------------------------------
在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。
如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。
那么什么是可重入函数呢?所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。
不可重入函数在实时系统设计中被视为不安全函数。
满足下列条件的函数多数是不可重入的:
1) 函数体内使用了静态的数据结构;
2) 函数体内调用了malloc()或者free()函数;
3) 函数体内调用了标准I/O函数。
(标准C I/O函数是典型的不可重入函数)
4)浮点运算对一般的编译器而言也是不可重入的,所以在一个可重入函数中使用浮点数及浮点运算是冒险的。
下面举例加以说明。
A. 可重入函数
void strcpy(char *lpszDest, char *lpszSrc)
{
while(*lpszDest++=*lpszSrc++);
*dest=0;
}
B. 不可重入函数1
char cTemp;//全局变量
void SwapChar1(char *lpcX, char *lpcY)
{
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//访问了全局变量
}
C. 不可重入函数2
void SwapChar2(char *lpcX,char *lpcY)
{
static char cTemp;//静态局部变量
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//使用了静态局部变量
}
问题1,如何编写可重入的函数?
答:在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用局部变量,写出的函数就将是可重入的。
如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
问题2,如何将一个不可重入的函数改写成可重入的函数?
答:把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写它。
其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。
1) 不要使用全局变量。
因为别的代码很可能覆盖这些变量值。
2) 在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。
完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”。
3) 不能调用其它任何不可重入的函数。
4) 谨慎使用堆栈。
最好先在使用前先OS_ENTER_KERNAL。
堆栈操作涉及内存分配,稍不留神就会造成益出导致覆盖其他任务的数据,所以,请谨慎使用堆栈!最好别用!很多黑客程序就利用了这一点以便系统执行非法代码从而轻松获得系统控制权。
还有一些规则,总之,时刻记住一句话:保证中断是安全的!
实例问题:曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?
unsigned int sum_int( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意,是static类型
for (index = 1; index <= base; index++)
sum += index;
return sum;
}
分析:所谓的函数是可重入的(也可以说是可预测的),即只要输入数据相同就应产生相同的输出。
这个函数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函数。
因此如果需要一个可重入的函数,一定要避免函数中使用static 变量,这种函数中的static变量,使用原则是,能不用尽量不用。
将上面的函数修改为可重入的函数,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto类型的变量,函数即变为一个可重入的函数。
当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto 类型,则返回为错指针。