嵌入式实时操作系统的设计与开发(九)

news2024/11/17 20:17:50

同步机制

aCoral信号量机制不仅可以实现临界资源互斥访问,控制系统中临界资源多个实例的使用,还可以用于维护线程之间、线程和中断之间的同步。

当信号量用来实现同步时,起始值为0,如一个线程正在等待某个I/O操作,当该I/O操作完成后,中断服务程序(或另一个线程)发出信号量,该线程得到信号量后才能继续往下执行。也就是说,某个线程将一直处于等待状态,除非获取了其它线程发给它的信号量。
在实现时,资源的实例数用“1-semNum”来表示,0代表有一个资源,-1代表有两个资源,1代表已经没有资源,且有一个任务在等待该资源。

  • 互斥的信号量初始值在创建时设置为1,1-semNum=0,是小于等于0,表明当前没有线程获取该信号量。
  • 同步的信号量初始值为0,1-semNum>=0,表明所同步的事件尚未发生。

通信机制

线程之间、线程与中断服务子程序之间还需要通信机制,也就是信息交互。
例如,线程A在执行过程中需要使用线程B(或中断服务子程序)产生的数据,那么B如何将数据传送给A呢?

邮箱

邮箱(MailBox)可以用来实现线程之间同步和通信功能。
在这里插入图片描述
假设线程2从邮箱中接收线程1发过来的信息(该信息被称为邮件)。线程2在同步点从邮箱中接收消息,如果线程1此时还没有执行到同步点,则邮箱中是不会有消息的,此时线程2会将自己挂起;当线程1执行到同步点时,会向邮箱中发送一条消息,此时就会激活挂起的线程2,继续执行。
一个线程将需要交互的信息发送到邮箱,另一个线程从邮箱中读出。
邮箱机制依赖于事件控制块acoral_evt_t,acoral_evt_t data成员就是用来挂载线程间传递的信息。

创建一个邮箱

acoral_evt_t *acoral_mbox_create(){
	acoral_evt_t *event;
	event = acoral_alloc_evt();
	if(NULL == event)
		return NULL;
	event->type = ACORAL_EVENT_MBOX;
	event->count = 0x00000000;
	event->data = NULL;
	acoral_evt_init(event);
	return event;
}

发送信息到邮箱

向邮箱中发送信息的接口为acoral_mbox_send(),传入的参数为先前创建的邮箱的地址(类型为邮箱的事件控制块)和指向信息的指针。

acoral_u32 acoral_mbox_send(acoral_evt_t *event,void *msg){
	acoral_thread_t *thread;
	if(NULL == event){
		return MBOX_ERR_NULL;
	}
	if(event->type != ACORAL_EVENT_MBOX)
		return MBOX_ERR_TYPE;
	HAL_ENTER_CRITICAL();
	if(event->data != NULL){
		HAL_EXIT_CRITICAL();
		return MBOX_ERR_MES_EXIST;
	}
	event->data = msg;
	thread = acoral_evt_high_thread(event);
	// 没有等待队列
	if(NULL == thread){
		HAL_EXIT_CRITICAL();
		return MBOX_SUCCED;
	}
	timeout_queue_del(thread);
	acoral_evt_queue_del(thread);
	acoral_rdy_thread(thread);
	HAL_EXIT_CRITICAL();
	acoral_sched();
	return MBOX_SUCCED;
}

从邮箱获取信息

邮箱中没有信息时,将自己挂到邮箱的等待队列;如果邮箱中有消息,则取出消息。

HAL_ENTER_CRITICAL()if(event->data == NULL){
	cur = acoral_running_thread;
	acoral_evt_queue_add(event,cur);
	acoral_unrdy_thread(cur);
	HAL_EXIT_CRITICAL();
	acoral_sched();
}

消息

消息机制和邮箱机制很类似,但邮箱一般只能容纳一条信息,消息则会包含一系列消息。
在这里插入图片描述
系统定义了一个全局变量g_msgctr_header,通过它可以查找到任一已创建的消息容器。每一个消息容器都可以根据其参数性质来实现不同的通信方式。(如1VS1,1VSn,nVSn,nVS1等)。这里的消息容器只是一个线程间的通信结构acoral_msgctr_t,是消息的存储容器,一个消息容器可以通过它的消息链指针成员,挂载多条消息。而消息结构acoral_msg_t是消息的容器,一个消息结构包含一条消息。

/*消息容器*/
typedef struct{
	acoral_res_t res;
	acoral_8 *name;
	acoral_u8 type;
	acoral_list_t msgctr_list; //全局消息列表
	acoral_u32 count; //消息容器上已挂消息的数量
	acoral_u32 wait_thread_num; //消息容器上已挂等待线程的数量
	acoral_list_t waiting; //等待线程链指针
	acoral_list_t msglist; //消息链指针
}acoral_msgctr_t;
/*消息*/
typedef struct{
	acoral_res_t res;
	acoral_list_t msglist; //消息链指针,用于挂载到消息容器
	acoral_u32 id; //消息标识
	acoral_u32 n;//消息被接受次数,每被接受一次减一,直到0为止
	acoral_u32 tt; //消息最大声明周期,ticks计数
	void *data; //消息指针
}acoral_msg_t;

创建消息容器

acoral_msgctr_t* acoral_msgctr_create(acoral_u32 *err){
	acoral_msg_ctr_t *msgctr;
	msgctr = acoral_malloc_msgctr();
	if(msgctr == NULL){
		return NULL;
	}
	msgctr->name = NULL;
	msgctr->type = ACORAL_MSGCTR;
	msg->count = 0;
	msgctr->wait_thread_num = 0;
	acoral_init_list(&msgctr->msgctr_list);
	acoral_init_list(&msgctr->msglist);
	acoral_init_list(&msgctr->waiting);
	acoral_list_add2_tail(&msgctr->msgctr_list,&(g_msgctr_header.head)); // 将初始化后的消息容器挂到全局消息容器队列g_msgctr_header上。
}

msgctr = acoral_alloc_msgctr(); 申请一片内存空间,从内存资源池中获取一个资源对象供消息容器结构acoral_msgctr_t使用。

acoral_msgctr_t *acoral_alloc_msgctr()
{
	return (acoral_msgctr_t *)acoral_get_res(&acoral_msgctr_pool_ctr);
}
extern acoral_queue_t g_msgctr_header;

在g_msgctr_header在message.h中定义,extern acoral_queue_t g_msgctr_header;这样就可以在任何需要的地方找到这个消息容器。

创建消息

消息容器并不直接包含消息,在消息容器之下,还有一层是消息结构,因而消息的创建,先是创建消息结构,再将消息挂到消息结构。

acoral_msg_t* acoral_msg_create(acoral_u32 n,acoral_u32 *err,acoral_u32 id,acoral_u32 nTtl, codi *data){
	acoral_msg_t *msg;
	msg = acoral_alloc_msg();
	if(msg == NULL)
		return NULL;
	msg->id = id;
	msg->n = n;
	msg->ttl = nTtl;
	msg->data = data;
	acoral_init_list(&msg-msglist);// 将初始化后的消息挂到消息队列上
	return msg;
}

发送消息

消息发送时,首先将包含消息的消息结构挂到消息容器的消息链上,然后判断是否有等待的线程,如果有的话,则唤醒最高优先级的线程。

acoral_u32 acoral_msg_send(acoral_msgctrl_t* msgctr,acoral_msg_t *msg){
	acoral_enter_critical();
	if(NULL == msgctr){
		acoral_exit_critical();
		return MSG_ERR_NULL;
	}
	if(NULL == msg){
		acoral_exit_critical();
		return MSG_ERR_NULL;
	}
	/*消息数限制*/
	if(ACORAL_MESSAGE_MAX_COUNT <= msgctr->count){
		acoral_exit_critical();
		return MSG_ERR_COUNT;
	}
	/*增加消息,将包含消息的消息结构挂到消息容器的消息链上*/
	msgctr->count++;
	msg->ttl += acoral_get_ticks(&msg->msglist,&msgctr->msglist);
	/*唤醒等待*/
	if(msgctr->wait_thread_num > 0){
		/*唤醒最高优先级线程*/
		wake_up_thread(&msgctr->waiting);
		msgctr->wait_thread_num--;
	}
	acoral_exit_critical();
	acoral_sched();
	return MSGCTR_SUCCED;
}

消息接收

消息接收函数的接口为void *acoral_msg_recv(acoral_msgctr_t *msgctr, acoral_u32 id,acoral_time timeout,acoral_u32 *err),需要的参数是消息容器指针msgctr,指出要从哪个消息容器接收消息,指定接收消息的ID,超时时间和错误返回码。

void *acoral_msg_recv(acoral_msgctr_t *msgctr, acoral_u32 id,acoral_time timeout,acoral_u32 *err){
	void *data;
	acoral_list_t *p,*q;
	acoral_msg_t *pmsg;
	acoral_thread_t *cur;
	if(acoral_intr_nesting > 0){
		*err = MSG_ERR_INTR;
		return NULL;
	}
	if(NULL == msgctr){
		*err = MSG_ERR_NULL;
		return NULL;
	}
	cur = acoral_cur_thread;
	acoral_enter_critical();
	if(timeout > 0){ //需要进行超时处理,以ms为单位
		cur->delay = TIME_TO_TICKS(timeout);
		timeout_queue_add(cur);
	}
	while(1){
		p = &msgctr->msglist; 
		q = p->next;
		for(;p!=q;q=q->next){
			pmsg = list_entry(q,acoral_msg_t,msglist);
			if((pmsg->id==id)&&(pmsg->n>0)){
				pmsg->n--;
				timeout_queue_del(cur);
				data = pmsg->data;
				acoral_list_del(q);
				acoral_release_res((acoral_res_t *)pmsg);
				msgctr->count--;
				acoral_exit_critical();
				return data;
			}
		}
		/*没有接收消息*/
		msgctr->wait_thread_num++;
		acoral_msgctr_queue_add(msgctr,cur);
		acoral_unrdy_thread(cur);
		acoral_exit_critical();
		acoral_sched();
		/*看是否超时*/
		if(timeout > 0 && (acoral_32)cur->delay <= 0)
			break;
	}
	/*超时退出*/
	if(msgctr->wait_thread_num > 0)
		msgctr->wait_thread_num--;
	acoral_list_del(&cur->waiting);
	acoral_exit_critical();
	*err = MSG_ERR_TIMEOUT;
	return NULL;
}

删除消息容器

/*pmsgctr消息容器指针,flag为参数指针,用于区分是否强制删除消息容器,如果指定强制删除,则会先将消息结构释放,然后将等待线程全部就绪,最后释放该消息容器结构。如果不指定强制删除,只有在容器上没有挂载消息和无等待线程时才会将其释放,否则返回错误。*/
acoral_u32 acoral_msg_del(acoral_msgctr_t *pmsgctr,acoral_u32 flag){
	acoral_list_t *p,*q;
	acoral_thread_t *thread;
	acoral_msg_t *pmsg;
	if(NULL == pmsgctr)
		return MST_ERR_NULL;
	//非强制删除
	if(flag == MST_DEL_UNFORCE){
		if((pmsgctr->count>0) || (pmsgctr->wait_thread_num > 0))
			return MST_ERR_UNDEF;
		else{
			acoral_release_res((acoral_res_t *)pmsgctr);
		}
	}else{
		//强制删除
		if(pmsgctr->wait_thread_num > 0){
			p = &msgctr->waiting;
			q = p->next;
			for(;q!=p;q=q->next){
				thread=list_entry(q,acoral_thread_t,waiting);
				acoral_rdy_thread(thread);
			}
		}
		//释放消息结构
		if(msgctr->count>0){
			p = &msgctr->msglist;
			q = p->next;
			for(;p!=q;q=q->next){
				pmsg = list_entry(q,acoral_msg_t,msglist);
				acoral_list_del(q);
				acoral_release_res((acoral_res_t *)pmsg);			}
		}
		acoral_release_res((acoral_res_t *)pmsgctr);
	}
}
/*删除消息*/
acoral_u32 acoral_msg_del(acoral_msg_t *msg){
	if(NULL != pmsg){
		acoral_release_res((acoral_res_t *)pmsg);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【外贸小知识】通过whatsapp获取流量的几种小方法

相信做外贸的小伙伴们对于是whatsap比较熟悉的&#xff0c;都想通过whatsapp来获取更多流量&#xff0c;更多用户。今天我们花漾灵动小编就给大家汇总了通过whatsapp获取流量的几种小方法&#xff0c;希望能对新手小白有点作用哦&#xff01;通过whatsapp获取流量的几种小方法1…

【PyTorch深度学习实践】07_Dataset和Dataloader

文章目录1. Epoch&#xff0c;Iteration&#xff0c;Batch-Size2. Dataset 和 Dataloader2.1 Dataset2.2 Dataloader2.2.1 例子2.2.2 enumerate函数3. 完整代码1. Epoch&#xff0c;Iteration&#xff0c;Batch-Size 参考博客 2. Dataset 和 Dataloader 参考博客 功能概览 2…

2023年浙江建筑八大员(标准员)精选真题题库及答案

百分百题库提供建筑八大员&#xff08;标准员&#xff09;考试试题、建筑八大员&#xff08;标准员&#xff09;考试真题、建筑八大员&#xff08;标准员&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 14.根据《施工现场临…

Electron Vue之间的通讯 自定义标题栏实现最小化全屏关闭功能

方便以后定制化使用&#xff0c;学习记录一下。 话不多说&#xff0c;先看看效果吧。 效果 版本 electron ^13.0.0 知识点 Vue 相互通讯 Electron 标题栏主要逻辑代码 新建public\preload.js文件&#xff0c;用于前端全局发送和监听消息。 const { contextBridge, ipcRen…

【Linux】Linux权限的理解

文章目录&#x1f3aa; Linux权限的理解&#x1f680;1.shell命令及其运行原理&#x1f680;2.Linux权限概念⭐2.1 用户与root身份切换⭐2.2 用户与用户身份切换⭐2.3 单条指令提权&#x1f680;3.Linux文件权限⭐3.1 文件属性(第一个字符)⭐3.2 文件角色划分与文件属性⭐3.3 文…

线性代数第四章 向量组的线性相关性

向量组及其线性组合一.向量、向量组1.向量n个有次序的数a1,a2,...,an所组成的数组称为n维向量&#xff0c;这n个数称为该向量的n个分量&#xff0c;第i个数ai称为第i个分量n维向量可以写成一行&#xff0c;也可以写成一列&#xff0c;在没有指明是行向量还是列向量时&#xff0…

Authing 入选长城战略咨询《2022中国潜在独角兽企业研究报告》

12 月 23 日&#xff0c;长城战略咨询&#xff08;GEI&#xff09;发布《2022 中国潜在独角兽企业研究报告》&#xff08;下称《报告》&#xff09;。作为身份云行业领先的代表企业&#xff0c; Authing 凭借着过硬的技术实力和突出的创新能力&#xff0c;首次入选中国潜在独角…

软件测试工程师为什么要写测试用例?

软件测试工程师为什么要写测试用例&#xff1f;相信从事软件测试行业的从业者来讲&#xff0c;测试用例并不陌生。因为测试用例不仅仅是一组简单的文档&#xff0c;它包含前提条件、输入、执行条件和预期结果等等重要内容&#xff0c;并且能够完成一定的测试目的和需求。下面本…

深度学习(20)—— ConvNext 使用

深度学习&#xff08;20&#xff09;—— ConvNext 使用 本篇主要使用convnext做分类任务&#xff0c;其中使用convnext-tiny&#xff0c;其主要有5块 stage0stage1stage2stage3head 文章目录深度学习&#xff08;20&#xff09;—— ConvNext 使用Part 1 ModelPart 2 Traini…

【数据结构】一篇博客带你实现双向带头循环链表!!!(零基础小白也可以看懂)

目录 0.前言 1. 简述双向带头链表 2.双向带头循环链表的实现 2.1 设计双向带头循环链表结构体 2.2双向带头循环链表的初始化 2.3双向带头循环链表的尾插 2.4双向带头循环链表的尾删 2.5双向带头循环链表的头插 2.6双向带头循环链表的头删 2.7双向带头循环链表的插入 …

【面试题】notify() 和 notifyAll()方法的使用和区别

【面试题】notify() 和 notifyAll()方法的使用和区别 Java中notify和notifyAll的区别 何时在Java中使用notify和notifyAll&#xff1f; 【问】为什么wait()一定要放在循环中&#xff1f; Java中通知和notifyAll方法的示例 Java中通知和notify方法的示例 Java中notify和no…

22年我在CSDN做到了名利兼收

写在前面 hi朋友&#xff0c;我是几何心凉&#xff0c;感谢你能够点开这篇文章&#xff0c;看到这里我觉得我们是有缘分的&#xff0c;因着这份缘分&#xff0c;我希望你能够看完我的分享&#xff0c;因为下面的分享就是要汇报给你听的&#xff0c;这篇文章是在 2022 年 12 月 …

从0到1完成一个Vue后台管理项目(二十三、初代项目完成、已开源)

开源地址 项目地址 项目还在优化&#xff0c;会增加很多新功能&#xff0c;UI也会重新设计&#xff0c;已经在修改啦&#xff01; 最近打算加一些组件、顺便分享一些好用的开源项目 现在正在做迁移到vue3TS的版本、预计年后会完事&#xff0c;然后迁移到vite、遇到的问题和报…

docker安装prometheus和grafana

docker安装prometheus和grafana docker安装prometheus和grafana 概念简述安装prometheus 第一步&#xff1a;确保安装有docker第二步&#xff1a;拉取镜像第三步&#xff1a;准备相关挂载目录及文件第四步&#xff1a;启动容器第五步&#xff1a;访问测试 安装grafana 第一步&…

分享66个ASP源码,总有一款适合您

ASP源码 分享66个ASP源码&#xff0c;总有一款适合您 66个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1Jf78pfAPaFo6QhHWWHEq0A?pwdwvtg 提取码&#xff1a;wvtg 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&…

Docker容器与镜像命令

文章目录帮助命令镜像命令容器命令其它命令命令总结帮助命令 显示 Docker 版本信息 docker version显示 Docker 系统信息&#xff0c;包括镜像和容器数 docker info 帮助 docker --help 镜像命令 列出本地主机上的镜像 docker images运行结果 REPOSITORY TAG …

Python采集彼岸4K高清壁纸

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 环境使用: Python 3.8 解释器 Pycharm 编辑器 模块 import re import requests >>> pip install requests ( 更多资料、教程、文档点击此处跳转跳转文末名片加入君羊&#xff0c;找…

【Leetcode面试常见题目题解】5. 最长公共前缀

题目描述 本文是LC第14题&#xff0c;最长公共前缀&#xff0c;题目描述如下&#xff1a; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 限制 1 < strs.length < 200 0 < strs[i].length < 200 strs[i] 仅…

数据库 MySQL-window安装和卸载

安装 官网&#xff1a; MySQL :: Download MySQL Community Server 或 MySQL :: Download MySQL Community Server (Archived Versions) 文件目录简述 bin存放了可执行文件&#xff0c;docs是文档&#xff0c;include放的是c语言相关的.h文件&#xff0c;lib是c语言的库文件…

wmv是什么格式?如何录制wmv格式的视频?图文教学

很多小伙伴在使用文件的时候&#xff0c;经常会发现自己的一些文件后缀名是wmv。或者说在工作、学习的过程中&#xff0c;有过被要求使用wmv格式的文件。wmv是什么格式&#xff1f;如何录制wmv格式的视频&#xff1f;今天小编就来详细的跟大家说说。 一、wmv是什么格式&#xf…