一、理解可重入函数
1、前言
在中断处理函数中调用了printf函数,因为中断处理函数调用了不可重入函数,导致中断丢失和系统位置错误,直接导致嵌入式linux系统应用进程中的所有线程停掉,进而导致看门狗进程得不到喂狗,设备重启。
2、什么是不可重入函数?
可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区、中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
满足下列条件的函数多数是不可重入的:
函数体内使用了静态(static)的数据结构;函数体内调用了malloc()或者free()函数;函数体内调用了标准I/O函数。
举例:
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;//使用了静态局部变量
}
3、为什么中断处理函数不能直接调用不可重入函数?
在多任务系统下,中断可能在任务执行的任何时间发生;如果一个函数的执行期间被中断后,到重新恢复到断点进行执行的过程中,函数所依赖的环境没有发生改变,那么这个函数就是可重入的,否则就不可重入的。
在中断前后不都要保存和恢复上下文吗,怎么会出现函数所依赖的环境发生改变了呢?我们知道中断时确实保存一些上下文,但是仅限于返回地址、CPU寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量、buffer等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。
在中断处理函数中调用有互斥锁保护的全局变量,如果恰好该变量正在被另一个线程调用,会导致中断处理函数不能及时返回,导致中断丢失等严重问题。并且在多线程环境中使用,在没有加锁的情况下,对同一段内存块进行并发读写,就会造成segmentfault/coredump之类的问题。总而言之,中断处理函数做的事情越简单越好。
4、如何写出可重入的函数?
在函数体内不访问那些全局变量;如果必须访问全局变量,记住利用互斥信号量来保护全局变量,或者调用该函数前关中断,调用后再开中断;不使用静态局部变量;坚持只使用缺省态(auto)局部变量;在和硬件发生交互的时候,切记关闭硬件中断,完成交互记得打开中断;不能调用任何不可重入函数;谨慎使用堆栈,最好先使用前先OS_ENTER_KERNAL。
二、任务
SYS/BIOS 系统内核组件;0~31优先级(默认0~15,优先级0被空闲线程使用,任务最低优先级为1);无限制数量(内存允许情况下);独立内存堆栈;更先进的调度技术(可以为等待某一时间的发生而被挂起;任务会被其他更高优先级的线程打断,所以图中的C函数必须是可重入函数);数量和状态可以在程序执行时动态的更改;优先级可以动态的修改。
每个任务都有自己独立的堆栈,因此任务是可以被挂起的。内核会为每个任务对象维护一份处理器关键寄存器的副本。由于每个任务都有自己独立的堆栈,所以任务的开销相对于其它线程来说较大,需要更大的内存。
任务线程内存消耗:
三、空闲(后台)线程
SYS/BIOS系统内核组件;非实时性线程;当且仅当没有任何其他线程(HWI/SWI/TASK运行时才会运行);所有空闲线程运行在同一个优先级;有数量限制;可以被任何其它线程随时抢占;默认任务堆栈,如果任务线程被禁用使用系统堆栈;一个接一个运行,每一个空闲线程函数都会从开始执行到结束,所有函数执行完成之后再从第一个函数开始执行,周而复始;不要在空闲线程的函数中使用阻塞语句(例如,Task_sleep()语句)。
四、BIOS系统的相关操作
BIOS系统具有良好的可移植性,可以在其他平台上使用。
任务模块:
空闲线程模块:
任务线程执行:
linux系统的非实时版本执行多线程采用的是轮询的方法,执行顺序是随机的;而BIOS系统采用的是抢占式的方式,也就是说每个线程的执行顺序一定是确定的。
任务线程挂钩函数:
任务线程配置:
如果任务在整个生命周期内都需要,一般通过静态配置。动态配置任务线程,灵活性高,且资源浪费少。
静态配置:
var Task = xdc.useModule('ti.sysbios.knl.Task');
var task0Params = new Task.Params();
task0Params.instance.name = "task0";
Program.global.task0 = Task.create("&taskMain",task0Params);
动态配置:
#include <ti/sysbios/knl/Task.h>
Task_Handle task;
Error_Block eb;
Error_init(&eb);
task = Task_create(taskMain,NULL,&eb);
if(task == NULL)
{
System_printf("Task_create() failed!\n");
}
如果Task_create中的参数为NULL,则默认使用.cfg中的相关参数,以下为任务参数。
任务线程相关函数:
任务线程状态:
任务中的堆栈非常重要,可以获取堆栈状态。
Task_Stat statbuf;
Task_stat(Task_self(),&statbuf);
if(statbuf.used > (statbuf.stackSize * 9 / 10))
{
System_printf("Over 90% of task's stack is in use.\n");
}
printf与System_printf的区别:
printf在运行时比System_printf更耗费系统资源,在使用时需要加\r\n,才可以立刻打印到终端。System_printf不一定立刻打印到终端,涉及一些交互资源的堆栈,如果需要立刻打印到终端,则需要加System_flush()语句。
printf("Exit taskMain()\r\n");
System_printf("Exit taskMain()\n");
System_flush();
五、实验结果与BIOS系统的相关配置
情况一:两个任务线程
/*
* task.c
*
* Created on: 2024-3-11
* Author: ouc
*/
#include <stdio.h>
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Memory.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/IHeap.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
// Task 1
void TaskMain1()
{
System_printf("Enter the TaskMain1!\n");
System_flush();
};
// Task 2
void TaskMain2()
{
System_printf("Enter the TaskMain2!\n");
System_flush();
};
// Idle 1
void IdleMain1()
{
System_printf("Enter the IdleMain1!\n");
System_flush();
};
// Idle 2
void IdleMain2()
{
System_printf("Enter the IdleMain2!\n");
System_flush();
};
int main()
{
Task_Handle task1;
Task_Handle task2;
Error_Block eb;
Error_init(&eb);
task1 = Task_create(TaskMain1,NULL,&eb);
task2 = Task_create(TaskMain2,NULL,&eb);
/*Task_stat(Task_self(),&statbuf);
if(statbuf.used > (statbuf.stackSize * 9 / 10))
{
System_printf("Over 90% of task's stack is in use.\n");
System_flush();
BIOS_exit(0);
};*/
if(task1 == NULL)
{
System_printf("Task_create() failed!\n");
System_flush();
BIOS_exit(0);
};
if(task2 == NULL)
{
System_printf("Task_create() failed!\n");
System_flush();
BIOS_exit(0);
};
BIOS_start();
return (0);
}
cfg Script代码:
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var Memory = xdc.useModule('xdc.runtime.Memory');
var Task = xdc.useModule('ti.sysbios.knl.Task');
var Idle = xdc.useModule('ti.sysbios.knl.Idle');
var BIOS = xdc.useModule('ti.sysbios.BIOS');
var heapMem0Params = new HeapMem.Params();
heapMem0Params.instance.name = "Systemheap";
heapMem0Params.size = 262144;
heapMem0Params.align = 8;
Program.global.Systemheap = HeapMem.create(heapMem0Params);
Memory.defaultHeapInstance = Program.global.Systemheap;
Program.sectMap["Systemheap"] = new Program.SectionSpec();
Program.sectMap["Systemheap"].loadSegment = "L2SRAM";
Program.sectMap["Systemheap"].loadAlign = undefined;
Program.sectMap["Systemheap"].runSegment = "L2SRAM";
Program.sectMap["Systemheap"].type = "COPY";
Program.sectMap["Systemheap"].loadAddress = undefined;
Program.sectMap["Systemheap"].fill = undefined;
Idle.idleFxns[0] = null;
Idle.idleFxns[1] = null;
Idle.idleFxns[2] = null;
运行结果:
分析:若创建任务线程时,未传入任务参数,即未配置任务优先级等参数,按照代码任务创建先后,运行线程。
情况二:两个空闲线程
/*
* task.c
*
* Created on: 2024-3-11
* Author: ouc
*/
#include <stdio.h>
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Memory.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/IHeap.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
// Task 1
void TaskMain1()
{
System_printf("Enter the TaskMain1!\n");
System_flush();
};
// Task 2
void TaskMain2()
{
System_printf("Enter the TaskMain2!\n");
System_flush();
};
// Idle 1
void IdleMain1()
{
System_printf("Enter the IdleMain1!\n");
System_flush();
};
// Idle 2
void IdleMain2()
{
System_printf("Enter the IdleMain2!\n");
System_flush();
};
int main()
{
/*Task_Handle task1;
Task_Handle task2;
Error_Block eb;
Error_init(&eb);
task1 = Task_create(TaskMain1,NULL,&eb);
task2 = Task_create(TaskMain2,NULL,&eb);
Task_stat(Task_self(),&statbuf);
if(statbuf.used > (statbuf.stackSize * 9 / 10))
{
System_printf("Over 90% of task's stack is in use.\n");
System_flush();
BIOS_exit(0);
};
if(task1 == NULL)
{
System_printf("Task_create() failed!\n");
System_flush();
BIOS_exit(0);
};
if(task2 == NULL)
{
System_printf("Task_create() failed!\n");
System_flush();
BIOS_exit(0);
};*/
BIOS_start();
return (0);
}
cfg Script代码:
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var Memory = xdc.useModule('xdc.runtime.Memory');
var Task = xdc.useModule('ti.sysbios.knl.Task');
var Idle = xdc.useModule('ti.sysbios.knl.Idle');
var BIOS = xdc.useModule('ti.sysbios.BIOS');
var heapMem0Params = new HeapMem.Params();
heapMem0Params.instance.name = "Systemheap";
heapMem0Params.size = 262144;
heapMem0Params.align = 8;
Program.global.Systemheap = HeapMem.create(heapMem0Params);
Memory.defaultHeapInstance = Program.global.Systemheap;
Program.sectMap["Systemheap"] = new Program.SectionSpec();
Program.sectMap["Systemheap"].loadSegment = "L2SRAM";
Program.sectMap["Systemheap"].loadAlign = undefined;
Program.sectMap["Systemheap"].runSegment = "L2SRAM";
Program.sectMap["Systemheap"].type = "COPY";
Program.sectMap["Systemheap"].loadAddress = undefined;
Program.sectMap["Systemheap"].fill = undefined;
Idle.idleFxns[0] = null;
Idle.idleFxns[1] = "&IdleMain1";
Idle.idleFxns[2] = "&IdleMain2";
运行结果1:
分析:不同空闲线程之间存在优先级先后的顺序
运行结果2:
情况三:一个任务线程、一个空闲线程
/*
* task.c
*
* Created on: 2024-3-11
* Author: ouc
*/
#include <stdio.h>
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Memory.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/IHeap.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
// Task 1
void TaskMain1()
{
System_printf("Enter the TaskMain1!\n");
System_flush();
};
// Task 2
void TaskMain2()
{
System_printf("Enter the TaskMain2!\n");
System_flush();
};
// Idle 1
void IdleMain1()
{
System_printf("Enter the IdleMain1!\n");
System_flush();
};
// Idle 2
void IdleMain2()
{
System_printf("Enter the IdleMain2!\n");
System_flush();
};
int main()
{
Task_Handle task1;
Error_Block eb;
Error_init(&eb);
task1 = Task_create(TaskMain1,NULL,&eb);
/*task2 = Task_create(TaskMain2,NULL,&eb);
Task_stat(Task_self(),&statbuf);
if(statbuf.used > (statbuf.stackSize * 9 / 10))
{
System_printf("Over 90% of task's stack is in use.\n");
System_flush();
BIOS_exit(0);
};*/
if(task1 == NULL)
{
System_printf("Task_create() failed!\n");
System_flush();
BIOS_exit(0);
};
/*if(task2 == NULL)
{
System_printf("Task_create() failed!\n");
System_flush();
BIOS_exit(0);
};*/
BIOS_start();
return (0);
}
cfg Script代码:
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var Memory = xdc.useModule('xdc.runtime.Memory');
var Task = xdc.useModule('ti.sysbios.knl.Task');
var Idle = xdc.useModule('ti.sysbios.knl.Idle');
var BIOS = xdc.useModule('ti.sysbios.BIOS');
var heapMem0Params = new HeapMem.Params();
heapMem0Params.instance.name = "Systemheap";
heapMem0Params.size = 262144;
heapMem0Params.align = 8;
Program.global.Systemheap = HeapMem.create(heapMem0Params);
Memory.defaultHeapInstance = Program.global.Systemheap;
Program.sectMap["Systemheap"] = new Program.SectionSpec();
Program.sectMap["Systemheap"].loadSegment = "L2SRAM";
Program.sectMap["Systemheap"].loadAlign = undefined;
Program.sectMap["Systemheap"].runSegment = "L2SRAM";
Program.sectMap["Systemheap"].type = "COPY";
Program.sectMap["Systemheap"].loadAddress = undefined;
Program.sectMap["Systemheap"].fill = undefined;
Idle.idleFxns[0] = null;
Idle.idleFxns[1] = "&IdleMain1";
Idle.idleFxns[2] = null;
运行结果:
文章来源:https://www.toymoban.com/news/detail-841812.html
分析:任务线程的优先级大于空闲线程文章来源地址https://www.toymoban.com/news/detail-841812.html
到了这里,关于线程(任务及空闲)与BIOS系统的交互配置问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!