摘 要:为满足系统的稳定性和处理数据的可靠性,在分析环境监控系统的功能基础上,设计了使用ARM9处理器s3c2410和Linux构建的环境监控下位机系统,从硬件和软件上介绍了下位机的实现方法。下位机应用程序采用多线程控制方法,实现了数据的处理、显示和通信等功能,提高了系统的可扩展性。利用信号量和互斥锁作为线程的同步问题,避免了线程因争夺资源而造成系统不稳定。
关键字:ARM-Linux;多线程;同步机制;环境监控;SQLite;
0 引言
随着国民经济的发展,环境保护也越来越受到重视。现在市场上已经出现了多种环境监控系统,但大多并不稳定。现场的监控终端大多采用工控机或单片机,前者抗干扰性好,但成本较高,后者处理能力低,人机界面不友好,不利于现场人员的监控管理。考虑到监控系统的可靠性、功耗、性能及处理速度,本文设计了基于ARM-Linux的环境监控系统[1]。该终端系统采用多线程技术,有效地实现了监控过程中数据的采集与存储、实时数据的显示、终端与上位机的通信、系统实时报警等功能。
单线程系统若需要处理多任务,则需要使用多个定时器触发各任务,定时器过多,系统会不稳定,甚至崩溃。而采用多线程技术,子线程去完成相对独立的任务,并合理的利用Linux系统信号阻塞特性,节省系统资源,提高系统稳定性。
1 系统总体结构
根据设计要求,可以把环境监控系统分为现场监控终端(下位机)、传输网络、监控中心(上位机)三个部分[2]。其结构如图1所示。下位机(监控终端)是一个基于ARM9的嵌入式系统,它对被监测的特征数据进行定时采集、处理、存储。经过下位机处理后的数据,按照相关协议,定时的经GPRS无线传输模块发送给上位机[3]。上位机由一台PC机担任,负责接收多个下位机发送的来的数据,并对这些数据进行分析、处理和显示。环保部门可以通过上位机监控其辖区内的污染排放状况。上位机基于VB.Net开发。
图1 系统总体结构 图2 下位机硬件组成框图
Fig.1 The architecture of monitoring system Fig.2 The block diagram of hardware
2 系统硬件部分
硬件平台的核心部分由s3c2410、Nand Flash和SDRAM组成[4]。S3c2410是三星公司的一款基于ARM920T内核的32位RISC嵌入式微处理器,带有独立的16KB指令Cache和16KB数据Cache,LCD控制器,RAM控制器,Nand Flash控制器,并行I/O口,8路10位ADC,其运行频率可达203MHz。64Mbytes 8位的Nand Flash选用的是K9F1208,64M的SDRAM由两片HY57V561620组成。通过以太网控制器CS8900A扩展了一个网口,这样,数据既可以通过无线传输,也可以通过有线传输。板上通过I/O接口扩展了8个DI口(数字量输入)、4个AI口(模拟量输入)、4个DO口(数字量输出),下位机通过这些接口与被监控的设备进行通信。如果现场的设备过多,还可以通过ModBus模块扩展更多接口。核心板周围共有3个RS232串口和一个触摸屏接口,分别接GPRS模块、传感器模块和ModBus模块。下位机的硬件组成框图如图2所示。
3 下位机的需求与结构设计
下位机定时对现场的数据进行采集、处理、并存储于数据库中,把实时数据发送给上位机,并响应上位机发送的控制命令。因此,下位机是需要同时处理多个任务,这些任务并发的执行。若使用单线程来完成这些任务,则需要使用多个定时器来触发,而过多的定时器会导致系统不稳定。若使用多个进程来协作完成,能避免上述缺点且系统比较稳定,但系统对进程的频繁调度会占用过多资源,程序的可读性也不好。
一个并行的、多线程系统实现方案[5],能够很好的完成系统的任务,并充分节约系统资源。在该方案中,监控终端有5个线程:GUI线程(主线程)、复位线程、数据采集与存储线程、网络通信线程、决策线程。其中GUI线程为主线程,它负责界面处理、系统数据的初始化以及创建子线程等工作;而复位线程、数据采集与存储线程、网络通信线程是后台的工作线程,通过优先级调度、线程同步等机制保证这些任务都能可靠的执行。线程之间的关系如下图所示。
图3 下位机软件结构示意图
Fig 3 Sketch map of lower computer’s architecture
4 多线程技术在系统中的应用
4.1 线程的创建
Linux环境下,使用pthread_create( )函数创建一个新线程,默认情况下主线程会等待子线程执行结束,并得到子线程的返回结果继续往后执行。此程序的子线程都是循环执行的,不需要运行后结束归并到主线程中,需设置其属性为PTHREAD_CREATE_DETACHED。根据线程的不同重要性对其进行优先级设置,确保重要线程优先执行。子线程的优先级从高到低依次为复位线程、数据采集与存储线程、决策线程、网络通信线程。线程的创建、设置的伪代码如下所示[6]。
void *thread_watchdog(void *arg); //复位线程函数
void *thread_collection(void *arg); //数据采集和存储线程函数
void *thread_communication(void *arg);//网络通信线程函数
void *thread_decise(void *arg); //决策线程函数
int data[12]; //数据缓冲区,用于存放线程间共享的数据函数
main( )
{
//初始化工作
…
pthread_t watchdog; //线程号
pthread_t collection;
pthread_t commnucation;
pthread_t decise;
pthread_attr_init( ); //初始化线程属性
pthread_attr_setdetachstate( ); //不对线程进行重新归并
pthread_attr_setschedparam( ); //设置线程的优先级
sem_init( ); //对相关信号量进行初始化
pthread_create( ) ; //创建新线程
//启动GUI程序,
…
}
4.2 线程的同步机制
同步机制是否合理是多线程应用程序运行是否稳定的关键。在程序设计的时候,需考虑到可能潜在的引起数据毁坏的多线程数据访问冲突,以及如何使用同步技术避免这种冲突。Linux操作系统实现同步机制的方法有信号量(semaphore)和互斥量(mutex)。这两种方法相似,但各有侧重,信号量侧重于一个线程被别一个线程激活,常有先后执行的关系。而互斥量则保护某一共享内存任一时刻只有一个线程访问。在本程序中这两种方法都有用到。
为了防止系统资源泄漏,保持各个线程的同步,主线程需要初始化数据采集驱动代码,为数据采集做好准备;申请相应的内存空间,用于存放采集到的实时数据;定义好各个信号量和互斥锁,实现进程间的同步。
4.3 线程的实现方法
数据采集与存储线程是获取数据的起始线程,由GUI主线程创建,网络通信子线程和决策子线程是由它来激活的。系统开始运行后,数据采集和存储线程启动,每隔5秒运行一次,读DI、AI接口的状态,并把这些状态和此刻的时间存入SQLite数据库中。数据采集与存储线程每运行一次,对信号量sem_decise和sem_com进行一次post操作,分别激活决策线程和网络通信线程。该线程的同步流程如下图所示。