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

一、软件法

  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缓存大小比每次需要传输的大,就可以不用加锁。消息队列可以实现缓存和信号同步的功能。

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

猜你喜欢

转载自blog.csdn.net/qq_32636341/article/details/82939711