当前位置:天才代写 > tutorial > C语言/C++ 教程 > Windows处事编写道理及探讨(4)

Windows处事编写道理及探讨(4)

2017-11-06 08:00 星期一 所属: C语言/C++ 教程 浏览:518

副标题#e#

(四)一些问题的接头

前面几章的内容都是处事的一些通用的编写道理,但内里隐含着一些问题,编写简朴的处事时看不出来,但碰着巨大的应用就会呈现一些问题,所以本章就是用来阐明、办理这些问题的,合用于高级应用的开拓人员。我这一章的内容都是颠末尝试获得的,很有实际意义。

我在第一章内里就说过,是由一个处事的主线程执行CtrlHandler函数,它将收到各类节制呼吁,可是真正处理惩罚呼吁,执行操纵的是ServiceMain的线程。此刻,当一个SERVICE_CONTROL_STOP达到之后,你作为一个开拓者,要奈何遏制这个处事?在我看过的一些源代码里,大部门只是简朴的挪用TerminateThread函数去强行杀掉处事历程。但应该稍稍有点线程编程的知识就应该知道TerminateThread函数是可用的挪用中最为糟糕的一个,处事线程将得不到任何时机去做应该的清理事情,诸如排除内存、释放焦点工具,Dlls也得不到任何线程已经被毁的通知。

所以遏制处事的适当要领是以某种方法激活处事线程,让它遏制继承提供处事成果,然后执行完当前操纵和排除事情后返回。这就暗示你必需在CtrlHandler线程和ServiceMain线程之间执行适当的线程通信。此刻已知的最好的内部线程通信机制是I/O Completion Port(I/O 完成端口),如果你编写的是一个大型的处事,需要同时处理惩罚为数浩瀚的请求,而且运行在多处理惩罚器系统上面,这个模子就可以提供最佳的系统机能。但也正因为它的巨大性较高,在小局限的应用上面不值得耗费许多的时间和精神,这时作为开拓者可以适当的选取其它的通信方法,诸如异步进程挪用行列、套接字和窗口动静,以适应实际环境。

开拓处事时的别的一个重要问题就是挪用SetServiceStatus函数时的所有状态陈诉问题。许多的处事开拓者为了在什么时候挪用SetServiceStatus的问题而经常发生争论,一般推荐的要领就是:先挪用SetServiceStatus函数,陈诉SERVICE_STOP_PENDING状态,然后将节制代码传给处事线程可能再成立一个新的线程,让它去继承执行操纵,当该线程即将执行完操纵之前,再由它将处事的状态配置成SERVICE_STOPPED,然后处事正好遏制。


#p#副标题#e#

上面的主意从两个方面来讲照旧很不错的。首先处事可以当即确认收到了节制代码,并将在它认为适当的时候举办处理惩罚;然后就是因为前面说过的,执行CtrlHandler函数的是主线程,假如凭据这种事情要领,CtrlHandler函数可以迅速的返回,不会影响到其它处事大概收到的节制请求,对含有多个处事的措施来说,响应各个处事的节制代码的速度会大大的提高。但是,随之而来的是问题—— race condition 即“竞争条件”的发生。

摆在下面的就是一个竞争条件的例子,我花了一点时间来修改我的根基处事的代码,意图存心激发“竞争条件”的产生。我添加了一个线程,CtrlHandler函数的线程在收到请求后立即作出回响,将当前的处事状态配置成“请求正在被处理惩罚”即…_PENDING,然后由我添加的线程在睡眠了5秒之后再将处事状态配置成“请求已完成”状态——以模仿处事正在处理惩罚一些不行中止的事件,只有处理惩罚完成后才会变动处事的状态。一切停当之后,我实验在短时间内持续发送两个“暂停”请求,假如“竞争条件”不存在的话应该只有先发送的谁人请求可以或许达到SCM,而另一个则应该返回请求发送失败的信息,天下太平。

事实上很不幸的,我乐成了。当我在两个差异的“呼吁提示符”窗口别离同样的输入下面的呼吁:

net pause kservice

之后在“事件查察器”内里,我找到了我的处事在“应用措施日志”里添加的事件记录,功效是我获得了这样的事件列表:

SERVICE_PAUSE_PENDING

SERVICE_PAUSE_PENDING

SERVICE_PAUSED

SERVICE_PAUSED

看上去很奇怪是不是?因为处事处于正在暂停状态的时候,它不该该被再次暂停的。但事实摆在面前,许多处事都曾明晰的陈诉过上面的顺序状态。我曾经认为这时SCM应该说些什么或做些什么,以阻止“竞争状态”的呈现,但尝试功效汇报我SCM好像对此无能为力,因为它不能节制状态代码在什么时候被发送。当用户利用“打点东西”内里的“处事”东西来打点处事的状态的时候,在一个“暂停”请求已经发出之后不能再次用这个东西向它发出“暂停”请求,假如正在暂停处事,会有一个对话框呈现,阻止你按下它后头的“处事”东西的东西栏上的任何按钮,假如已经暂停,“暂停“按钮将酿成灰色。可是这时用呼吁行东西 net.exe 就可以很顺利地将暂停请求再次送随处事。证据就是我添加的其他事件记录内里记下了SetServiceStatus的挪用全都乐成了,这更进一步的说明白我提交的两个暂停请求都颠末SCM,然后达到了我的处事。

#p#副标题#e#
#p#分页标题#e#

接下来我又举办了其它的测试,譬喻先发送“暂停”请求,后发送“遏制”请求,和先发送“遏制”请求,再发送“暂停”或“遏制”请求。前一种环境越发糟糕,先发送的“暂停”请求和后发送的“遏制”请求都没有获得什么好下场,固然SCM老诚恳实的先暂停了处事,后遏制了处事,但 net.exe 的两个实例的挪用均告失败。不外在测试先发送遏制“请求”的时候,所有的现象都暗示这两个请求只有先发送的“遏制”达到了SCM,这还算是个好动静…

为了办理这个问题,当处事获得一个“遏制”“暂停”或“继承”请求的时候,应该首先检点处事是否已经在处理惩罚别的的一个请求,假如是,就依环境而定:是不挪用SetServiceStatus直接返回照旧临时忍耐直到前一个请求行动完成再挪用SetServiceStatus,这是你作为一个开拓者要本身抉择的。

假如说前面的问题已经足够贫苦了,下面的问题会令你以为越发独特。它其实是一种可以办理上面的问题的要领:当CtrlHandler函数的线程收到SERVICE_PAUSE_PENDING请求之后,它挪用SetServiceStatus陈诉处事正在暂停,然后由它本身挪用SuspendThread来暂停处事的线程,然后再由它本身挪用SetServiceStatus陈诉处事已经被暂停。这样做简直制止了“竞争条件”的呈现,因为所有的事情都是由一个函数来做的。此刻需要留意的不是“竞争条件”而是处事自己,挂起处事的线程会不会暂停处事呢?谜底是会的。可是暂停处事意味着什么呢?

如果我的处事是用来处理惩罚网络客户的请求,那么暂停对付我的处事来说应该是遏制接管新的请求。假如我此刻正处在处理惩罚请求的进程中,那么我应该怎么办?也许我应该竣事它,使客户不至于无限期悬挂。但假如我只是简朴的挪用SuspendThread,那么不解除处事线程正处于孤独的中间状态的大概,可能正在挪用malloc函数去实验分派内存,假如运行在同一个历程中的另一个处事也调内存分派函数,那么它也会被挂起,这必定不是我期望的功效。

#p#副标题#e#

尚有一个问题:用户认为本身可以被答允去遏制一个已经被暂停了的处事吗?我认为是这样的,并且很明明的,微软也这么认为。因为当我们在“处事”打点东西内里选中一个已暂停的处事之后,“遏制”按钮是可以被按下的。但我要奈何遏制一个由于线程被挂起才处于暂停状态的处事呢?不,不要TerminateThread,请别跟我提起它。

办理这所有的杂乱的最好要领,就是有一个可以或许把所有事做好的线程,并且它应该是处事线程,而不是CtrlHandler线程。当CtrlHandler函数获得节制代码之后,它要迅速的将节制代码通过线程内部通讯手段送随处事线程中列队,然后CtrlHandler函数就应该返回,它决不该该调SetServiceStatus。这样,处事可以随心所欲的节制每件工作,因为没有什么比它更有讲话权的了,没有“竞争条件”。处事抉择暂停意味着什么,处事可以或许答允本身在已经暂停的环境下遏制,处事抉择什么内部通讯机制是最好的——而且CtrlHandler函数必需简朴的与这种机制相一致。

工作没有完美的,上面的要领也不破例,它仅有一个小缺陷:就是假定当处事收到节制代码后,在较短的时间内就能做出应有的响应。假如处事线程正在忙于处理惩罚一个客户的请求,节制代码大概进入期待行列,并且SetServiceStatus大概也无法迅速的被挪用。假如然是这样的话,认真发送通知的SCP大概会认为你的处事已经失败,并向用户陈诉一个动静框。事实上处事并没有失败,并且也不会被终止。

这种环境够糟糕了,没有用户会去责怪SCP——固然SCP将他们引导到了错误的状态,他们只会责怪处事的作者——就是我或你…因此,在处事中怎么做才气防备这种问题产生呢?很简朴,使处事快速有效的运行,而且总保持一个勾当线程期待去处理惩罚节制代码。

#p#副标题#e#

说起来仿佛很容易,但实际做起来就被那么简朴了,这也不是我可以或许向列位表明的了,只有当真的调试本身的处事,才气找出最为适合处理惩罚要领。所以我的文章也真的到了该竣事的时候了,感激列位的欣赏。假如我有什么处所说的差池,请不惜见教,感谢。

下面是我写的一个处事的源代码,没什么成果,只能启动、遏制和安装。

#p#分页标题#e#

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#define SZAPPNAME "basicservice"
#define SZSERVICENAME "KService"
#define SZSERVICEDISPLAYNAME "KService"
#define SZDEPENDENCIES ""
void WINAPI KServiceMain(DWORD argc, LPTSTR * argv);
void InstallService(const char * szServiceName);
void LogEvent(LPCTSTR pFormat, ...);
void Start();
void Stop();
SERVICE_STATUS ssStatus;
SERVICE_STATUS_HANDLE sshStatusHandle;
int main(int argc, char * argv[])
{
  if ((argc==2) && (::strcmp(argv[1]+1, "install")==0))
  {
   InstallService("KService");
   return 0;
  }
SERVICE_TABLE_ENTRY  service_table_entry[] =
  {
   { "KService", KServiceMain },
   { NULL, NULL }
  };
  ::StartServiceCtrlDispatcher(service_table_entry);
  return 0;
}
void InstallService(const char * szServiceName)
{
  SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  char szFilename[256];
  ::GetModuleFileName(NULL, szFilename, 255);
  SC_HANDLE hService = ::CreateService(handle, szServiceName,
  szServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
  SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, szFilename, NULL,
  NULL, NULL, NULL, NULL);
  ::CloseServiceHandle(hService);
  ::CloseServiceHandle(handle);
}
SERVICE_STATUS servicestatus;
SERVICE_STATUS_HANDLE servicestatushandle;
void WINAPI ServiceCtrlHandler(DWORD dwControl)
{
  switch (dwControl)
  {
//下面固然添加了暂停、继承等请求的处理惩罚代码,但没有实际浸染
  //这是为什么呢?到了下面的KServiceMain函数内里就大白了...
case SERVICE_CONTROL_PAUSE:
   servicestatus.dwCurrentState = SERVICE_PAUSE_PENDING;
   // TODO: add code to set dwCheckPoint & dwWaitHint
   // This value need to try a lot to confirm
   // ...
   ::SetServiceStatus(servicestatushandle, &servicestatus);
   // TODO: add code to pause the service
   // not called in this service
   // ...
   servicestatus.dwCurrentState = SERVICE_PAUSED;
   // TODO: add code to set dwCheckPoint & dwWaitHint to 0
   break;
  case SERVICE_CONTROL_CONTINUE:
   servicestatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
   // TODO: add code to set dwCheckPoint & dwWaitHint
   ::SetServiceStatus(servicestatushandle, &servicestatus);
   // TODO: add code to unpause the service
   // not called in this service
   // ...
   servicestatus.dwCurrentState = SERVICE_RUNNING;
   // TODO: add code to set dwCheckPoint & dwWaitHint to 0
   break;
  case SERVICE_CONTROL_STOP:
   servicestatus.dwCurrentState = SERVICE_STOP_PENDING;
   // TODO: add code to set dwCheckPoint & dwWaitHint
   ::SetServiceStatus(servicestatushandle, &servicestatus);
   // TODO: add code to stop the service
   Stop();
   servicestatus.dwCurrentState = SERVICE_STOPPED;
   // TODO: add code to set dwCheckPoint & dwWaitHint to 0
   break;
  case SERVICE_CONTROL_SHUTDOWN:
   // TODO: add code for system shutdown
   // as quick as possible
   break;
  case SERVICE_CONTROL_INTERROGATE:
   // TODO: add code to set the service status
   // ...
   servicestatus.dwCurrentState = SERVICE_RUNNING;
   break;
  }
  ::SetServiceStatus(servicestatushandle, &servicestatus);
}
void WINAPI KServiceMain(DWORD argc, LPTSTR * argv)
{
  servicestatus.dwServiceType = SERVICE_WIN32;
  servicestatus.dwCurrentState = SERVICE_START_PENDING;
  servicestatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;//上面的问题的谜底就在这里
  servicestatus.dwWin32ExitCode = 0;
  servicestatus.dwServiceSpecificExitCode = 0;
  servicestatus.dwCheckPoint = 0;
  servicestatus.dwWaitHint = 0;
  servicestatushandle =
  ::RegisterServiceCtrlHandler("KService", ServiceCtrlHandler);
  if (servicestatushandle == (SERVICE_STATUS_HANDLE)0)
  {
   return;
  }
  bool bInitialized = false;
  // Initialize the service
  // ...
  Start();
  bInitialized = true;
  servicestatus.dwCheckPoint = 0;
  servicestatus.dwWaitHint = 0;
  if (!bInitialized)
  {
   servicestatus.dwCurrentState = SERVICE_STOPPED;
   servicestatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
   servicestatus.dwServiceSpecificExitCode = 1;
  }
  else
  {
   servicestatus.dwCurrentState = SERVICE_RUNNING;
  }
  ::SetServiceStatus(servicestatushandle, &servicestatus);
  return;
}
void Start()
{
  LogEvent("Service Starting...");
}
void LogEvent(LPCTSTR pFormat, ...)
{
  TCHAR chMsg[256];
  HANDLE hEventSource;
  LPTSTR lpszStrings[1];
  va_list pArg;
  va_start(pArg, pFormat);
  _vstprintf(chMsg, pFormat, pArg);
  va_end(pArg);
  lpszStrings[0] = chMsg;
  if (1)
  {
   // Get a handle to use with ReportEvent().
   hEventSource = RegisterEventSource(NULL, "KService");
   if (hEventSource != NULL)
   {
   // Write to event log.
    ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
    DeregisterEventSource(hEventSource);
   }
  }
  else
  {
   // As we are not running as a service, just write the error to the console.
   _putts(chMsg);
  }
}
void Stop()
{
  LogEvent("Service Stoped.");
}

 

    关键字:

天才代写-代写联系方式