操作系統(tǒng)實(shí)踐報(bào)告
Job6/sh4
題目要求:
重新編寫makefile;
實(shí)現(xiàn)連接多條命令;
編寫測(cè)試用例。
解決思路:
重新編寫makefile:
根據(jù)教學(xué)視頻所講述模板進(jìn)行編寫,得到自動(dòng)生成依賴關(guān)系makefile文件。在 Makefile.dep 中定義依賴關(guān)系,然后在 Makefile 文件中引用依賴關(guān)系進(jìn)行編 譯等操作。
實(shí)現(xiàn)連接多條命令:
創(chuàng)建管道,前一個(gè)命令的輸出作為下一個(gè)命令的輸入,管道的創(chuàng)建和關(guān)閉依靠函數(shù)對(duì)命令數(shù)組進(jìn)行遍歷,將命令的輸入輸出連接起來,在子進(jìn)程中,函數(shù)執(zhí)行當(dāng)前命令,并將輸出寫入管道;在父進(jìn)程中,函數(shù)等待子進(jìn)程執(zhí)行完畢,并將管道的輸入端設(shè)置為下一個(gè)命令的輸入。
編寫測(cè)試用例:
對(duì)幾種測(cè)試用例進(jìn)行實(shí)現(xiàn)。
關(guān)鍵代碼:
重寫makefile:
exec_cmd函數(shù)實(shí)現(xiàn)
運(yùn)行結(jié)果:
test_parse_cmd_1 pass
test_parse_cmd_2 pass
test_parse_pipe_cmd_1 pass
test_parse_pipe_cmd_1 pass
test_exec_cmd pass
test_exec_pipe_cmd pass
All test have passed!
>pwd
current working directory:/root/os/job6/sh4
>ls
Makefile Makefile.dep cmd.c cmd.h cmd.o main.c main.o parse.c
parse.h parse.o readme.md sh4 test test.in test.out
>exit
Job8/pc1.c
題目要求:
系統(tǒng)中有3個(gè)線程:生產(chǎn)者、計(jì)算者、消費(fèi)者
系統(tǒng)中有2個(gè)容量為4的緩沖區(qū):buffer1、buffer2
生產(chǎn)者
生產(chǎn)'a'、'b'、'c'、‘d'、'e'、'f'、'g'、'h'八個(gè)字符
放入到buffer1
打印生產(chǎn)的字符
計(jì)算者
從buffer1取出字符
將小寫字符轉(zhuǎn)換為大寫字符,按照 input:OUTPUT 的格式打印
放入到buffer2
消費(fèi)者
從buffer2取出字符
打印取出的字符
程序輸出結(jié)果(實(shí)際輸出結(jié)果是交織的) a b c ... ?a:A ?b:B ?c:C ?... ? ? ?A ? ? ?B ? ? ?C ? ? ?...
解決思路:
定義一個(gè)buffer結(jié)構(gòu)體,用來存儲(chǔ)buffer1與buffer2狀態(tài),定義buffer_is_empty(struct array a)
、buffer_is_full(struct array a)
、get_item(struct array *a)
、put_item(struct array *a, char item)
函數(shù)。
總共有三個(gè)角色,他們的關(guān)系是:計(jì)算者相當(dāng)于生產(chǎn)者的消費(fèi)者,“消費(fèi)者”相當(dāng)于計(jì)算者的消費(fèi)者。
1.生產(chǎn)者產(chǎn)生字符后,查看buffer1的鎖狀態(tài),加鎖,之后判斷buffer1是否為滿,滿則阻塞等待,反之放入緩沖區(qū)buffer1,之后喚醒等待的計(jì)算者線程,解開鎖。
2.計(jì)算者被喚醒后,獲取buffer1的鎖,加鎖,判斷buffer1是否為空,如果空則阻塞等待,反之取出一個(gè)字符,轉(zhuǎn)化為大寫形式。之后再喚醒生產(chǎn)者,釋放鎖。接下來,它作為消費(fèi)者的生產(chǎn)者,查看buffer2的鎖狀態(tài),加鎖,之后判斷buffer2是否為滿,滿則阻塞等待,反之將字符放入緩沖區(qū)buffer2,之后喚醒等待的消費(fèi)者線程,解開鎖。
3.消費(fèi)者被喚醒后,獲取buffer2的鎖,加鎖,判斷buffer2是否為空,如果空則阻塞等待,反之取出一個(gè)字符,輸出到屏幕上,之后喚醒消費(fèi)者,解開鎖。
關(guān)鍵代碼:
運(yùn)行結(jié)果:
produce item: a
produce item: b
a:A
produce item: c
? ?consume item: A
b:B
produce item: d
? ?consume item: B
c:C
produce item: e
? ?consume item: C
d:D
produce item: f
? ?consume item: D
e:E
produce item: g
? ?consume item: E
f:F
produce item: h
? ?consume item: F
g:G
? ?consume item: G
h:H
? ?consume item: H
Job8/pc2.c
題目要求:
使用信號(hào)量解決生產(chǎn)者、計(jì)算者、消費(fèi)者問題 功能與 job8/pc1.c
相同
解決思路:
引入信號(hào)量結(jié)構(gòu)體,并且定義多個(gè)信號(hào)量,通過sema_init()
對(duì)信號(hào)量進(jìn)行初始化,sema_wait()
進(jìn)行等待,sema_signal()
進(jìn)行釋放。
總共有三個(gè)角色,他們的關(guān)系是:計(jì)算者相當(dāng)于生產(chǎn)者的消費(fèi)者,“消費(fèi)者”相當(dāng)于計(jì)算者的消費(fèi)者,按一定的邏輯執(zhí)行PV
操作。
生產(chǎn)者首先執(zhí)行 sema_wait(&empty_buffer1_sema)
函數(shù),如果buffer1
存在空閑位置, empty_buffer1_sema
減1,對(duì)buffer1
進(jìn)行加鎖,加入字符后進(jìn)行解鎖。之后執(zhí)行sema_signal(&full_buffer1_sema);
進(jìn)行加1操作后,喚起計(jì)算者進(jìn)程。
喚起計(jì)算者進(jìn)程后,首先執(zhí)行sema_wait(&full_buffer1_sema);
函數(shù),如果buffer1
存在值,則full_buffer1_sema
減1,加buffer1
的鎖,取字符轉(zhuǎn)換為大寫形式,解開鎖,之后執(zhí)行sema_signal(&empty_buffer1_sema);
此時(shí)empty_buffer1_sema
加1,喚醒等待的生產(chǎn)者線程。之后作為生產(chǎn)者,向buffer2
寫字符,首先執(zhí)行sema_wait(&full_buffer2_sema)
函數(shù),判斷是否buffer2
有值,如果等到有值的情況,則full_buffer2_sema
減1,加buffer1
的鎖,取字符轉(zhuǎn)換為大寫形式,解開鎖,再執(zhí)行ema_signal(&empty_buffer2_sema)
,此時(shí)empty_buffer2_sema
加1,喚醒等待的消費(fèi)者線程。
消費(fèi)者首先執(zhí)行sema_wait(&full_buffer2_sema);
對(duì)buffer2
進(jìn)行判斷,如果有值,則full_buffer2_sema
減1,加buffer2
的鎖,取字符打印輸出,解開鎖,再執(zhí)行sema_signal(&empty_buffer2_sema)
,此時(shí)empty__buffer2_sema
加1,喚醒等待的計(jì)算者線程。
關(guān)鍵代碼:
運(yùn)行結(jié)果:
produce item: a
produce item: b
produce item: c
? ? ? ? ? ? ? ?compute item before: a
? ? ? ? ? ? ? ?compute item after : A
produce item: d
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: A
? ? ? ? ? ? ? ?compute item before: b
? ? ? ? ? ? ? ?compute item after : B
produce item: e
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: B
? ? ? ? ? ? ? ?compute item before: c
? ? ? ? ? ? ? ?compute item after : C
produce item: f
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: C
? ? ? ? ? ? ? ?compute item before: d
? ? ? ? ? ? ? ?compute item after : D
produce item: g
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: D
? ? ? ? ? ? ? ?compute item before: e
? ? ? ? ? ? ? ?compute item after : E
produce item: h
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: E
? ? ? ? ? ? ? ?compute item before: f
? ? ? ? ? ? ? ?compute item after : F
? ? ? ? ? ? ? ?compute item before: g
? ? ? ? ? ? ? ?compute item after : G
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: F
? ? ? ? ? ? ? ?compute item before: h
? ? ? ? ? ? ? ?compute item after : H
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: G
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?consume item: H
Job9/pfind.c
題目要求:
功能與 sfind 相同
要求使用多線程完成
主線程創(chuàng)建若干個(gè)子線程
主線程負(fù)責(zé)遍歷目錄中的文件
遍歷到目錄中的葉子節(jié)點(diǎn)時(shí)
將葉子節(jié)點(diǎn)發(fā)送給子線程進(jìn)行處理
兩者之間使用生產(chǎn)者消費(fèi)者模型通信
主線程生成數(shù)據(jù)
子線程讀取數(shù)據(jù)
解決思路:
分不同情況,如果路徑為文件,則對(duì)文件進(jìn)行查找是否有包含指定的字符串,輸出行。如果路徑為目錄,則遍歷目錄與其子目錄,將得到的文件路徑與搜索的字符串添加到任務(wù)隊(duì)列中。
創(chuàng)建WORKER_NUMBER
個(gè)子進(jìn)程,不同子進(jìn)程從任務(wù)隊(duì)列中獲取任務(wù)并執(zhí)行,即對(duì)指定文件進(jìn)行查找。當(dāng)任務(wù)隊(duì)列為空時(shí),等待新的任務(wù)加入。
主線程完成遍歷后,根據(jù)WORKER_NUMBER
添加標(biāo)記,表示所有任務(wù)完成,子線程得到標(biāo)記后,自動(dòng)退出線程。
關(guān)鍵代碼:
運(yùn)行結(jié)果:
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# cc pfind.c -o pfind -lpthread
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# ./pfind test main
test/hello/hello.c: int main() {
test/world/world.c: int main()
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# ./pfind test int
test/hello/hello.c: int main() {
test/hello/hello.c: ?printf("hello");
test/world/world.c: int main()
test/world/world.c: ?printf("world");
Job11/http服務(wù)器
題目要求:
完成多進(jìn)程版本的 httpd
放置在 job10/http/concurrent 目錄下
解決思路:
主函數(shù)首先解析命令行參數(shù),獲取要監(jiān)聽的 IP 地址和端口號(hào)。然后使用 tcp_listen 函數(shù)創(chuàng) 建一個(gè) TCP 服務(wù)器,該服務(wù)器會(huì)監(jiān)聽指定的 IP 地址和端口號(hào)。 在服務(wù)器開始循環(huán)等待客戶端連接時(shí),使用 tcp_accept 函數(shù)接受一個(gè)新的連接,然后使用 fork 函數(shù)創(chuàng)建一個(gè)子進(jìn)程來處理客戶端連接。在子進(jìn)程中,調(diào)用 http_handler 函數(shù)來處 理客戶端的 HTTP 請(qǐng)求,并在處理完畢后關(guān)閉連接。
通過 http_parse_req 函數(shù)解析客戶端發(fā)送的請(qǐng)求,獲取請(qǐng)求的路徑。
判斷路徑是否以 "/app/" 開頭,如果是則認(rèn)為客戶端請(qǐng)求的是一個(gè) CGI 腳本,需要執(zhí)行該腳本 并將輸出發(fā)送給客戶端。
如果路徑不是以 "/app/" 開頭,則判斷路徑對(duì)應(yīng)的是一個(gè)文件還是目錄,如果是目錄則調(diào)用 generate_directory_listing 函數(shù)生成一個(gè) HTML 目錄列表并發(fā)送給客戶端,如果是文 件則直接將文件內(nèi)容發(fā)送給客戶端。
在向客戶端發(fā)送響應(yīng)時(shí),使用分塊傳輸編碼(Chunked Transfer Encoding)進(jìn)行傳輸,這是 一種將消息分塊傳輸?shù)姆绞?,可以在發(fā)送大量數(shù)據(jù)時(shí)提高傳輸效率。
關(guān)鍵代碼
http.c
main.c
運(yùn)行結(jié)果:
http://localhost:8080/
Hello World!
/a.html
/b.html
/c.html
/source
/app/now
/app/show_env?name=tom&age=10
/app/list_student
/add_student.html
/remove_student.html