临界资源,是指同一时刻只能由一个线程(linux下为进程)访问的资源,而临界区就是为了确保临界资源访问是单一数据流。
临界区的代码执行,也就是进行原子操作,不会被打断。
先分析RTOS的运行架构,以cm3为例,它的中断屏蔽寄存器如下:
那么让我们看看FreeRTOS中的临界区,在RTOS中,多线程本质上是协程,也就是CPU不断切换而达到的伪多线程,其实某一时刻仍然只有一个任务被执行。而任务的切换又是通过Pednsv中断编程进行的,所以为了完成原子操作,RTOS采用的临界区实现方式就是关闭中断:
basepri的目的是屏蔽你设置的中断优先级及以下的中断,所以如果有更高的中断,这里的代码就会被打断,后面的dsb和isb指令将会毫无意义,因为在嵌入式系统中,访问硬件寄存器的操作必须是原子的,这样显然是不行的,所以要使用cpsid指令先屏蔽所有中断(cpsid本质上是在操作PRIMASK)。
进入临界区,屏蔽一定优先级的中断后,由于Pendsv中断设置优先级是最低,所以其他线程将无法工作。
不过临界区的代码也是会被打断的,因为basepri只屏蔽一定优先级的中断,如果你对ulnewBASEPRI设置过低,那么临界区的代码其实还不是严格意义上的原子操作。不过在RTT内核中,它是直接操作primask屏蔽所有中断,确保了严格的原子性。
现在让我们看看linux内核中的临界区:
linux内核中,为了确保对数据操作的原子性,采用的是内存锁。
不同于RTOS,linux的实时性差得要命,通过屏蔽中断来完成临界区更是无稽之谈,所以linux采用了其他方法:内存锁。
linux中,进程和线程其实是统一的,对于一个复杂的操作系统,为了确保只有单一进程访问内存数据,对内存进行上锁,这样,持有锁的进程可以访问内存,不持有锁的进程无法访问,也就无法对数据进行操作,确保了数据的有效性。
通过memw_locked函数,linux内核也可以形成原子操作,这样就不会被打断了。
其实,对于运行linux内核的单核CPU而言,这里的代码还是会被打断的,因为单核处理器多进程的本质也是swtich。