任务实现过程中需要三个信号量:full、empty、mutex
#define N 2
OS_EVENT *full;
OS_EVENT *empty;
OS_EVENT *mutex;
其中,full用来记录充满的缓冲区数目,empty记录空的缓冲区数目,mutex用来确保生产者和消费者不会同时访问缓冲区。full的初始值为0,empty初始值为缓冲区中槽的数目,mutex初始值为1。
full = OSSemCreate (0);
empty = OSSemCreate (N);
mutex = OSSemCreate (1);
文件开始处声明了生产者和消费者任务所使用的栈和函数
static OS_STK producer_stk[TASK_STK_SIZE];
static OS_STK consumer_stk[TASK_STK_SIZE];
void producer(void *p_arg);
void consumer(void *p_arg);
生产者首先执行P(empty)操作,将空槽数量减一,然后执行P(mutex)操作,进入临界区,进入临界区后将数据放入缓冲区,此过程用打印字符串表示,然后执行V(mutex)操作离开临界区,P(full)将满槽的数目加一。
static void producer(void *p_arg)
{
int i;
int x_temp = (int) p_arg;
for (;;)
{
print_str("Producer!\n");
// your code
OSSemPend(empty,0,0);
OSSemPend(mutex,0,0);
print_str("The producer ");
char str[40]="x puts the data into the buffer\n";
str[0]=(char)(x_temp+48);
print_str(str);
OSSemPost(mutex);
OSSemPost(full);
OSTimeDly (100);
for(i=0;i<=10000;i++);
}
}
消费者首先执行P(full)操作将满槽数目减一,然后P(mutex)进入临界区,取出数据,然后V(mutex)操作离开临界区,V(empty)操作将空槽数量加一。
static void consumer(void *p_arg)
{
int i;
int x_temp =(int)p_arg;
for (;;)
{
print_str("Consumer!\n");
// your code
OSSemPend(full,0,0);
OSSemPend(mutex,0,0);
print_str("The consumer ");
char str[40]="x fetches the data from the buffer\n";
str[0]=(char)(x_temp+48);
print_str(str);
OSSemPost(mutex);
OSSemPost(empty);
OSTimeDly (100);
for(i=0;i<=10000;i++);
}
}
当生产者生产一个数据并放入缓冲区后,任务挂起等待,随后消费者从缓冲区中取出数据,由于任务等待时间相同,所以生产者生产一个数据就被消费掉了,如此循环往复。
若将生产者(producer)挂起时间增加,做如下修改:
OSTimeDly (100); -> OSTimeDly (200);
生产者挂起时间翻倍,当生产者生产一个数据后,消费者随后取出一个数据并将其消费掉,此时生产者还处于挂起状态,程序轮至消费者,输出Consumer!后,缓冲区为空,不能进入临界区,于是等待生产者生产数据。
每个叉子不能被同时拿起,因此叉子为互斥访问量,将其作为信号量。设置哲学家数量为5,并设置新的优先级与其他任务区分,为每个哲学家任务创建栈:
#define N 5
#define PHI_TASK_PRIO 3
OS_STK TaskStk[N][TASK_STK_SIZE];
OS_EVENT *forks[N];
通过动态分配内存为每个哲学家任务创建了一个PhilosopherParams
结构体对象,并在任务创建时传递给任务函数。这样可以确保每个任务都有自己独立的参数对象,避免共享同一个指针的问题。创建哲学家任务函数。
typedef struct {
INT8U philosopher_id;
} PhilosopherParams;
void philosopher(void *p_arg);
主函数任务创建,为每个哲学家分配栈空间并赋予不同优先级:
int main(void)
{
int i;
OSInit();
systick_init();
for (i = 0; i < N; i++) {
forks[i] = OSSemCreate(1);
PhilosopherParams *params = (PhilosopherParams *)malloc(sizeof(PhilosopherParams));
params->philosopher_id = i;
OSTaskCreate(philosopher, (void *)params, &TaskStk[i][TASK_STK_SIZE - 1], PHI_TASK_PRIO+i);
}
OSStart();
return 0;
}
首先让哲学家进入饥饿状态,哲学家试图拿起左边和右边的叉子,如果同时拿起了两边的叉子,则可以开始进食,使用时间延迟模拟哲学家进食过程,哲学家进食完毕后,放下左边和右边的叉子,离开临界区开始思考,使用时间延迟函数模拟思考过程。
void philosopher(void *p_arg)
{
INT8U i, j;
i = *((INT8U *)p_arg);
j = (i + 1) % N;
for (;;)
{
print_str("Philosopher ");
char str[20] = "x is hungry...\n";
str[0] = (char)(i + 48);
print_str(str);
OSSemPend(forks[i], 0, 0);
OSSemPend(forks[j], 0, 0);
print_str("Philosopher ");
char str1[20] = "x is eating...\n";
str1[0] = (char)(i + 48);
print_str(str1);
OSTimeDly(500);
print_str("Philosopher ");
char str2[40] = "x is done eating and begin thinking...\n";
str2[0] = (char)(i + 48);
print_str(str2);
OSSemPost(forks[i]);
OSSemPost(forks[j]);
OSTimeDly(500);
}
}
程序运行至哲学家0号,开始饥饿,此时0号左右叉子均可用,于是开始就餐;轮到哲学家1号,由于0号正在就餐,有一个叉子不可用,所以任务挂起;轮至哲学家2号,左右均可用,开始就餐;3号哲学家由于2号正在就餐所以任务挂起;4号哲学家由于1号正在就餐所以任务挂起。随后1号和2号哲学家就餐完毕开始思考,1号左右叉子释放,1号开始就餐;4号被1号占用的叉子释放,开始就餐。0号又开始饥饿,随后1号就餐完毕,2号开始饥饿,4号就餐完毕,3号和0号左右叉子释放,开始就餐…
定义读者数量N,RC为正在读或者即将读的进程数目,db控制对数据库的访问,信号量mutex控制对RC的访问
#define N 2
OS_EVENT *mutex;
OS_EVENT *db;
INT32U RC = 0;
为读者和写者分配栈空间和声明函数
static OS_STK reader_stk[N][TASK_STK_SIZE];
static OS_STK writer_stk[TASK_STK_SIZE];
void reader(void *p_arg);
void writer(void *p_arg);
主函数创建读者写者任务,这里创建了2个读者1个写者,写者优先级最高。
int main(void)
{
OSInit();
systick_init();
OSTaskCreate(writer, (void *)0,
&writer_stk[TASK_STK_SIZE-1], TASK1_PRIO);
OSTaskCreate(reader, (void *)1,
&reader_stk[0][TASK_STK_SIZE-1], TASK2_PRIO);
OSTaskCreate(reader, (void *)1,
&reader_stk[1][TASK_STK_SIZE-1], TASK3_PRIO);
//ASM_Switch_To_Unprivileged();
db=OSSemCreate(1);
mutex=OSSemCreate(1);
OSStart();
return 0;
}
读者首先通过P(mutex)操作获得对RC的互斥访问权,然后RC++,表示又多了一个读者,如果是第一个读者就执行P(db)操作获得对数据库的访问,然后通过V(mutex)操作释放对RC的互斥访问,访问数据然后通过P(mutex)获得对RC的互斥访问,RC–读者数减一,如果是最后一个读者就进行V(db)操作释放对数据库的互斥访问,接着释放对RC的互斥访问。
static void reader(void *p_arg)
{
int i;
for (;;)
{
OSSemPend(mutex, 0, 0);
RC++;
if(RC==1)
{
OSSemPend(db,0,0);
}
OSSemPost(mutex);
char str[20]="x is reading...\n";
str[0]=(char)(RC+48);
print_str(str);
OSTimeDly(100);
OSSemPend(mutex,0,0);
RC--;
if(RC==0)
{
OSSemPost(db);
}
OSSemPost(mutex);
OSTimeDly (200);
for(i=0;i<=10000;i++);
}
}
写者的操作比较简单,首先通过P(db)操作获取对数据库的互斥访问权,更新数据后通过V(db)操作释放该访问权。
static void writer(void *p_arg)
{
int i;
for (;;)
{
OSSemPend(db, 0, 0);
print_str("The writer is writting...\n");
OSSemPost(db);
OSTimeDly (200);
for(i=0;i<=10000;i++);
}
}
首先写者写入数据,然后读者1和读者2轮流读取数据,由于任务挂起时间相同,所以一直如此循环。