朱 澳,陳麗瓊
(湖南科技學院 智能制造學院,湖南 永州 425199)
Windows系統(tǒng)上的FTP服務器軟件雖然種類很多,但是大部分都是需要付費使用的,許多軟件就算付費也有許多問題存在。而在Linux上,利用shell命令使用FTP服務器,能讓FTP的使用極為方便簡潔。在Linux上,它們是可以完全免費使用的,比如vsftpd。開發(fā)一個基于Linux的FTP服務器具有重要的意義[1]。
本FTP服務器的主要模塊包括登錄模塊、文件目錄操作模塊、文件操作模塊、文件傳輸模塊、工作模式和下載模塊。登錄模塊包括本地用戶直接登錄和用戶匿名登錄兩種方式。匿名用戶登錄時應注意控制在當前目錄下,但只有在客戶端才能出現(xiàn)在根目錄下。登錄用戶模式下的用戶名必須是在Linux環(huán)境下運行的用戶,如果不是Linux環(huán)境下運行的用戶,登錄時會提示出錯。原因是這種登錄模型的前提是讓Linux用戶判斷用戶名和登錄密碼的一致性和正確性。一些基本的目錄操作都是由文件目錄操作模塊來完成的。一般來說,文件目錄操作模塊包含了對目錄的刪除、目錄的重命名、特殊情況下的增加目錄操作等。以上所有的操作都必須是Linux用戶,如果是一個匿名用戶將無法進行上述操作[2]。其他與文件相關的操作一般同上述的目錄操作一起進行,比如對文件進行刪除和重命名等。只要是用戶在客戶端進行的操作,都是屬于混合操作,那么客戶端的處理方式是將所有的這些混合操作轉(zhuǎn)化成命令傳輸?shù)椒掌鞫?。用戶要實現(xiàn)文件上傳或?qū)⑽募螺d到本地都需要使用文件傳輸模塊來實現(xiàn)。其中還有很重要的一個功能是斷點續(xù)傳。當然,文件上傳和下載才是本文設計的服務器的最主要功能,也是同斷點續(xù)傳一樣有用的功能[3]。
程序開始執(zhí)行時,從配置文件中讀取相關的內(nèi)容進行初始化,如使用的連接方式、監(jiān)聽的端口、監(jiān)聽的服務器IP地址、支持連接的總IP數(shù)量等。然后,通過創(chuàng)建一個守護進程來避免一些問題的出現(xiàn),比如關于進程暫停的問題。創(chuàng)建一個守護進程主要是進行初始化操作,該操作主要是通過應用共享內(nèi)存來實現(xiàn)。這里的處理既包括數(shù)據(jù)進程的建立,也包括對進程函數(shù)的調(diào)用。最后利用TCP協(xié)議創(chuàng)建Socket套接字來進行監(jiān)聽,服務器接收客戶端并創(chuàng)建新的套接字。此過程需要創(chuàng)建一個子進程來處理通信服務[4]。
基于epoll、半同步/半異步模型以及線程池的FTP服務器是采用配置文件的方式保存系統(tǒng)配置以及用戶信息的。該用戶信息包括用戶名、密碼、權限、工作目錄、日志文件等。服務器支持PASV被動打開方式進行數(shù)據(jù)傳輸,支持斷點續(xù)傳與多用戶不同權限接入控制。線程池采用動態(tài)創(chuàng)建與回收基制,其中線程池保存兩個隊列——工作線程隊列與空閑線程隊列。當空閑隊列中線程數(shù)低于一個門限或高于一個門限時,則分配或回收一部分線程,從而在保證系統(tǒng)高效性的前提下盡可能減小系統(tǒng)開銷。
整個系統(tǒng)的工作流程如下,系統(tǒng)啟動后首先進行資源分配,讀入配置文件,初始化線程池,初始化 epoll模型并開啟監(jiān)聽;當新用戶接入時,進行用戶接入驗證,若合法則允許接入并把用戶消息保存到用戶管理對象中;當新命令到達時,把命令放入消息隊列中等待處理;系統(tǒng)開啟若干個線程用來處理消息隊列中的消息,并使用信號進行通知,一旦消息隊列非空時,則會喚醒一個空閑線程;當讀到“LIST”“STOR”“RETR”“REST”時,說明用戶準備進行數(shù)據(jù)傳輸,則在數(shù)據(jù)傳輸線程池中提取一個空閑線程,修改線程數(shù)據(jù)并放到工作線程中執(zhí)行,執(zhí)行完畢后由線程把結(jié)果狀態(tài)返回給用戶并從工作狀態(tài)進入空閑狀態(tài)。用戶自接入成功一直處于命令交互狀態(tài),直到發(fā)送“QUIT”退出。
首先,設計人員應該打開任務管理器,查看一下正在運行的有哪些進程。然后,逐一讀取出這些進程的全路徑,將該全路徑與要守護的應用程序的全路徑比對。如果一致,說明要守護的應用程序已開啟了,此時要分配一條線程監(jiān)控該進程句柄。當該進程句柄返回信息,說明該進程已關閉,此時釋放進程句柄內(nèi)存,并重啟該進程。如果遍歷任務管理進程列表中所有進程時,沒有找到與要守護的應用程序的全路徑一致的進程,說明要守護的應用程序尚未打開,此時要啟動該應用程序,然后轉(zhuǎn)入監(jiān)控流程。另外,一定要額外分配線程去監(jiān)控要守護的應用程序。因為如果、使用主線程去執(zhí)行監(jiān)控任務,會被長期阻塞,直到進程退出才會被激活,這樣就無法運行后續(xù)程序。況且,監(jiān)控程序要實現(xiàn)持續(xù)監(jiān)控,必須避免死循環(huán),如果主線程進入死循環(huán),則無法監(jiān)控其他要守護的進程。創(chuàng)建守護進程的關鍵步驟如下。
4.2.1 后臺實現(xiàn)
一般情況下,在后臺實現(xiàn)的第一步需要讓父進程重新排隊,而子進程由用戶進行創(chuàng)建。注意這時的父進程稱為一個孤兒進程。而在守護進程的終端,第一步將完成在shell終端的運行。設計人員要守護進程在后臺實現(xiàn),就必須讓守護進程不被掛起,采用的方法是調(diào)用fork()函數(shù)讓父進程結(jié)束,這樣守護進程就會作為子進程在后臺運行。
4.2.2 登錄會話和進程組
雖然守護可以在后臺運行,但是調(diào)用fork()函數(shù)后產(chǎn)生的子進程所擁有的資源是繼承父進程而來的,包括進程組、登錄會話、控制終端的這些資源是可以被系統(tǒng)收回的。為了能夠讓守護進程不受控制,設計人員可以通過調(diào)用setsid()函數(shù)實現(xiàn)。setsid()函數(shù)的主要功能就是使進程成為新的進程組長和會話組長,這樣就能夠同從父進程那邊繼承下來的登錄會話和進程組真正脫離。
進程組也被稱為作業(yè),這個作業(yè)本質(zhì)上是由一個或多個進程組合而成,這是進程組的一個重要特征。進程組中有多個進程,每一個進程不是孤立存在的,一定是屬于某一個確定的進程組。在waitpid函數(shù)和kill函數(shù)的參數(shù)中都曾使用進程組的概念,目的是簡化對多個進程的管理。當父進程創(chuàng)建子進程時默認子進程與父進程屬于同一個進程組,進程組ID等于進程組第一個進程ID(組長進程)。所以,組長進程標識其進程組ID。組長進程可以創(chuàng)建一個進程組及進程組中的進程,也可以隨時被終止。只要進程組中有一個進程存在,進程組就存在,與組長進程是否終止無關。
會話期間通常包含了至少一個進程的集合。既然是會話,前提是用戶一開始就進行了登錄操作,而且一定要成功登錄,當用戶退出登錄以后回話結(jié)束。在整個過程中,出現(xiàn)的所有進程都是在當前的會話中,也隸屬于當前的會話。
4.2.3 禁止程序重新開啟控制終端
禁止程序重新開啟控制終端,是服務器端需要嚴格考慮的問題,以保證整個服務器的運行正?!,F(xiàn)在,進程已經(jīng)成為無終端的會話組長,但它可以重新申請打開一個控制終端。要禁止進程重新打開控制終端,采用的方法是再次創(chuàng)建一個子進程,使進程不再成為會話組長。
守護進程的實現(xiàn)過程中,有一個標準步驟是關閉當前打開的所有文件描述符。當lim.rlim_curr為RLIM_INFINITY時,只需要關閉前1 024個文件描述符。遍歷所有可能打開的文件描述符是比較復雜的。但現(xiàn)在的系統(tǒng)調(diào)用很快,很多系統(tǒng)調(diào)用不用耗費太長的時間即可完成。通常情況下,守護進程啟動的時候,打開的文件描述符不多。
各大高校為師生提供了一些基本的網(wǎng)絡服務,比如教師和學生可以通過教務系統(tǒng)進行成績、選課和評教方面的操作,還能夠共享一些軟件安裝文件。教師也建設了自己的一些課程網(wǎng)站。通過課程網(wǎng)站,教師可以提供教學資源供學生瀏覽和下載,也可以發(fā)布最新的科研成果;學生能夠通過這些網(wǎng)站滿足一定的文件下載和共享需求。隨著雙一流精品課程的建設,師生在使用過程中可能受到網(wǎng)速的限制,不能及時下載和上傳。為了讓這些網(wǎng)站真正發(fā)揮作用,本文設計并實現(xiàn)了一個基于Linux的FTP服務器,供教師和學生訪問。