读懂源码系列-FileZilla Server 设计原则分析-入口分析(2)
2017-09-06 21:13
507 查看
1.预备知识
FileZilla Server ftp 服务器是作为 Windows 服务运行的。我们来看下安装好的服务,通过 Win + R,输入 services.msc 找到 FileZilla Server FTP server:可以看到名为 FileZilla Server 的服务,指向了新编译生成的 *\Debug\FileZilla Server.exe 可执行文件。如果点击启动,那么 FTP 服务器就会正式运行。
Windows 操作系统不但提供了 services.msc 服务管理界面,而且还提供了 sc.exe 命令行工具,使得我们可以安装、删除、启动、停止和配置服务。
此外,还有如下 API 接口,可以完成上述功能:
OpenSCManager
CreateService
DeleteService
OpenService
StartService
ControlService
2.入口分析
FileZilla Server 项目源码,把 Windows 服务管理功能和 FTP 服务器本身功能结合在一起了。首先,找到包含 WinMain 函数的文件 Service.cpp。入口函数可以分为两个部分:
1.根据命令行选项,完成自身的安装、删除、启动、停止和配置服务;
2.作为服务程序入口 等待 StartServiceCtrlDispatcher 函数返回;
注意 StartServiceCtrlDispatcher 这个函数,使得当前调用进程与服务控制管理器(SCM)进程 services.exe 建立连接,并使调用进程的主线程成为服务控制派遣线程,服务控制派遣线程之后将处理 SCM 发送的服务控制请求,直到所有服务都停止,派遣线程才返回。
接下来看以下代码段:
const SERVICE_TABLE_ENTRY servicetable[] = { {ServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain}, {NULL,NULL} }; BOOL success; success = StartServiceCtrlDispatcher(servicetable);
SERVICE_TABLE_ENTRY 可以指定服务进程的服务名和服务入口,对于每一个服务入口,都会新建一个线程,用于执行服务入口。本服务只有一个入口,就是 ServiceMain。因此,主线程调用 StartServiceCtrlDispatcher 函数后,服务进程有两个线程:
1.主线程,进入 StartServiceCtrlDispatcher ,并且在之后处理 SCM 的控制命令;
2.新建服务线程,执行 ServiceMain;
根据以上分析,执行流程就进入了 ServiceMain 函数:
1.注册 SCM 控制命令处理函数 ServiceCtrlHandler ,这个函数由主线程执行,可以处理服务停止等 SCM命令;
void ServiceCtrlHandler(DWORD nControlCode) { BOOL success; switch (nControlCode) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: nServiceCurrentStatus = SERVICE_STOP_PENDING; success = UpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 3000); KillService(); return; case 128: SendReloadConfig(); break; default: break; } UpdateServiceStatus(nServiceCurrentStatus, NO_ERROR, 0, 0, 0); }2.新建线程 ServiceExecutionThread ,作为 FTP 服务器的主线程;
DWORD ServiceExecutionThread(LPDWORD param) { // initialize Winsock library BOOL res = TRUE; WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); int nResult = WSAStartup(wVersionRequested, &wsaData); if (nResult != 0) res = FALSE; else if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); res = FALSE; } if (!res) { SetEvent(killServiceEvent); UpdateServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0); return 0; } CServer *pServer = new CServer; VERIFY(pServer->Create()); if (!nServiceRunning) PostQuitMessage(0); MSG msg; while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } delete pServer; WSACleanup(); SetEvent(killServiceEvent); return 0; }可以看到, FTP 主线程,是一个 Windows 消息循环。这个也是我们接下来主要分析的部分。
3.等待服务结束事件
3.结构总结
我们通过忽略所有细节,分析出了 FTP 服务器的整体结构。结构如下:从外围代码来看,服务程序有3个线程:
1.主线程,直到服务处于停止状态,才从 StartServiceCtrlDispatcher 函数返回,并负责执行 SCM 发送的命令;
2.服务入口线程,执行 ServiceMain函数。函数体里边会等待一个事件,以及等待 FTP 主线执行完毕,才会返回;
3.FTP 主线程,初始化服务器,并进入Windows 消息循环。消息循环结束后,线程结束。
所以,我们对 FTP 服务器的分析,将集中于 FTP主线程部分。
相关文章推荐
- 读懂源码系列-FileZilla Server 设计原则分析-编译篇(1)
- 读懂源码系列-FileZilla Server 设计原则分析-socket 事件响应流程(3)
- 读懂源码系列-FileZilla Server 设计原则分析-socket 事件处理流程(4)
- Tomcat8源码分析系列-tomcat框架设计
- nova创建虚拟机源码分析系列之六 api入口create方法
- Thinkphp源码分析系列(一)–入口文件
- Dubbo系列(八)Dubbo源码分析之Dubbo中采用的设计模式
- jQuery源码分析系列(40): 动画设计
- mapreduce系列(1)---入门案例深入分析以及切片源码简析
- Tomcat源码分析环境搭建(Tomcat源码阅读系列之一)
- UiAutomator系列之——UiAutomator源码分析之注入事件(004)
- Android设计模式系列(12)--SDK源码之生成器模式(建造者模式)
- SSO单点登录系列1:cas客户端源码分析cas-client-java-2.1.1.jar
- JCF框架源码分析系列-ArrayList(二)
- Java集合系列之HashSet源码分析
- 菜鸟读jQuery 2.0.3 源码分析系列(2)
- glibc源码分析之chown系列函数
- Redis源码分析系列七:initServer下
- [转]jQuery源码分析系列
- jQuery源码分析系列:Extend扩展方法