程序中任务(中断)间共享资源(临界区)的保护和互斥

一、软件法

  1.轮转法

    p0 进程:
          while(turn != 0);		//进入区
          critical section ; 		//临界区
          turn = 1;                       //退出区
          remainder section;   //剩余区
    p1进程:
            while(turn != 1);           //进入区
            critical section;            //临界区
            turn = 0;                      //退出区
            remainder section;     //剩余区
--------------------- 
作者:李永贵 
来源:CSDN 
原文:https://blog.csdn.net/lierming__/article/details/78974244 
版权声明:本文为博主原创文章,转载请附上博文链接!

  2.标志法
  3.Perterson算法

  Pi 进程:
       flag[i] = TRUE; turn = j;             //进入区
       while(flag[j] && turn == j);        //进入区
       critical section;                            //临界区
       flag[i] = FALSE ;                         //退出区
       remainder section;                    //剩余区
  Pj 进程:
       flag[j] = TRUE; turn = i;             //进入区 
       while(flag[i] && turn == i);        //进入区
       critical section;                             //临界区
       flag[j] = FALSE;                         //退出区
       remainder section;                     //剩余区
--------------------- 
作者:李永贵 
来源:CSDN 
原文:https://blog.csdn.net/lierming__/article/details/78974244 
版权声明:本文为博主原创文章,转载请附上博文链接!

    软件方法看似从逻辑上实现了互斥,但隐含条件是对共享标志变量的写和读需要在一条指令内完成,否则对标志的访问本身就会造成错误。例如,在Perterson算法中,若CPU为8位,turn为16位变量,则对标志变量的存取至少需要2条指令,如果一个任务先对turn写了低8位,然后切换到另一个任务完整的写了turn,再切换回去写了高8位,那么turn的值是无法预测的。
    所以所谓软件方法要完全实现保护,也是需要和硬件特性相关的,实际上用汇编实现才能绝对正确。个人觉得完全不如下面的硬件法。

二、硬件法

  1.利用硬件原子指令
  2.关闭中断
  3.关闭任务调度
  4.利用信号量
(3和4实际上是有操作系统时,操作系统用其他方法实现的封装)

三、方法总结

  1.单片机裸机编程
    在单片机裸机编程时,出现的情况常常是在中断中获取数据,然后在后台循环中进行数据处理,我思考得到的方法伪代码如下:

//主函数
void main()
{
	while(1)
	{
		...
		intClose();			//关中断
		if(flag==true)		//标志为真
		{
			...	  //处理数据(操作尽量少,一般只是把数据移到局部变量中)
			flag=false;
		}
		intOpen();			//开中断
		...
	}
}
//中断处理函数
void interrupt()
{
//加上判断时若数据未处理则不再接收,可不加判断使数据未处理时接收新数据覆盖
	if(flag==false)	
	{
		...	  //接收数据
		flag=true;
	}
}

(仅为示例,实际当中可使用环形缓冲区)

  2.操作系统编程
    在操作系统上编程主要使用信号量,我想到的信号量PV操作可以用如下伪代码实现

void P(sem)
{
	int temp;
	intClose();		//关中断
	sem--;			//信号量减一
	temp = sem;		//用局部变量保存信号量
	intOpen();		//开中断
	if(temp < 0)
	{
		sleep();		//阻塞自身进程
	}
}
void V(sem)
{
	int temp;
	intClose();		//关中断
	sem++;			//信号量加一
	temp = sem;		//用局部变量保存信号量
	intOpen();		//开中断
	if(temp <= 0)
	{
		wake();		//从阻塞序列中唤醒进程
	}
}

    信号量可以实现共享资源的保护(线程锁)和任务同步,使用时应该遵循一定规则避免程序长时间非正常阻塞,通常是锁操作在同步操作的内部,伪代码如下:

datatype data;     //共享变量
void taskSend()   //发送任务
{  
	datatype  buff;
	while(1)
	{
		 .......                //操作
		 pthread_mutex_lock();
		 data = buff;
		 pthread_mutex_unlock();
		 sem_post();
     }
}
void taskReceive()   //接收任务
{  
	datatype  buff;
	while(1)
	{
		 sem_wait();
		 pthread_mutex_lock();
		 buff = data;
		 pthread_mutex_unlock();
		 .......                //操作
      }
}

  其中,如果data缓存大小比每次需要传输的大,就可以不用加锁。

笔记:操作缓冲区是否关中断的问题:
    通信速率低时:例如比特率9600时,每次关中断的时间一般不会超过1ms,因此不会漏掉数据,这时关中断最严谨
    通信速率高时:例如比特率115200时,每个字节发送时间大约100us,关中断时间大于这个时间就容易丢数据,因此不关中断,但不确定会不会产生错误
    rs485半双工发送数据时必须等待发送完成(无论是上位机还是下位机)

    记录:自己总结基于裸机编程的串口驱动通过环形队列实现,中断和普通程序中都得共享队列,如果直接关中断因为操作队列函数不短可能造成接收数据漏掉。想法是通过关中断实现简单互斥量(关中断时间很短),普通程序里先锁互斥量,再操作队列;中断程序里先判断是否锁,如果锁了将数据接收到中断独占的缓冲区然后返回,否则先将中断独占缓冲区里留存的数据(如果有)全部移到环形队列里再加上本次接收的数据完成接收。(中断发送时同理,只是中断中改成如果锁就不取发送队列里的数据发送)
这样干可能最后收到的数据在独占缓冲里,还是只有间歇开关中断或只在关键处关

    最后决定,中断接收里用的环形队列还是自己简单实现吧,可以严格控中断开关
    第一种,根据读写位置求得已存数据数量,开始要取另一方位置时有隐患,但一般取位置只需一条汇编指令,所以可以认为安全,最后修改自己位置时最后放回是一般也只要一条指令,所以安全。
    第二种,根据count知道已存数据数量,开始判断时其中取count一般一条指令,安全。最后放回count一般也一条指令,安全。
以上实现对关键量的操作顺序不能变
    环形队列的线程安全性:写程序使用时在单消费者单生产者是可以认为安全。自己写中断接收发送驱动时最好在关键位置关中断。
    在不溢出覆盖的环形队列里,为保证在一个生产者一个消费者的情况下(包括其中一个是中断),只需在开始关中断将count放到局部变量中为后面利用,最后关中断改变count的值即可(顺序不能变)。多个生产消费者情况下有多个的一方(必须是在用户程序中)要加互斥量(在抢占调度的操作系统中需要,时间触发轮转里不需要)。中断里的一方必须是唯一的

(以上内容均为自己思考所得,如有错误和疏漏,感谢大家指正)

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RTOS互斥锁和关中断都是用来保护共享资源的手段,但它们在实现和应用上有一些区别。 1. 互斥锁(Mutex):互斥锁是一种软件机制,用于保护共享资源的访问。当一个任务获得了互斥锁的所有权,其他任务需要等待该任务释放锁后才能获取它。这样可以确保同一时只有一个任务能够访问共享资源,从而避免数据竞争和冲突。互斥锁是通过任务的协作来实现的,需要在代码显式地获取和释放锁。 2. 关中断:关中断是一种硬件机制,在某些RTOS可以用来保护共享资源。当关中断时,CPU会禁用中断信号,这样其他任务中断处理程序无法打断当前任务的执行。在关中断的情况下,当前任务可以安全地访问共享资源,因为没有其他中断任务能够干扰。关中断主要用于保护临界区代码,即对共享资源的访问代码段。 区别: - 实时性:互斥锁是通过任务协作来实现的,因此可能存在等待时,而关中断是立即生效的。 - 范围:互斥锁只是限制了多个任务共享资源的访问,而关中断可以限制整个系统中断任务。 - 复杂性:使用互斥锁需要在代码显式地获取和释放锁,而关中断是通过硬件机制实现的,不需要特殊的代码。 - 灵活性:互斥锁可以用来保护任意大小的共享资源,而关中断只适用于保护临界区代码。 在实际应用,选择使用互斥锁还是关中断取决于具体的需求和RTOS的支持。通常情况下,推荐使用互斥锁来保护共享资源,因为它更灵活,允许多个任务同时访问非共享资源,并且可以在代码明确指定锁的获取和释放。关中断一般用于保护共享资源临界区访问,或者在某些特殊情况下需要快速禁用中断的场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值