- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试在小型 Cortex-M3 上实现一个简单的增量队列,以便在将来安排一些任务。我 build 了一些东西,但我不认为它很优雅(我不经常写代码)。它也似乎有点片状由于不正确地使用了 volatile 说明符。
#include "deltaqueue.h"
#include "debug.h"
#include "interrupt.h"
//*****************************************************************************
//
// Define NULL, if not already defined.
//
//*****************************************************************************
#ifndef NULL
#define NULL ((void *)0)
#endif
//! Delta queue structure encapsulating a complete process entry into the queue
typedef struct dq{
struct dq * psPrev; //Address of previous queue entry
struct dq * psNext; //Address of next queue entry
unsigned long ulDelta; //Delta ticks (ticks relative to the next/previous process)
tProcessObject sProcess; //Process to be executed
} tDeltaQueueObject;
//! Contains the maximum number of processes in the queue at any one time (health indicator).
static unsigned long g_ulMaximumProcesses=0;
//! Contains the current number of processes in the queue (health indicator).
static unsigned long g_ulCurrentProcesses=0;
//! Contains the current number of executed processes (health indicator).
static unsigned long g_ulExecutedProcesses=0;
//! Contains the total number of processes scheduled since initialized (health indicator).
static unsigned long g_ulTotalProcesses=0;
//! Contains the accumulated tick count.
static volatile unsigned long g_ulSchedulerTickCount;
//! Simple counter used to generate process IDs.
static unsigned long g_ulPID=1;
//! Pointer to the first sleeping process.
static tDeltaQueueObject * volatile psSleeping;
//! Pointer to the processes ready for execution.
static tDeltaQueueObject * psReady;
//! Pointer to an available slot in the queue.
static tDeltaQueueObject * psAvailable;
//! Queue of processes.
static tDeltaQueueObject sDeltaQueue[QUEUE_MAX];
unsigned long SchedulerElapsedTicksCalc(unsigned long, unsigned long);
unsigned long GetProcessID(void);
tDeltaQueueObject * FreeEntry(void);
//****************************************************************************
//
//! Initializes the scheduler.
//!
//! This function resets the queue pointers.
//!
//! \return None.
//
//****************************************************************************
void SchedulerInit(void){
//Initialize queue pointers
psAvailable=&sDeltaQueue[0];
psSleeping=psAvailable;
psReady=psAvailable;
}
//****************************************************************************
//
//! Inserts supplied process into the queue.
//!
//! This function iterates the queue starting the sleep pointer and looks for
//! the insert location based on the supplied delay. As this is a delta queue,
//! the delay is decremented by the sleeping process' delta until a the delay
//! is less than that of the sleeping process. This then becomes the insertion
//! point. If there are no sleeping processes then the process is inserted
//! after the last ready process. If there are no sleeping processes or ready
//! processes then it's inserted and becomes the sole sleeping process.
//!
//! \param pf is the process to execute after the supplied delay.
//! \param ulDelay is the number of ticks to wait before executing the supplied
//! process.
//!
//! \return Process ID of inserted process or zero if unable to insert.
//
//****************************************************************************
unsigned long SchedulerInsert(void (*pf)(void),unsigned long ulDelay){
static unsigned long ulBeginCount;
static unsigned long ulEndCount;
ASSERT(psSleeping);
ASSERT(psAvailable);
//Pick off current systick count to calculate execution time
ulBeginCount=(*((volatile unsigned long *)(NVIC_ST_CURRENT)));
//CRITICAL SECTION BEGIN
IntMasterDisable();
//Begin iterating at the current sleep pointer
tDeltaQueueObject * p=(void *)psSleeping;
tDeltaQueueObject * q;
//Adjust health indicators
g_ulTotalProcesses++;
if(++g_ulCurrentProcesses>g_ulMaximumProcesses)
g_ulMaximumProcesses=g_ulCurrentProcesses;
//Loop through each sleeping process starting at the current
//sleep pointer and ending when the next pointer of any is
//equivalent to the available pointer
while(p!=psAvailable){
//If the delay is greater than the current queue item delay,
//compute the delta for the inserted process and move on
if(p->ulDelta <= ulDelay){
ulDelay-=p->ulDelta;
}
//Otherwise, this is the point to insert the new process
else{
//Insert the new process before the current queue entry
q=FreeEntry();
ASSERT(q); //TODO: Exit gracefully when no room
q->psNext=p;
q->psPrev=p->psPrev;
//Adjust previous and next pointers on each side of the new process
p->psPrev->psNext=q;
p->psPrev=q;
//Set deltas for inserted queue entry and the supplied queue entry
p->ulDelta-=ulDelay;
q->ulDelta=ulDelay;
//Set the function pointer for the new process and obtain a unique
//process ID
q->sProcess.pf=pf;
q->sProcess.ulPID=GetProcessID();
//Adjust the sleep pointer if the insert
//happens before it
if(p==psSleeping)
psSleeping=q;
//CRITICAL SECTION END
IntMasterEnable();
//Pick off current systick count to calculate execution time
ulEndCount=(*((volatile unsigned long *)(NVIC_ST_CURRENT)));
return q->sProcess.ulPID;
}
//Move to next
p=p->psNext;
}
//If here, the list is either empty or the delay is larger than the
//sum of all the delays in the queue and so it should be appended
//to the end of the queue
psAvailable->ulDelta = ulDelay;
psAvailable->sProcess.pf=pf;
psAvailable->sProcess.ulPID=GetProcessID();
q=psAvailable;
//Increment the available pointer
psAvailable=FreeEntry();
ASSERT(psAvailable);
psAvailable->psPrev=q;
q->psNext=psAvailable;
psAvailable->psNext=NULL;
//CRITICAL SECTION END
IntMasterEnable();
//Pick off current systick count to calculate execution time
ulEndCount=(*((volatile unsigned long *)(NVIC_ST_CURRENT)));
return q->sProcess.ulPID;
}
//****************************************************************************
//
//! Runs any processes which are ready for execution.
//!
//! This function is usually called in the main loop of the application
//! (anywhere NOT within an interrupt handler). It will iterate the queue
//! and execute any processes which are not sleeping (delta is zero).
//!
//! \return None.
//
//****************************************************************************
void SchedulerRunTask(void){
tDeltaQueueObject * p;
ASSERT(psReady);
//Run tasks until we bump up against the sleeping tasks
while(psReady!=psSleeping){
//Adjust health indicators
g_ulCurrentProcesses--;
g_ulExecutedProcesses++;
//Execute task
if(psReady->sProcess.pf)
(psReady->sProcess.pf)();
p=psReady->psNext;
//Clear task
psReady->sProcess.pf=NULL;
psReady->sProcess.ulPID=0;
psReady->psNext=NULL;
psReady->psPrev=NULL;
psReady->ulDelta=0;
//Increment ready pointer
psReady=p;
}
}
//****************************************************************************
//
//! Manages sleeping processes in the queue.
//!
//! This function is to be called by the system tick interrupt (at a given
//! interval). When called, the sleeping tasks' delta is decremented and the
//! sleep pointer is adjusted to point at the next sleeping task (if changed).
//!
//! \return None.
//
//****************************************************************************
void SchedulerTick(void){
ASSERT(psSleeping);
//Increment tick counter
g_ulSchedulerTickCount++;
//Adjust sleeping task (never roll past zero)
if(psSleeping->ulDelta)
psSleeping->ulDelta--;
//Push the sleep pointer until a non-zero delta.
//Multiple processes can expire on one tick.
while(!psSleeping->ulDelta && psSleeping!=psAvailable){
psSleeping=psSleeping->psNext;
}
}
//****************************************************************************
//
//! Searches the queue for a free slot.
//!
//! This function iterates the entire queue looking for an open slot.
//!
//! \return Pointer to the next free DeltaQueueObject or 0 if no free space
//! available.
//
//****************************************************************************
tDeltaQueueObject * FreeEntry(){
unsigned long i;
//Iterate entire queue
for(i=0; i<QUEUE_MAX; i++){
//Look for a free slot by examining the contents
if(!(sDeltaQueue[i].psNext) && !(sDeltaQueue[i].psPrev) && !(sDeltaQueue[i].sProcess.ulPID) && !(sDeltaQueue[i].ulDelta) && !(sDeltaQueue[i].sProcess.pf))
return &sDeltaQueue[i];
}
//If we are here, there are no free spots in the queue
ASSERT(1);
return NULL;
}
//****************************************************************************
//
//! Produces a unique process ID.
//!
//! This function simply returns the next PID available.
//!
//! \todo Keep a list of unexpired PIDs so that it can be guaranteed unique
//! must have before creating remove function
//!
//! \return A unique process ID.
//
//****************************************************************************
unsigned long GetProcessID(void){
//PID can never be zero, catch this case
if(!g_ulPID)
g_ulPID=1;
return g_ulPID++;
}
我所拥有的背后的想法是存在一个已填充的静态缓冲区与增量队列对象。每个增量队列对象都有指向前一个/下一个增量队列对象,相对于前一个任务的延迟和一些进程信息(进程 ID 和函数指针)。有3个全局指针、就绪指针、 sleep 指针和可用指针指针。就绪指针指向要执行的任务列表。这sleep 指向任务列表的指针,这些任务......好吧......睡着了但还没有准备好执行。可用指针基本上指向那里的结尾是可用插槽。这些指针只会向前移动。当一个被推与另一个相对,那个“子队列”是空的。例如,当准备指针等于 sleep 指针,没有就绪任务。
所以,一个例子可能是这样的:
最初指针看起来像这样..
Pointers Slot # Delta
RP,SP,AP -> Slot 1 0
一个任务以 50 毫秒的延迟插入,队列现在看起来像...
Pointers Slot # Delta
RP,SP -> Slot 1 50
AP -> Slot 2 0
几个滴答声过去,另一个任务被插入,延迟 10 毫秒...
Pointers Slot # Delta
RP,SP -> Slot 3 10
-> Slot 1 38
AP -> Slot 2 0
20 个滴答声过去了,我们有...
Pointers Slot # Delta
RP -> Slot 3 0
SP -> Slot 1 18
AP -> Slot 2 0
SchedulerTick()
由 systick 中断以 1 毫秒的速率调用。SchedulerRun()
从应用程序的主循环中调用(当它没有做任何其他事情)所以我的系统中断很短。SchedulerInsert()
根据需要调用以安排任务。
所以,这就是我使用上面的代码的目的。现在,我的问题...
1) 我将 psSleeping
指定为 volatile 指针,因为它在 SchedulerTick()
中被修改。我确信它是必需的,但我的用法正确吗?指针是声明为 volatile 还是指向的指针声明为 volatile。
2) SchedulerTick()
和 SchedulerRun()
函数非常简单,但 SchedulerInsert()
变得相当困惑。大多数困惑是由于插入的任务可以放在 sleep 指针之前,这意味着 SchedulerTick()
不再专门写入它,所以我必须在这样做时禁用中断.此外,插入中似乎存在一些错误(大概)导致 SchedulerTick()
在 while 循环中停止,因为从未达到 psAvailable
。这个错误很少发生......我不能在单步执行时重复它。可能与 volatile 声明有关?
有什么想法吗?
最佳答案
我的建议是您重新考虑您是否真的需要在中断处理程序中进行任何实际的列表处理。
据我所知,您可以通过仅跟踪经过的滴答并使用它们来唤醒您之前在中断之外访问 sleep 尾指针的任何地方的 sleep 任务来获得类似的结果。
例如沿着这些方向的东西:
// Only bumb the tick counter from within interrupts
void SchedulerTick(void) {
g_ulSchedulerTickCount++;
}
// Use the number of elapsed ticks since the last call wake up processes for execution.
// Returns the first task that's still sleeping
tDeltaQueueObject *SchedulerStillSleeping(void) {
static unsigned long lastTick;
unsigned long currentTick = g_ulSchedulerTickCount;
signed long elapsedTicks = currentTick - lastTick;
lastTick = currentTick;
for(; psSleeping != psAvailable; psSleeping = psSleeping->psNext) {
if(psSleeping->ulDelta > elapsedTicks)
psSleeping->ulDelta -= elapsedTicks;
break;
}
elapsedTicks -= psSleeping->ulDelta;
psSleeping->ulDelta = 0;
}
return psSleeping;
}
// Reassess the set of sleeping processes by calling the StillSleeping function anywhere
// you would previously have polled the list head
void SchedulerRunTask(void) {
while(psReady != SchedulerStillSleeping()) {
.
.
.
}
}
unsigned long SchedulerInsert(...) {
.
.
.
tDeltaQueueObject *p = SchedulerStillSleeping();
while(p != psAvailable) {
.
.
.
}
}
关于c - 增量队列 - 嵌入式调度程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7427614/
我是 C 语言新手,我编写了这个 C 程序,让用户输入一年中的某一天,作为返回,程序将输出月份以及该月的哪一天。该程序运行良好,但我现在想简化该程序。我知道我需要一个循环,但我不知道如何去做。这是程序
我一直在努力找出我的代码有什么问题。这个想法是创建一个小的画图程序,并有红色、绿色、蓝色和清除按钮。我有我能想到的一切让它工作,但无法弄清楚代码有什么问题。程序打开,然后立即关闭。 import ja
我想安装screen,但是接下来我应该做什么? $ brew search screen imgur-screenshot screen
我有一个在服务器端工作的 UDP 套接字应用程序。为了测试服务器端,我编写了一个简单的 python 客户端程序,它发送消息“hello world how are you”。服务器随后应接收消息,将
我有一个 shell 脚本,它运行一个 Python 程序来预处理一些数据,然后运行一个 R 程序来执行一些长时间运行的任务。我正在学习使用 Docker 并且我一直在运行 FROM r-base:l
在 Linux 中。我有一个 c 程序,它读取一个 2048 字节的文本文件作为输入。我想从 Python 脚本启动 c 程序。我希望 Python 脚本将文本字符串作为参数传递给 c 程序,而不是将
对于一个类,我被要求编写一个 VHDL 程序,该程序接受两个整数输入 A 和 B,并用 A+B 替换 A,用 A-B 替换 B。我编写了以下程序和测试平台。它完成了实现和行为语法检查,但它不会模拟。尽
module Algorithm where import System.Random import Data.Maybe import Data.List type Atom = String ty
我想找到两个以上数字的最小公倍数 求给定N个数的最小公倍数的C++程序 最佳答案 int lcm(int a, int b) { return (a/gcd(a,b))*b; } 对于gcd,请查看
这个程序有错误。谁能解决这个问题? Error is :TempRecord already defines a member called 'this' with the same paramete
当我运行下面的程序时,我在 str1 和 str2 中得到了垃圾值。所以 #include #include #include using namespace std; int main() {
这是我的作业: 一对刚出生的兔子(一公一母)被放在田里。兔子在一个月大时可以交配,因此在第二个月的月底,每对兔子都会生出两对新兔子,然后死去。 注:在第0个月,有0对兔子。第 1 个月,有 1 对兔子
我编写了一个程序,通过对字母使用 switch 命令将十进制字符串转换为十六进制,但是如果我使用 char,该程序无法正常工作!没有 switch 我无法处理 9 以上的数字。我希望你能理解我,因为我
我是 C++ 新手(虽然我有一些 C 语言经验)和 MySQL,我正在尝试制作一个从 MySQL 读取数据库的程序,我一直在关注这个 tutorial但当我尝试“构建”解决方案时出现错误。 (我正在使
仍然是一个初学者,只是尝试使用 swift 中的一些基本函数。 有人能告诉我这段代码有什么问题吗? import UIKit var guessInt: Int var randomNum = arc
我正在用 C++11 编写一个函数,它采用 constant1 + constant2 形式的表达式并将它们折叠起来。 constant1 和 constant2 存储在 std::string 中,
我用 C++ 编写了这段代码,使用运算符重载对 2 个矩阵进行加法和乘法运算。当我执行代码时,它会在第 57 行和第 59 行产生错误,非法结构操作(两行都出现相同的错误)。请解释我的错误。提前致谢:
我是 C++ 的初学者,我想编写一个简单的程序来交换字符串中的两个字符。 例如;我们输入这个字符串:“EXAMPLE”,我们给它交换这两个字符:“E”和“A”,输出应该类似于“AXEMPLA”。 我在
我需要以下代码的帮助: 声明 3 个 double 类型变量,每个代表三角形的三个边中的一个。 提示用户为第一面输入一个值,然后 将用户的输入设置为您创建的代表三角形第一条边的变量。 将最后 2 个步
我是新来的,如果问题不好请见谅 任务:将给定矩阵旋转180度 输入: 1 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 输出: 16 15 14 13 12 11
我是一名优秀的程序员,十分优秀!