嵌入式Linux在SMP系统上的移植研究与实现
2016-11-25朱苏建邵培南
朱苏建,邵培南,金 刚
(华东计算技术研究所 上海 200233)
嵌入式Linux在SMP系统上的移植研究与实现
朱苏建,邵培南,金 刚
(华东计算技术研究所 上海 200233)
基于自主开发以双核嵌入式CPU EM8301为处理核心的嵌入式应用的目的,针对双核CPU芯片的系统结构和Linux内核的特性,通过研究嵌入式Linux操作系统在SMP系统上的移植,探讨SMP架构多核硬件平台下的启动流程、任务调度策略、任务间同步与互斥、中断处理等问题,结合自主研发嵌入式应用的实际需求,得到解决上述问题的方案,完成嵌入式Linux在SMP系统上的移植,并实现嵌入式Linux在自主CPU芯片上的成功运行。
嵌入式;Linux内核;对称多处理器;移植
如何不断提升处理器性能一直是处理器研究的重要内容,在单核处理器时代,提高处理器主频一直是提高处理器性能的主要方式。但由于工业工艺的限制,处理器主频已经很难再有大幅度的提升,并且功耗增加的代价也已经超出了提高主频所能带来的收益。这一难题随着多核处理器的出现得到了很大程度上的解决,单一处理器的主频对性能的限制被突破。多核处理器就是将一个或多个CPU核集成到一个芯片内,多个CPU核心共同承担系统负载,完成功能需求,形成多个处理器核心(Chip Multi-Processing,CMP)的并行体系结构,这样就可以将处理器主频保持在较低水平的同时提高处理器性能[1]。
EM8301是针对以太网网络通信应用开发的一款双核嵌入式CPU,该嵌入式CPU的内核和系统结构都是自主设计。首先需要选择较为高效和可靠的操作系统进行移植,使其满足该嵌入式CPU内核的功能需求,以便进一步研究和实现基于该CPU的嵌入式应用板。作为开源系统的嵌入式Linux操作系统,系统结构易于研究和应用,同时,Linux内核实现了对SMP的支持,因此本文选择嵌入式Linux作为移植目标。考虑到嵌入式 CPU EM8301的硬件特性,本文选择使用Linux-3.17.2版本的内核,开展针对多核EM8301的操作系统移植研究和实现。文中重点对 SMP(Symmetric Multi-Processing)多核平台下移植过程中的内核启动流程、任务调度策略、任务间同步与互斥、中断处理等问题进行分析解决。
1 硬件平台的系统结构
EM8301嵌入式CPU的系统结构框图如图1所示。
图1 EM8301嵌入式CPU系统结构框图
EM8301嵌入式CPU的内核是参考PowerPC指令集自主设计的 CPU核心 E300。E300内核集成了 32 kB指令Cache和32 kB数据Cache,支持存储管理功能。
EM8301的外围接口包括:DDR、GMAC、PCIE、串口等。
2 嵌入式Linux的SMP架构分析
Linux的代码完全开放及其良好的结构设计,使其非常适用于嵌入式系统。由于Linux系统的开源特性,目前出现了许多针对嵌入式硬件系统的单处理器Linux内核版本。随着多核处理器的应用领域不断扩展,这些单处理器的Linux内核版本已经无法满足现有嵌入式应用的需求。因此,必须对所选用的Linux内核进行重新改进和设计,适应当前的问题。但是从单核处理器到多核处理器,原有的操作系统模型已经无法满足现有的功能需求,整个模型都需要发生变化[2],主要体现在以下几个方面:
1)内核的启动过程。在单处理器上,内核的启动只需要完成一个CPU的初始化,而在多核处理器上,必须有新的启动方式,否则会造成混乱。多核处理器并不同时启动处理器上的所有核心,而是首先确定一个CPU作为主核心,启动这一主核心并完成必需的初始化操作。这个主核心称为 BP(Booting Processor)。在BP完成一定的初始化操作之后,再逐个唤醒其它的次处理器核心,这些次 CPU被称为 SP(Secondary Processor)。在所有次处理器核心(SP)都启动后,就和主核心(BP)共同承担系统负载,实现处理器性能的最大利用。
2)高速缓存一致性。从EM8301嵌入式CPU的系统结构图中可以看出,EM8301的两个CPU(CPU0、CPU1)分别独享32 kB的数据Cache。系统中存在大量共享数据被各个处理器调用和修改,这些数据在各处理器的数据Cache中存有副本。当处理器修改共享数据时,仅是修改数据Cache中的副本,并不对共享数据本身产生影响。而数据的修改对另一个处理器是不可见的,另一个处理器在调用或者修改同一数据时,无法获得被修改后的最新数据,这样就会造成Cache不一致。通常情况下,Cache不一致问题主要有软件和硬件解决两种不同的方法:一种是通过软件的方法实现,由软件来控制数据的修改,以保证一致性,但这种方法会使操作系统更加复杂,影响系统性能;另一种方法是通过硬件的方法实现,在硬件上使用Cache一致性维护逻辑,EM8301嵌入式CPU就是采用的这种方法。
3)负载的平衡。单核处理器中的单一处理器内核完成所有的系统功能需求,承担系统负载,因此无需考虑负载平衡的问题。而在多处理器中,所有的处理器核心共同承担负载,如果负载不平衡,出现某些处理器上的任务量偏重而其它处理器比较空闲,就会使处理器资源得不到充分利用,使用多处理器的优势就没有体现出来。所以在多核架构下,系统的负载是由多个CPU共同承担,操作系统必须处理好CPU核负载均衡的问题。
4)任务之间的同步与互斥。在单处理器上,只要保证在内核中运行进程是非抢占的,就能确保进程是互斥的。而在多处理器上,系统中有多个CPU,由于多个进程可以同时运行在不同的CPU核心上,就必须引入新的同步与互斥机制。并且在Linux2.6以后版本的Linux内核中,为了提高系统的实时性,一个内核任务可以被抢占[3],非抢占内核几乎已被淘汰。
5)中断与异常处理。在单处理器上,一个CPU完成中断响应、异常处理等操作,处理机制较为容易实现。而在多核处理器中,在工作的多个处理器核心中选择一个合适的CPU进行中断的响应和异常的处理需要新的机制进行控制,以实现合理分配中断和异常处理给相应的CPU。
3 嵌入式Linux的SMP平台移植实现
目前,Linux-3.17.2版本的内核已经能够很好的支持SMP系统,这为移植提供了便利。本文针对自主开发的一款双核嵌入式CPU EM8301,开展嵌入式Linux操作系统的移植研究,移植后的Linux内核应该达到以下目标:
1)Linux内核能够支持嵌入式多核处理器系统,在自主嵌入式CPU上成功运行,同时保留Linux效率高、可靠性强的特点,实现从单处理器向多处理器的功能拓展;
2)成功移植的内核应拥有可扩展的系统架构,该内核应当易于配置,并且实现多核服务与底层硬件的分离。
在前面已经分析过嵌入式Linux从单处理器到多处理器的主要变化,下面就对具体的移植过程中的问题进行分析与实现。
3.1 启动过程
Linux系统中,多核的启动是由BP(Booting Processor)引导SP(Secondary Processor)的启动。在启动最初的Bootrom阶段,每个CPU都会识别自身的ID,如果ID是0,则引导Bootloader和Linux内核执行,如果ID不是0,就将自身置于WFI或WFE状态,并等待CPU0的唤醒[4]。启动的流程图如图2所示。
图2 多核Linux启动流程
具体的实现过程是,BP通过start_kernel()函数完成初始化,并在最后调用rest_init()函数,通过rest_init()创建一个内核线程kernel_init()来为SP的启动布置好运行环境。
Kernel_init()内核线程首先调用smp_init_cpus()函数获取EM8301系统中CPU核的个数,smp_init_cpus(void)实现的代码如下:
然后通过smp_prepare_cpus()函数设置SP启动的地址,具体调用的是flags_set(virt_to_phys(secondary_startup)),其中secondary_startup就是SP被唤醒的执行入口,因为此时SP的MMU尚未开启,所以要将其转换为物理地址。
最后smp_boot_secondary()完成最终的SP唤醒工作,实现代码如下:
调用的write_pen_release()会将pen_release变量设置为要唤醒的CPU核的CPU号,而后通过arch_send_wakeup_ipi_mask()给要唤醒的CPU发IPI中断,这时SP就会从前面smp_prepare_cpus()函数设置的地址 secondary_startup处开始执行。接着就是SP进行初始化操作,在所有的SP完成初始化后,SP和BP一起共同承担系统负载,完成功能需求,此时就需要完善的多处理器任务调度策略。
3.2 任务调度策略
任务在双核处理器上调度需要满足两个前提条件,才能保证任务在运行空间上的数据一致性[5]:1)多个任务在执行过程中如需进行相互之间的通信操作,必须通过调用消息处理模块进行处理,避免多任务之间的共享内存,以消除可能产生的重入隐患;2)内核的可重入性。保证内核的接口操作函数在执行过程中不被打断,即保证函数操作为原子操作,同时避免操作过程中引入各类重入问题。当满足这两个条件时,上层调用接口时数据访问的一致性才能够得到保证。
EM8301嵌入式CPU的两个CPU核心使用一个操作系统内核,在同步模式下进行工作,共同完成系统指令。在这种同步模式下,单核处理器对指令流的控制方式已经无法满足实际需要,idle任务会导致指令流出现断流。因此在双核处理器上,为了防止指令流断流的情况出现,要为每一个处理器核设置独立无关的idle任务。
在双核处理器中,实现任务的透明调度对系统来说很重要,这样用户任务只需确定其功能是否正确,无需关心任务具体在哪个CPU核上运行。为了使任务调度具有透明性,需要在处理器内核中新增3个不同功能的任务队列,便于任务的分配运行。如图3所示。
1)ready队列:系统在运行过程中,将存在大量的任务,因此系统需要保证大量不同优先级和同一优先级任务的正常运行。如图3中所示,ready队列的结构包含两个主要部分,左侧部分是一个优先级指针数组,用于区分不同优先级,右侧部分则是一个双向链表,用于保存同一优先级的多个任务。这样的ready队列可以保证大规模任务在多个CPU核上的合理分配。
图3 任务队列数据结构
2)tick队列:tick队列即为延时队列,任务在被挂到tick队列后,通过delay计数控制延迟运行。系统任务通过调用taskDelay()函数来改变当前的工作状态。当该函数被执行后,其任务的系统状态发生改变,由 ACTIVE转变为 TICK_ DELAY,同时该任务将被放入到tick队列中,并设置一个delay计数。tick队列中的任务都拥有的delay计数对任务的TICK_DELAY进行控制。每当有一个tick中断,系统内核将对tick队列中所有任务的delay计数都减一。随后将对delay计数进行检测,如有delay计数为0,则把对应的任务重新加入到ready队列中。在多核处理器的环境下,每个核的tick中断不会对同一任务的delay计数进行刷新操作,保证tick队列在逻辑上分离同核任务。
3)active队列:在该嵌入式系统中可运行的任务都将被放到active队列中。
同时,在系统中增加scheduler模块,用于管理ready,tick,active3个任务队列,并基于round-robin策略和多优先级策略实现系统任务的调度功能。基于双核处理器的内核调度结构图如图4所示。
图4 双核处理器中scheduler位置示意图
Scheduler模块的总体流程是:任务处理函数通过系统接口调用该模块,在调用过程中选择调度任务的策略作为参数提交给scheduler模块。根据这一参数,scheduler模块运行对应的子模块进行处理。在完成处理后,scheduler模块将任务放入到ready队列合适的位置中。
3.3 任务间同步与互斥
在单核系统上,每个任务宏观上并行执行,但在微观上某一时刻并不存在并行,而是只有一个任务在执行。在这样的系统上,同步和互斥问题相对比较容易解决,只要简单的采取禁止中断的方式能够保证对临界资源的互斥访问。而多核平台上的操作系统在运行时,同一时刻可能有多个任务执行,这个时候单条指令中完成的操作也会受到干扰出现系统问题,因为可能存在两个核同时对某一内存或者数据进行操作。由此来看,传统的方法有时不能满足这种多核情况下的需求,SMP系统需要新的同步与互斥机制。
为了保证SMP环境下内核功能的正常实现以及系统共享资源的合理分配和利用,Linux中使用了新的锁机制:自旋锁(spin_lock)[6]。自旋锁是一种底层同步机制,是用于解决多处理器并发造成的各类问题的特殊锁,只能被一个内核任务所持有,保证共享资源的互斥使用。当某个处理器内核执行线程中需要调用共享资源时,该内核首先将申请自旋锁。自旋锁可用,则会分配给该内核,使其可以进入临界区对资源进行操作。完成操作后,该内核将向系统释放自旋锁。如果锁被占用,任务并不会直接进入睡眠状态,而是忙等待直到正在使用自旋锁的内核释放锁后再获得锁,继续运行。
在EM8301嵌入式CPU的操作系统中,使用了任务自旋锁控制任务对临界区的数据访问。任务自旋锁的实现过程中是禁止本CPU核上任务调度的,当任务获取该任务自旋锁时,运行此任务的CPU核是不允许其它任务发生优先抢占的,下面是任务自旋锁的设计与实现。
任务自旋锁实现任务的同步和互斥需要以下3个操作:
1)任务自旋锁的初始化。将任务自旋锁的nextTicket、ticketInService、cpuIndex的属性分别设置为0、0、-1,表示下个获取该任务自旋锁的任务标识为0,当前获取到任务自旋锁的任务标识为0,所对应的CPU核标识为-1,这样的属性设置便于实现自旋锁的获取和使用。
2)任务自旋锁的获取流程如图5所示,在获取任务自旋锁后,任务线程可以进行相应的操作。
图5 任务自旋锁获取流程
3)任务自旋锁的释放。将cpuIndex设置回初始值-1,表示CPU核标识无效,将ticketInService加1,释放自旋锁时打开任务调度,至此完成自旋锁操作。
任务自旋锁首先进行初始化的操作,只有完成后才能被处理器核获得和使用,处理器核在自旋锁空闲时获得并使用。当完成临界区的操作后释放该锁,等待下一次任务获取自旋锁操作,任务自旋锁采用票锁的算法。
3.4 中断处理
中断机制是一个操作系统必不可少的机制,在单核处理器的系统中,通过外部中断控制器处理中断问题是一个较为常用的方式,这个外部中断控制器处理系统的所有中断,控制器的处理效率影响操作系统性能。但在多核处理器的系统中,若系统中的所有中断都由一个处理器处理,则会影响系统的性能,同时,这个CPU上运行的任务可能会一直被打断,造成严重的问题。
所以在多核处理器系统中,需要同时由多个CPU核来处理不同的中断,多个CPU核分担中断处理,以保证不会因为单一处理器处理中断造成系统问题。那么就需要一个分配中断的中断管理策略,一般有如下两种方法[7]:
1)静态分配:不同的中断请求和不同的处理器核进行分类绑定,每个处理器核处理特定的中断请求,这种方法实现方式相对简单,同时系统的并行性也较高。
2)动态分配:系统确定每个处理器核是否屏蔽某种中断请求。每当有中断请求时,系统首先对各个处理器屏蔽的中断请求进行判断,然后动态地把这一中断分配给未屏蔽这一中断的处理器核,由该处理器处理这一中断。
由于EM8301硬件系统提供了对于中断控制的支持,进行一定的配置工作就可以实现中断请求的分配,因此关注的重点是核间中断。核间中断是处理器间的中断,是任务在运行过程中主动触发的任务调度。在多核处理器环境下,某一任务上运行的任务调度果会在整个系统的任务调度中产生全局影响。比如,有两个任务分别运行在CPU0和CPU1上,任务1阻塞在某个信号量上,而这时任务0触发了这个信号量,这时将唤醒任务1,使它从等待队列转入就绪队列,但通常任务1不会立即执行,而是等到下一个时钟中断到来时才会执行,这时就需要提出核间中断的概念了,只有处理器核间中断能解决这种延时并处理这种情况,并且核间中断子程序主要完成清中断,防止这类中断的反复进入造成严重的系统问题,然后切换任务。
4 结束语
文中基于一款自主开发的双核嵌入式CPU,分析了多核硬件结构和从单核到多核操作系统所发生的变化,提出在SMP硬件平台上进行嵌入式Linux操作系统内核移植过程中需要解决的问题,包括多核启动过程、任务调度策略、任务间同步与互斥、中断处理等,并给出了相应的解决方案。另外,由于本文仅针对的是双核嵌入式CPU系统,随着SMP系统中核个数的增加,保持系统负载均衡的难度可能会提高,核间通信的效率可能会下降,这就需要在后续的工作中设计出针对SMP更加完善的操作系统。
[1]邓竹莎.面向多处理器结构的嵌入式Linux系统研究与实现[D].成都:电子科技大学,2006.
[2]陈云川.面向多核处理器的嵌入式操作系统研究[D].成都:电子科技大学,2009.
[3]曾树洪,刘卫国.Linux内核抢占的实现机制分析[J].惠州学院学报:自然科学版,2008,23(3):56-59.
[4]宋宝华.Linux设备驱动开发详解[M].3版.北京:机械工业出版社,2015.
[5]张国杰.嵌入式Linux在多核多线程平台上的移植研究与实现[D].重庆:重庆大学,2008.
[6]彭正文,徐新爱.基于SMP的Linux内核自旋锁分析[J].江西教育学院学报(综合),2005,26(3):23-25,28.
[7]李娇娇.面向SMP架构处理器平台操作系统主要模块的设计与实现[D].西安:西安电子科技大学,2012.
Research and implementation of embedded Linux transplantation on SMP system
ZHU Su-jian,SHAO Pei-nan,JIN Gang
(East China Institute of Computer Technology,Shanghai 200233,China)
The thesis is aimed to conduct independent research and development of embedded applications,which are implemented with dual-core embedded EM 8301 CPU as a processing center.Additionally,the thesis also discusses varied topics,ranging from the characteristics of dual-core CPU chip systems and Linux kernels to transplantation of Linux operating system to SMP system.Based on these discussions,it goes further to investigate problems related to the multi-core hardware platform built under SMP system.These problems are related to start-up process,task-scheduling strategies,the synchronization and mutual exclusion among tasks,as well as interruption processing.In the end,the thesis proposes a solution that solves the above problems and meanwhile caters to the practical needs of developing embedded applications.Furthermore,the proposal can also prove that embedded Linux can be transplanted to SMP system,and functions successfully on self-developed CPU chip.
embedded;Linux kernel;symmetric Multi-processing;transplantation
TN710
A
1674-6236(2016)18-0093-04
2015-09-16 稿件编号:201509116
朱苏建(1990—),男,江苏邳州人,硕士研究生。研究方向:计算机软件与理论软件工程。