无线传感网络课程论文
第四章 具体设计流程与实现
4.1安全队列
数据保护是在使用多线程技术时必须要解决的关键问题,防止多个线程同时操作某一变量,导致逻辑错误,是多线程协调运行的重要内容。线程安全队列这种数据保护机可以保护共享数据,实现线程间安全的数据传输。
一般地,每个进程中的线程都不知道进程中的其他线程的任何情况,除非我们在程序中明确的使他们互相可见。为了在不同时刻访问共享的资源,任何共享这些资源的线程都必须通过进程通信的手段进行调度。线程调度是一个复杂的问题,不正确的调度将导致进程的死锁。为了这种情况的发生,访问共享资源的线程必须进行同步。
临界区对象只有由同一个进程的线程共享,临界区对象在某一个时刻只能由一个线程所拥有。临界区对象不保证线程对临界区的访问顺序。进程负责分配临界区所使用的内存,一般声明一个类型为CRITICAL_SECTION的变量来完成此任务。在进程的线程使用临界区之前,必须使用函数InitializeCriticalSection或InitializeCriticalSectionAndSpinCount初始化临界区。线程使用函数EnterCriticalSection或函数TryEnterCriticalSection请求对临界区的拥有权。如果临界区对象正由其他线程所拥有,则函数EnterCriticalSection无限等待该拥有权。线程使用LeaveCriticalSection释放对临界区的拥有权。进程的所有线程都可以使用函数DeleteCriticalSection释放临界区对象初始化时分配的系统资源。
线程安全队列工作机制如图,操作队列的时候需要先进入临界区,操作完成后离开临界区,由此保证在任何时候都只有一个线程操作该队列。线程间的数据传输是安全可靠的。
11
无线传感网络课程论文
InitializeCriticalSectionEnterCriticalSection队列操作 入队列出队列LeaveCriticalSectionDeleteCriticalSection 图 1 安全队列工作机制
入队列相关代码:
BOOL Push(const Type & item) {
EnterCriticalSection(&lock); //进入临界区
_safe_queue_item
if (first==NULL) last=first=next; else {
last->next=next; last=next; }
LeaveCriticalSection(&lock); //离开临界区 return TRUE; };
出队列相关代码:
Pop() {
EnterCriticalSection(&lock); //进入临界区 Type val;
_safe_queue_item
LeaveCriticalSection(&lock); throw \ } else {
val=first->value; first=first->next; delete oldFirst;
LeaveCriticalSection(&lock); //离开临界区
12
无线传感网络课程论文
}
4.2服务应用程序
在基于NT的系统中,全部服务任务都是由服务控制管理器(SCM)系统管理。SCM维持注册表中已知服务列表,打开各项服务(即可以是开机时自动打开,也可以使用户请求打开)。SCM把服务列表和它们的打开状态保存在注册表中,新服务项在安装时加入此列表中。另外也可以删除服务项目。作为服务任务的程序是一个普通的exe文件,但是必须满足特定的要求才能确切地与SCM连接。微软已经详细编制了这些函数的程序流,必须紧密依据此流程表,否则服务项目就不能运行。要求如下:
商业源代码,全套计算机毕业设计免费下载 http://www.ibaidusoft.com 更多全套设计联系QQ:1042897696
最新设计大全 http://www.bylw168.com/sf/20100915/3539.html 1.服务项目的EXE文件必须有一个普通的Main或WinMain函数,此函数必须立即(特定情况下是在30秒之内)调用函数StartServiceCrtlDispatcher。调用之后,EXE文件就在SCM中注册,并且给出一个调用函数ServiceMain(在启动服务时调用)的指针。可以更改函数ServiceMain的函数名,然后就能在ServiceMain下面的记录文本中找到所用函数名的描述。main函数应该在注册ServiceMain之后返回。不能用命令行方式运行服务的exe文件。而应该在SCM知道的服务列表中安装,SCM会调用main函数,打开EXE文件。偶然用命令行方式执行服务时一定会出错,因为它不能连接SCM。
2.SCM在打开服务时将调用函数ServiceMain。例如,当管理员选择“控制面板”上“服务”的start按钮时,SCM将在一个工作线程中执行函数ServiceMain。函数ServiceMain完成多项工作。其中最为主要的是立即调用函数RegisterServiceCtrlHandler,该函数能用SCM注册一个函数Handler以调用控制要求。此函数的函数名可以随便更改,但大会在Handler下面的记录文本中列出。函数RegisterServiceCtrlHandler返回一个句柄,服务在给SCM发送状态信息时将用到该句柄。函数ServiceMain还开始将完成实际服务任务的工作现程。一旦线程开始,函数ServiceMain就等待一个事件的发生。直道服务停止,函数ServiceMain才返回。之后,重新调用函数ServiceMain,SCM将重新开始该项服务。
3.函数Handler包含一个转换语句,用于分析来自SCM的控制请求。默认时,SCM发送以下控制常数:
SERVICE_CONTROL_STOP 通知服务停止 SERVICE_CONTROL_PAUSE 通知服务暂停
13
无线传感网络课程论文
SERVICE_CONTROL_CONTINUE 通知服务重新开始
SERVICE_CONTROL_INTERROGATE 通知服务立即报告当前状态 SERVICE_CONTROL_SHUTDOWN 通知服务紧急关闭
用户也可以创建自定义常数(128~255之间),通过SCM把它们发送到服务项。当创建一个包含上述main、ServiceMain和Handler等函数的EXE文件以及一个包含服务线程的函数时,就得到了一个完整的服务任务。
服务的EXE文件ServeiceThread由函数ServerMain打开,该线程执行服务的具体任务对应暂停。继续停止等请求调用函数HandleHandle:Handle函数接收来自SCM的请求服务控制管理器SCM调用函数ServiceMain开始服务任务ServerMain:开始函数ServiceThread 告诉SCM关于函数Handle的信息在开机或安装后调用Main函数Main:Main函数告诉SCM关于函数ServerMain信息 图 2 服务中各部分之间的关系
Main函数,这是代码的入口。是在这里解析任何命令行参数并进行服务的安装,移除,启动等。提供真正服务代码的入口函数叫 ServiceMain。在服务第一次启动的时候,将该函数的地址传递给服务管理器。处理来自服务管理器命令消息的函数。这个函数叫 Handler,
服务回调函数,因为 ServiceMain 和 Handler 函数都是由系统来调用,所以它们必须遵循操作系统的参数传递规范和调用规范。也就是说,它们不能简单地作为某个 C++ 类的成员函数。这样就给封装带来一些不便,因为想把 Win32 服务的功能封装在一个 C++ 类中。为了解决这个问题,将 ServiceMain 和 Handler 函数创建成 CNTService 类的静态成员。这样就使得以创建可以由操作系统调用的函数。 但是,这样做还没有完全解决问题,因为系统不允许给被调用的函数传递任何形式的用户数据,所以我们无法确定对 C++ 对象特定实例的 ServiceMain 或 Handler 的调用。在这里用了一个非常简单但有局限的方法来解决这个问题。创建一个包含 C++ 对象指针的静态变量。这个变量是在该对象首次创建是进行初始化的。这样便限制每个服务应用只有一个C++对象。下面是
14
无线传感网络课程论文
NTService.h 文件中的声明:
class CNTService {
[...] // 静态数据
static CNTService* m_pThis; // nasty hack to get object ptr [...] };
下面是初始化 m_pThis 指针:
CNTService::CNTService(const char* szServiceName) {
m_pThis = this; [...] }
ServiceMain函数:
ServiceMain函数提供真正服务代码的入口。它完成多项工作。其中最为主要的是立即调用函数RegisterServiceCtrlHandler。之后ServiceMain做一些服务初始化的工作,为工作线程的运行提供必要的准备。最后函数ServiceMain还开始将完成实际服务任务的工作线程。一旦线程开始,函数ServiceMain就等待一个事件的发生。直到服务停止,函数ServiceMain才返回。之后重新调用函数ServiceMain,SCM将重新开始该项服务。流程如图:
RegisterServiceCtrlHandler初始化工作开始完成实际服务任务的工作线程 图 3 ServerMain函数流程
初始化工作OnInit函数:
OnInit函数的主要任务是初始化全局变量串口队列。扫描服务器的串口,如果串口可用,则把此串口进入安全队列。以备客户端请求使用。
15