一、信号量(Semaphores)
信号量有助于协调多任务应用程序的活动。任务最明显的通信方式是通过各种共享数据结构。由于单个进程中的所有任务都存在于单个线性地址空间中,因此在任务之间共享数据结构很简单。在不同任务上下文中运行的代码可以直接引用全局变量、线性缓冲区、环形缓冲区、链接列表和指针。然而,虽然共享地址空间简化了数据交换,但对内存的互锁访问对于避免数据损坏至关重要。确保独占访问共享资源的方法之一是信号量机制。此外,信号量可用于任务同步。
xenomai 中的信号量提供了快速的任务间通信的方式。信号量是处理互斥和任务同步需求的主要手段。一般来说,我们可以说:
信号量的接口由两个操作组成,V和P,它们影响与信号量相关的内部计数器。V来自荷兰语“Verhogen”,意为“增加”;P来自荷兰语“Proberen”,意思是“努力减少”。
“V”(信号、释放、给出)操作递增计数器并立即返回(释放信号)。
“P”操作(等待、获取、获取)减少计数器并立即返回,除非计数器已经为零,并且操作阻塞,直到另一个进程释放信号量(挂起信号)。
Xenomai中的信号量是计数信号量,它可以用来保护一个资源的多个实例,这与只使用0和1值的二进制信号量形成对比。二进制信号量在Xenomai中称为“互斥量”,并有一个单独的API。
二、信号量机制常用的API
信号量 Alchemy API.
1、创建一个计数信号量:
int rt_sem_create(RT_SEM *sem, const char *name, unsigned long icount, int mode)
其中icount是计数器的初始值,mode可以是S_FIFO或S_PRIO,这使得任务分别按FIFO(先进先出)顺序或优先级顺序挂起在信号量上。
2、挂起一个信号量,使计数器icount减1.
int rt_sem_p(RT_SEM *sem, RTIME timeout)
3、发出信号,使icount计数器加1:
int rt_sem_v(RT_SEM *sem)
4、广播一个信号量,即发出一个信号量,解除所有等待它的任务的阻塞(若任务间是同等级将按启动顺序,若设置了优先级将以优先级顺序执行任务的顺序):
int rt_sem_broadcast(RT_SEM *sem)
5、删除一个信号量:
int rt_sem_delete(RT_SEM *sem)
三、通过两个任务修改一个全局变量
任务要求:
两个任务(taskOne和taskTwo)竞相更改名为“global”的全局变量的值,程序的目标是切换全局变量的值(1和0)。TaskOne递增“global”值,taskTwo递减该值。
1、利用普通循环
程序3a:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <alchemy/sem.h>
#define ITER 10
static RT_TASK t1;
static RT_TASK t2;//建立两个实时任务
int global = 0;//定义一个global变量
void taskOne(void *arg)//任务一功能函数,递增global
{
int i;
for (i=0; i < ITER; i++) {
printf("I am taskOne and global = %d................\n", ++global);
}
}
void taskTwo(void *arg)//任务二功能,递减global
{
int i;
for (i=0; i < ITER; i++) {
printf("I am taskTwo and global = %d----------------\n", --global);
}
}
int main(int argc, char* argv[]) {
rt_task_create(&t1, "task1", 0, 1, 0);
rt_task_create(&t2, "task2", 0, 1, 0);
rt_task_start(&t1, &taskOne, 0);
rt_task_start(&t2, &taskTwo, 0);
return 0;
}
如图所示:没有使用信号量,那么是一个一个任务在循环中执行完整个循环后,再执行的递减,因为其中的任务函数是依赖启动顺序的。
2、加入信号量
在程序中为“加”方法与”减“方法各自创建一个信号量,在各自方法中将函数挂起,等待信号的来临,在死循环中一直发送信号使两种方法交替发生作用,使全局变量在0与1 之间变化。
以下是程序运行的代码以及结果。
程序3b:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <alchemy/sem.h>
static RT_TASK t1;
static RT_TASK t2;
RT_SEM sem1;
RT_SEM sem2;
int global = 0;
void taskOne(void *arg)
{
while(1)
{
rt_sem_p(&sem1,TM_INFINITE);
printf("I am taskOne and global = %d................\n", ++global);
}
}
void taskTwo(void *arg)
{
while(1)
{
rt_sem_p(&sem2,TM_INFINITE);
printf("I am taskTwo and global = %d----------------\n", --global);
}
}
int main(int argc, char* argv[]) {
rt_sem_create(&sem1,"MySemaphore1",0,S_FIFO);
rt_sem_create(&sem2,"MySemaphore2",0,S_FIFO);
rt_task_create(&t1, "task1", 0, 1, 0);
rt_task_create(&t2, "task2", 0, 1, 0);
rt_task_start(&t1, &taskOne, 0);
rt_task_start(&t2, &taskTwo, 0);
while(1)
{
rt_sem_v(&sem1);
rt_sem_v(&sem2);
rt_timer_spin(2e9);
}
return 0;
}
3、两种方法按优先级启动多任务
使用信号量调整练习2c中的程序,按任务优先级的顺序(而不是按启动顺序)执行任务。尝试两种解决方案,一种使用rt_sem_v,另一种使用rt_sem_broadcast,其中信号量用于发出任务开始的信号。
1.使用rt_sem_v()函数发出任务启动信号:
使用信号量赋予多个任务新的启动顺序:
程序3c:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <alchemy/sem.h>
#define NTASKS 5
RT_TASK task[NTASKS];
RT_SEM demo0;
// function to be executed by task
void demoa(void *arg) {
rt_sem_p(&demo0,TM_INFINITE);
RT_TASK_INFO curtaskinfo;
rt_task_inquire(NULL,&curtaskinfo);
rt_printf("Task name: %s \n", curtaskinfo.name);
rt_printf("Task priority is %d \n" ,curtaskinfo.prio);
}
void demob(void *arg) {
rt_sem_p(&demo0,TM_INFINITE);
RT_TASK_INFO curtaskinfo;
rt_task_inquire(NULL,&curtaskinfo);
rt_printf("Task name: %s \n", curtaskinfo.name);
rt_printf("Task priority is %d \n" ,curtaskinfo.prio);
}
void democ(void *arg) {
rt_sem_p(&demo0,TM_INFINITE);
RT_TASK_INFO curtaskinfo;
rt_task_inquire(NULL,&curtaskinfo);
rt_printf("Task name: %s \n", curtaskinfo.name);
rt_printf("Task priority is %d \n" ,curtaskinfo.prio);
}
void demod(void *arg) {
rt_sem_p(&demo0,TM_INFINITE);
RT_TASK_INFO curtaskinfo;
rt_task_inquire(NULL,&curtaskinfo);
rt_printf("Task name: %s \n", curtaskinfo.name);
rt_printf("Task priority is %d \n" ,curtaskinfo.prio);
}
void demoe(void *arg) {
rt_sem_p(&demo0,TM_INFINITE);
RT_TASK_INFO curtaskinfo;
rt_task_inquire(NULL,&curtaskinfo);
rt_printf("Task name: %s \n", curtaskinfo.name);
rt_printf("Task priority is %d \n" ,curtaskinfo.prio);
}
int main(int argc, char* argv[])
{
int i;
char str[20] ;
char name[]={'A','B','C','D','E'};
int priority[]={50,51,52,53,54};
rt_sem_create(&demo0,"MySemaphore0",0,S_PRIO);
for(i=0; i < NTASKS; i++)
{
//printf("start task : %c\n",name[i]);
sprintf(str,"task%d",i);
rt_task_create(&task[i],str,0,priority[i],0);
// rt_task_start(&task[i], &demo, 0);
}
rt_task_start(&task[0], &demoa, 0);
rt_task_start(&task[1], &demob, 0);
rt_task_start(&task[2], &democ, 0);
rt_task_start(&task[3], &demod, 0);
rt_task_start(&task[4], &demoe, 0);
while(1)
{rt_sem_v(&demo0);}
pause();
}
2.使用rt_sem_broadcast()函数发出启动信号:
程序以及运行结果如下:
程序3d:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <alchemy/sem.h>
#define NTASKS 5
RT_TASK task[NTASKS];
RT_SEM demo1;
// function to be executed by task
void demo(void *arg) {
rt_sem_p(&demo1,TM_INFINITE);
static int i=1;
rt_printf("第%d次调用demo方法 \n",i);
i=i+1;
RT_TASK_INFO curtaskinfo;
rt_task_inquire(NULL,&curtaskinfo);
rt_printf("Task name: %s \n", curtaskinfo.name);
rt_printf("Task priority is %d \n" ,curtaskinfo.prio);
}
int main(int argc, char* argv[])
{
int i;
char str[20] ;
char name[]={'A','B','C','D','E'};
int priority[]={50,51,52,53,54};
rt_sem_create(&demo1,"MySemaphore",0,S_FIFO);
for(i=0; i < NTASKS; i++)
{
//printf("start task : %c\n",name[i]);
sprintf(str,"task%d",i);
rt_task_create(&task[i],str,0,priority[i],0);
rt_task_start(&task[i], &demo, 0);
}
rt_sem_broadcast(&demo1);
}
如图所示,与我在上一篇文章中的2c程序相比,2c程序中的任务是以启动顺序所启动的,而这里是以按任务按各自的优先级顺序启动。使用信号量启动多任务,当任务的优先级相同的时候,将会按照任务创建启动的顺序执行任务,当任务优先级不同的时候将按照高优先级先启动后启动低优先级任务的顺序进行启动任务。
相比之下,在对于多任务按优先级启动使用第二种方法更加经济。