簡單的具有shell功能的web服務器(帶有緩存加速訪問的功能)

 

一、實驗目的
所選題目應該與操作系統原理相關,包含進程的控制,進程間通信,併發控制,文件操作等內容。
二、實驗內容
實現了一個簡單的web服務器,其中使用了內存文件系統加快部分訪問量過大的大文件的訪問速度,可以減低服務器負載,內嵌了一個簡單的shell模塊,以協助執行一些shell命令。
主要使用了一下相關知識:
1. 內存共享區
2. 文件系統
3. 信號量
4. 進程控制與同步
5. 進程間通信(管道通信和共享內存區)
6. 線程控制
7. 併發控制
8. 文件操作(讀取文件屬性,讀取文件內容)
三、實驗環境
CPU:P7450 2.13G
內存:2G
操作系統:Ubuntu10.10
         實驗網絡:北京理工大學校園網
四、程序設計與實現
         下面我們分幾大塊來說明我們的程序的實現。
1.       shell模塊
shell模塊中內置了一些命令,內置命令爲cd、kill、exit、logout、opensocket(打開服務器)、closesocket(關閉服務器)、log(查看服務器日誌)、myls(遞歸顯示服務器內的文件)、load(加載文件進入內存文件系統)、remove(將文件從內存文件系統中刪除)、mem(顯示內存文件系統中的文件)、look(查看內存文件系統中的文件內容)。這些內部命令都是分別調用了相應的函數和子程序進行執行。
shell模塊還支持後臺運行&、輸入重定向<、輸出重定向>、管道|等,主要是先解析輸入的命令,如果發現後臺運行標誌,則在創建子進程後不在wait,輸入重定向和輸出重定向使用dup2函數複製文件描述符,將標準輸出和標準輸入的描述符複製到文件的文件描述符,管道的原理也類似,只是利用了進程間通信,將前面程序的輸出當做後面的輸入。
2.       服務器模塊
服務器首先進行初始化,這裏主要進行的是建立socket,建立監聽,之後建立線程進行accept,如果accept成功,建立一個進程響應請求。
簡單HTTP協議如下:
瀏覽器會發送請求:
GET /index.htm HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57 Safari/534.24
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,p_w_picpath/png,*/*;q=0.5
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
裏面包含了瀏覽器和操作系統,並且指明瞭連接種類,對於我們有用的只有第一行,瀏覽器以GET的方式,請求根目錄下的index.htm。
響應如下:
HTTP/1.1 200 OK
Content-type:text/html
Connection:close
Content-length:
 
//文件內容
 
 
當然如果找不到文件,則返回狀態碼爲404,告訴瀏覽器該頁面無法找到。
這裏注意的是HTTP頭與文件內容之間應該以空行分割,以表明文件內容的開始。
其中再接受請求後統計各個文件的訪問次數,如果超過設定的閾值則將文件加入到內存文件系統中,在真正的服務器中,有一個服務器緩存機制,就是將訪問量過大的大文件例如圖片等提前加載到服務器緩存中,這樣可以有效地減少打開文件與關閉文件的時間,加快訪問速度,減少服務器負載。
另外,服務器模塊還在每次請求後打開日誌文件,將請求內容記錄進入日誌,這裏利用了信號量互斥訪問,防止多個進程同時訪問文件出錯。
這裏還記錄了一下每個文件的訪問次數,放在如下數據結構:
struct note                                    /*網頁訪問記錄*/
{
    char filename[64];
    int num;
};
3.       內存文件系統模塊
這裏使用的內存文件系統爲一個簡單的文件系統。當然這裏也相應的設置一個互斥信號量,防止多線程同時訪問出現問題,防止加載過程中訪問出現問題。
struct filemap                              /*內存文件數據結構*/
{
 char name[64];
 int begin;
 int length;
};
struct memsystem                    /*內存文件系統結構*/
{
int filenum;
int next;
struct filemap file[32];
};
文件目錄表如下:

文件名
起始地址
文件長度
A
002
4
B
007
2
……
……
……
目錄後面爲簡單內存文件系統的數據區。文件系統最多存儲32個文件,存放大小最大爲8M。
我們建立了文件系統中幾種常見的操作,如存入文件、刪除文件、查看文件目錄內容和產看文件內容。這也方便了不同模塊之間的調用,提高了模塊內的內聚性,降低了模塊間的耦合性。
         整個系統的函數說明部分分解過多,依次如下:
/*******************************************************************
P操作
sem_id:信號量集標識
sem_num:信號量標識
*******************************************************************/
void p(int sem_id, int sem_num)
/*******************************************************************
V操作
sem_id:信號量集標識
sem_num:信號量標識
*******************************************************************/
void v(int sem_id, int sem_num)
/*******************************************************************
將文件複製到指定地址,主要用於向內存文件系統中的數據區複製數據
*data:數據區地址指針
*fp:文件操作符
length:文件長度
*******************************************************************/
int copy(char *data,FILE *fp,int length)
/*******************************************************************
將指定路徑的文件拷入內存文件系統
*file:文件路徑
*******************************************************************/
int load(char *file)
/*******************************************************************
將指定序號的文件刪除出內存文件系統
i:文件在內存文件系統中的編號
*******************************************************************/
int rm(int i)
/*******************************************************************
顯示內存文件系統中的文件
*******************************************************************/
void mem()
/*******************************************************************
查看內存文件系統中指定編號的文件內容
i:文件在內存文件系統中的編號
*******************************************************************/
void look(int i)
/*******************************************************************
向瀏覽器返回狀態碼501
fd:socket描述符
*******************************************************************/
void return_501(int fd)
/*******************************************************************
向瀏覽器返回狀態碼404
fd:socket描述符
*******************************************************************/
void return_404(char* file,int fd)
/*******************************************************************
判斷是否爲文件夾
*file:文件路徑
*******************************************************************/
int isdir(char *file)
/*******************************************************************
判斷文件是否存在
*file:文件路徑
*******************************************************************/
int isexist(char *file)
/*******************************************************************
確定文件類型
*file:文件路徑
*******************************************************************/
char * file_type(char *file)
/*******************************************************************
判斷是否爲cgi文件
*file:文件路徑
*******************************************************************/
int iscgi(char *file)
/*******************************************************************
輸出狀態碼爲200的http頭
*fp:socket文件描述符
*content_type:http返回類型
*******************************************************************/
void header(FILE *fp,char *content_type)
/*******************************************************************
調用子程序顯示文件夾中的文件內容
*file:文件路徑
fd:socket文件描述符
*******************************************************************/
void do_ls(char *file,int fd)
/*******************************************************************
調用子程序執行cgi
*file:文件路徑
fd:socket文件描述符
*******************************************************************/
void exec(char *file,int fd)
/*******************************************************************
調用子程序顯示文件內容
*file:文件路徑
fd:socket文件描述符
*******************************************************************/
void do_cat(char *file,int fd)
/*******************************************************************
客戶端函數,負責與瀏覽器聯絡,解析瀏覽器的請求,響應瀏覽器的請求
client_fd:socket標識符
*******************************************************************/
void client(int client_fd)
/*******************************************************************
監控瀏覽器鏈接請求
*argv:socket標識符
*******************************************************************/
void thread(void * argv)
/*******************************************************************
創建服務器
port:端口號
*******************************************************************/
int server_create(int port)
/*******************************************************************
創建服務器,建立監控線程
port:端口號
*******************************************************************/
int opensock(int port)
/*******************************************************************
shell輸出頭
*******************************************************************/
void shellheader()
/*******************************************************************
網頁瀏覽記錄內存共享區初始化
*******************************************************************/
int notesystem()
/*******************************************************************
內存文件系統內存共享區初始化
*******************************************************************/
int filesystem()
/*******************************************************************
處理ctrl-c
*******************************************************************/
void intHandler()
/*******************************************************************
shell處理輸入命令
*******************************************************************/
int shell()
/*******************************************************************
主函數,負責整個程序的調度,初始化
*******************************************************************/
int main(int argc,char *argv[])

以上是我們主要的數據結構和主體設計

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章