題目1shell程序設(shè)計(jì)_第1頁(yè)
題目1shell程序設(shè)計(jì)_第2頁(yè)
題目1shell程序設(shè)計(jì)_第3頁(yè)
題目1shell程序設(shè)計(jì)_第4頁(yè)
題目1shell程序設(shè)計(jì)_第5頁(yè)
已閱讀5頁(yè),還剩19頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、題目1 shell 程序設(shè)計(jì)1.1 實(shí)驗(yàn)?zāi)康?Linux操作系統(tǒng)中shell是用戶與系統(tǒng)內(nèi)核溝通的中介,它為用戶使用操作系統(tǒng)的服務(wù)提供了一個(gè)命令界面。用戶在shell提示符($或#)下輸入的每一個(gè)命令都由shell先解釋,然后傳給內(nèi)核執(zhí)行。本實(shí)驗(yàn)要求用C語(yǔ)言編寫(xiě)一個(gè)簡(jiǎn)單的shell程序,希望達(dá)到以下目的:l 用C語(yǔ)言編寫(xiě)清晰易讀、設(shè)計(jì)優(yōu)良的程序,并附有詳細(xì)的文檔。l 熟悉使用Linux下的軟件開(kāi)發(fā)工具,例如gcc、gdb和make。l 在編寫(xiě)系統(tǒng)應(yīng)用程序時(shí)熟練使用man幫助手冊(cè)。l 學(xué)習(xí)使用POSIX/UNIX系統(tǒng)調(diào)用、對(duì)進(jìn)程進(jìn)行管理和完成進(jìn)程之間的通信,例如使用信號(hào)和管道進(jìn)行進(jìn)程間通信。l

2、理解并發(fā)程序中的同步問(wèn)題。l 鍛煉在團(tuán)隊(duì)成員之間的交流與合作能力。1.2 實(shí)驗(yàn)要求1.2.1 ysh解釋程序的重要特征 本實(shí)驗(yàn)要實(shí)現(xiàn)一個(gè)簡(jiǎn)單的命令解釋器,也就是Linux中的shell程序。實(shí)驗(yàn)程序起名為ysh,要求其設(shè)計(jì)類似于目前流行的shell解釋程序,如bash、csh、tcsh,但不需要具備那么復(fù)雜的功能。ysh程序應(yīng)當(dāng)具有如下一些重要的特征:l 能夠執(zhí)行外部程序命令,命令可以帶參數(shù)。 。l 能夠執(zhí)行fg、bg、cd、history、exit等內(nèi)部命令。l 使用管道和輸入輸出重定向。l 支持前后臺(tái)作業(yè),提供作業(yè)控制功能,包括打印作業(yè)的清單,改變當(dāng)前運(yùn)行作業(yè)的前臺(tái)/后臺(tái)狀態(tài),以及控制作業(yè)

3、的掛起、中止和繼續(xù)運(yùn)行。 除此之外,在這個(gè)實(shí)驗(yàn)中還須做到:l 使用make工具建立工程。l 使用調(diào)試器gdb來(lái)調(diào)試程序。l 提供清晰、詳細(xì)的設(shè)計(jì)文檔和解決方案。1.2.2 ysh解釋程序的具體要求1. Shell程序形式本實(shí)驗(yàn)的ysh程序設(shè)計(jì)不包括對(duì)配置文件和命令行參數(shù)的支持。如果實(shí)現(xiàn)為像bash那樣支持配置文件,當(dāng)然很好,但本實(shí)驗(yàn)并不要求。ysh應(yīng)提供一個(gè)命令提示符,如ysh>,表示接收用戶的輸入,每次執(zhí)行完成后再打印下一個(gè)命令提示符ysh>。當(dāng)用戶沒(méi)有輸入時(shí), ysh需要一直處于隨時(shí)等待輸入狀態(tài),同時(shí)在屏幕上顯示一些必要的信息。2. 外部命令和內(nèi)部命令在大多數(shù)情況下,用戶輸入的

4、命令是執(zhí)行存儲(chǔ)在文件系統(tǒng)中的可執(zhí)行程序,我們叫做外部命令或外部程序。ysh應(yīng)當(dāng)支持在執(zhí)行這些程序時(shí)可以將輸入輸出重新定向到一個(gè)文件,并允許若干個(gè)程序使用管道串聯(lián)起來(lái)。從本實(shí)驗(yàn)的角度來(lái)講,我們把由管道連接起來(lái)的復(fù)合命令以及單獨(dú)使用的命令統(tǒng)稱為作業(yè)。外部命令的形式是一系列分隔的字符串。第一個(gè)字符串是可執(zhí)行程序的名字,其他的是傳給這個(gè)外部程序的參數(shù)。如果第一個(gè)字符串所聲明的可執(zhí)行文件并不存在或者不可執(zhí)行則認(rèn)為這個(gè)命令是錯(cuò)誤的。解釋器還須支持一些內(nèi)部命令,這些命令在ysh程序內(nèi)部實(shí)現(xiàn)了特定的動(dòng)作,下面是一些內(nèi)部命令,如果用戶提交了一個(gè)內(nèi)部命令,ysh應(yīng)當(dāng)按照下面的描述執(zhí)行相應(yīng)動(dòng)作。l exit:結(jié)束所

5、有的子進(jìn)程并退出ysh。l jobs:打印當(dāng)前正在后臺(tái)執(zhí)行的作業(yè)和掛起的作業(yè)信息。輸出信息應(yīng)采用便于用戶理解的格式。jobs自身是一條內(nèi)部命令,所以不需要顯示在輸出上。l fg <int>:把<int>所標(biāo)識(shí)的作業(yè)放到前臺(tái)運(yùn)行。如果這個(gè)作業(yè)原來(lái)已經(jīng)停止,那么讓它繼續(xù)運(yùn)行。shell應(yīng)當(dāng)在打印新的命令提示符之前等待前臺(tái)運(yùn)行的子進(jìn)程結(jié)束。l bg <int>:在后臺(tái)執(zhí)行<int>標(biāo)識(shí)的已掛起的進(jìn)程。3命令行當(dāng)用戶在提示符后面輸入命令時(shí),輸入的整行內(nèi)容叫做“命令行字符串”,ysh應(yīng)當(dāng)保存每一條命令行字符串,直到它表示的作業(yè)執(zhí)行結(jié)束,其中包括后臺(tái)作業(yè)和被

6、掛起的作業(yè)。ysh應(yīng)當(dāng)給每一個(gè)命令行字符串賦一個(gè)非負(fù)整數(shù)標(biāo)識(shí)符。這個(gè)整數(shù)用來(lái)標(biāo)識(shí)存儲(chǔ)作業(yè)的數(shù)據(jù)結(jié)構(gòu),作業(yè)的數(shù)據(jù)結(jié)構(gòu)應(yīng)包含整個(gè)命令行字符串所表示的內(nèi)容。一旦命令行字符串代表的作業(yè)執(zhí)行結(jié)束,ysh就要?jiǎng)h掉表示這個(gè)作業(yè)的數(shù)據(jù)結(jié)構(gòu)。標(biāo)識(shí)符可以循環(huán)使用。對(duì)于包含內(nèi)部命令的命令行字符串,不需要為它們建立作業(yè)的數(shù)據(jù)結(jié)構(gòu),因?yàn)樗鼈儽旧淼膬?nèi)容全部包含在ysh程序中。4前臺(tái)和后臺(tái)作業(yè)ysh應(yīng)當(dāng)能夠執(zhí)行前臺(tái)和后臺(tái)作業(yè)。shell在前臺(tái)作業(yè)執(zhí)行結(jié)束之前要一直等待。而在開(kāi)始執(zhí)行后臺(tái)作業(yè)時(shí)要立刻打印出提示符ysh>,讓用戶輸入下一條命令。前臺(tái)作業(yè)的執(zhí)行總是優(yōu)先于執(zhí)行一個(gè)后臺(tái)作業(yè),ysh不需要在打印下一個(gè)提示符前等待

7、后臺(tái)作業(yè)的完成,無(wú)論是否有后臺(tái)作業(yè)的執(zhí)行,只要完成一個(gè)前臺(tái)作業(yè),便立即輸出提示符ysh>。一個(gè)后臺(tái)作業(yè)結(jié)束時(shí),ysh應(yīng)當(dāng)在作業(yè)執(zhí)行結(jié)束后立刻打印出一條提示信息。下面語(yǔ)法中會(huì)在命令語(yǔ)法分析程序中介紹相應(yīng)的語(yǔ)法來(lái)支持后臺(tái)作業(yè)。5特殊鍵又稱組合鍵。通過(guò)終端驅(qū)動(dòng)程序,特殊的組合鍵可以產(chǎn)生信號(hào)給ysh,程序應(yīng)當(dāng)對(duì)這些信號(hào)做出適當(dāng)?shù)捻憫?yīng)。l Ctrl+Z:產(chǎn)生SIGTSTP信號(hào),這個(gè)信號(hào)不是掛起ysh,而是讓shell掛起在前臺(tái)運(yùn)行的作業(yè),如果沒(méi)有任何前臺(tái)作業(yè),則該特殊鍵無(wú)效。l Ctrl+C:產(chǎn)生SIGINT信號(hào),這個(gè)信號(hào)不是中止ysh,而是通過(guò)ysh發(fā)出信號(hào)殺死前臺(tái)作業(yè)中的進(jìn)程。如果沒(méi)有任何前臺(tái)

8、作業(yè),則該特殊鍵無(wú)效。6分析用戶輸入1) 分隔符和特殊字符分析用戶輸入的語(yǔ)法分析器應(yīng)具有下面介紹的功能,它能夠檢查用戶的輸入錯(cuò)誤。如果用戶輸入的某些地方出錯(cuò)了,ysh應(yīng)提供合理的出錯(cuò)信息。就像商業(yè)級(jí)別的shell一樣,ysh每次接受用戶輸入的一行命令,在用戶按下回車鍵 (Enter)后開(kāi)始執(zhí)行分析動(dòng)作??彰畈划a(chǎn)生任何操作,而只是打印一個(gè)新提示符。定義空格符為分隔符,ysh應(yīng)能處理命令行中間和前后出現(xiàn)的重復(fù)空格符。某些字符被稱做“元字符",它們?cè)谟脩糨斎氲纳舷挛闹芯哂刑囟ǖ暮x。這些字符包括“、| 、<、>“。shell假設(shè)這些字符不會(huì)出現(xiàn)在程序名、參數(shù)名和文件名中,它們

9、是ysh的保留字符。下面幾小節(jié)會(huì)解釋這些元字符的含義。2) 內(nèi)部命令如果命令行字符串符合前面介紹的內(nèi)部命令的格式,它就作為一個(gè)內(nèi)部命令被解釋。如果不是,就要考慮可能是外部程序的執(zhí)行,或者是錯(cuò)誤的。3) I/O重定向一個(gè)程序命令后面可能還跟有元字符“<”或“>”,它們是重定向符號(hào),而在重定向符號(hào)后面還跟著一個(gè)文件名。在“<”的情況下,程序的輸入被重定向到一個(gè)指定的文件中。在“>”的情況下,程序的輸出被重定向到一個(gè)指定的文件中。如果輸出文件不存在,需要?jiǎng)?chuàng)建一個(gè)輸出文件。如果輸入文件不存在,則認(rèn)為是出現(xiàn)了錯(cuò)誤。4) 管道和協(xié)同程序在一條命令行中當(dāng)若干個(gè)命令被元字符“|”分隔開(kāi)

10、時(shí),這個(gè)元字符代表管道符號(hào)。在這種情況下,ysh為每一個(gè)子命令都創(chuàng)建一個(gè)進(jìn)程,并把它們的輸入/輸出用管道連接起來(lái)。例如下面這條命令行: progA argA1 argA2<infile | progB argB1>outfile應(yīng)生成progA和progB兩個(gè)進(jìn)程,progA的輸入來(lái)自文件infile,progA的輸出是progB的輸入,并且progB的輸出是文件outfile。這種命令行可以通過(guò)進(jìn)程間通信中的管道來(lái)實(shí)現(xiàn)。含有一個(gè)和多個(gè)管道的命令會(huì)在如下幾種情況下產(chǎn)生錯(cuò)誤:l 當(dāng)其任何一個(gè)子程序執(zhí)行出錯(cuò)時(shí)。l 除了第一個(gè)子程序以外的其他子程序的輸入被重定向。l 除了最后一個(gè)子程序以

11、外的其他子程序的輸出被重定向。由管道連接的多個(gè)進(jìn)程所組成的作業(yè)只有當(dāng)其所有的子進(jìn)程都執(zhí)行完畢后才算結(jié)束。5)后臺(tái)作業(yè)當(dāng)用戶需要在后臺(tái)執(zhí)行一個(gè)作業(yè)時(shí),可以在作業(yè)命令的后面加上元字符“”。用戶以該種方式輸入的作業(yè)命令都必須放在后臺(tái)執(zhí)行,同時(shí)并不影響用戶與終端的交互。6)語(yǔ)法下面給出的語(yǔ)法規(guī)則描述圖提供了控制用戶輸入的一種更規(guī)范的描述形式。如果使用Linux中的現(xiàn)有工具lex和yacc來(lái)建立命令行分析器,還需要對(duì)這個(gè)語(yǔ)法進(jìn)行修改,以便使它支持LALR(1)(Look Ahead Left Reduction)分析方法。這個(gè)語(yǔ)法并不包括特殊鍵,因?yàn)樗鼈儾粫?huì)在用戶輸入時(shí)顯示出來(lái),而是需要單獨(dú)處理。Com

12、mandLine代表用戶的合法輸入,它作為ysh要執(zhí)行的一條“指令”。這里假設(shè)存在一個(gè)詞法分析器,它將空格符作為分隔符,并識(shí)別元字符作為一個(gè)詞法記號(hào)等。CommandLine := NULL FgCommandLine FgCommandLine&FgCommandLine := SimpleCommand FirstCommand MidCommand LastCommandSimpleCommand := ProgInvocation InputRedirect OutputRedirectFirstCommand := ProgInvocation InputRedirectMi

13、dCommand := NULL | ProgInvocation MidCommand LastCommand := |ProgInvocation OutputRedirectProgInvocation := ExecFi le Args InputRedirect := NULL, <STRINGOutputRedirect := NULL >STRINGExecFile := STRINGArgs := NULL STRING ArgsSTRING := NULL CHAR STRINGCHAR := 0 |1 | 9| a |b | z | A | B | Z7實(shí)驗(yàn)步驟

14、建議(1)閱讀關(guān)于fork、exec、wait和exit系統(tǒng)調(diào)用的man幫助手冊(cè)。(2)編寫(xiě)小程序練習(xí)使用這些系統(tǒng)調(diào)用。(3)閱讀關(guān)于函數(shù)tcsetpgrp和setpgid的man幫助手冊(cè)。(4)練習(xí)編寫(xiě)控制進(jìn)程組的小程序,要注意信號(hào)SIGTTIN和SIGTTOU。(5)設(shè)計(jì)命令行分析器(包括設(shè)計(jì)文檔)。(6)實(shí)現(xiàn)命令行分析器。(7)使用分析器,寫(xiě)一個(gè)簡(jiǎn)單的shell程序,使它能執(zhí)行簡(jiǎn)單的命令。(8)增加對(duì)程序在后臺(tái)運(yùn)行的支持,不必?fù)?dān)心后臺(tái)作業(yè)運(yùn)行結(jié)束時(shí)要打印一條信息(這屬于異步通知)。增加jobs命令(這對(duì)于調(diào)試很有幫助)。(9)增加輸入輸出重定向功能。(10)添加代碼支持在后臺(tái)進(jìn)程結(jié)束時(shí)打

15、印出一條信息。(11)添加作業(yè)控制特征,主要實(shí)現(xiàn)對(duì)組合鍵Ctrl+Z、Ctrl+C的響應(yīng),還有實(shí)現(xiàn)fg和bg命令功能。(12)增加對(duì)管道的支持。(13)實(shí)現(xiàn)上面的所有細(xì)節(jié)并集成。(14)不斷測(cè)試。(15)寫(xiě)報(bào)告。(16)結(jié)束。1.3相關(guān)基礎(chǔ)知識(shí)1.3.1 shell與內(nèi)核的關(guān)系shell是用戶和Linux內(nèi)核之間的接口程序,如果把Linux內(nèi)核想象成一個(gè)球體的中心, shell就是包圍內(nèi)核的外殼,如圖51所示。當(dāng)從shell或其他程序向Linux傳遞命令時(shí),內(nèi)核會(huì)做出相應(yīng)的反應(yīng)。shell是一個(gè)命令語(yǔ)言解釋器,它擁有自己內(nèi)建的shell命令集,shell也能被系統(tǒng)中其他應(yīng)用程序所調(diào)用。用戶在提

16、示符ysh>下輸入的命令都是由shell先解釋后傳給Linux核心的 1.3.2 系統(tǒng)調(diào)用系統(tǒng)調(diào)用是一個(gè)“函數(shù)調(diào)用”,它控制狀態(tài)的改變。系統(tǒng)調(diào)用與普通函數(shù)過(guò)程的區(qū)別在于系統(tǒng)調(diào)用的執(zhí)行會(huì)引起特權(quán)級(jí)的切換,因?yàn)楸徽{(diào)用的函數(shù)處于操作系統(tǒng)內(nèi)核中,是內(nèi)核的一部分。操作系統(tǒng)定義了一個(gè)系統(tǒng)調(diào)用集合。為了安全起見(jiàn),調(diào)用操作系統(tǒng)內(nèi)部的函數(shù)必須謹(jǐn)慎地控制,這種控制是由硬件通過(guò)陷阱向量執(zhí)行的。只有那些在操作系統(tǒng)啟動(dòng)時(shí)填入陷阱向量的地址,才是正當(dāng)而且有效的系統(tǒng)調(diào)用地址。因此,系統(tǒng)調(diào)用就是一種在受約束的行為下進(jìn)入保護(hù)核心的“函數(shù)調(diào)用”。因?yàn)椴僮飨到y(tǒng)負(fù)責(zé)進(jìn)程控制和調(diào)度,ysh就需要調(diào)用操作系統(tǒng)內(nèi)部的函數(shù)來(lái)控制它的子

17、進(jìn)程。這些函數(shù)叫做系統(tǒng)調(diào)用。在Linux中,我們可以區(qū)分系統(tǒng)調(diào)用和用戶應(yīng)用層次的庫(kù)函數(shù),因?yàn)橄到y(tǒng)調(diào)用函數(shù)手冊(cè)在“幫助”手冊(cè)的第二部分,而庫(kù)函數(shù)在手冊(cè)的第三部分。在Linux中可以通過(guò)man命令查詢“幫助”手冊(cè)。例如,使用命令man fork會(huì)給出手冊(cè)第二部分關(guān)于fork系統(tǒng)調(diào)用的描述,而命令man 2 exec會(huì)給出exec系統(tǒng)調(diào)用族的描述(2表示手冊(cè)的第二部分)。還有很多其他的系統(tǒng)調(diào)用,都可以通過(guò)man命令來(lái)查閱,你會(huì)發(fā)現(xiàn)man是很有用的查閱參考手冊(cè)的命令。下面是在實(shí)驗(yàn)中會(huì)用到的重要的UNIX系統(tǒng)調(diào)用。l pid_t fork(void):創(chuàng)建一個(gè)新的進(jìn)程,它是原來(lái)進(jìn)程的一個(gè)副本。在fork

18、成功返回后,父進(jìn)程和子進(jìn)程都要繼續(xù)執(zhí)行fork后的指令。這兩個(gè)進(jìn)程通過(guò)fork的返回值進(jìn)行區(qū)分,對(duì)父進(jìn)程fork的返回值是子進(jìn)程的進(jìn)程號(hào),對(duì)子進(jìn)程的返回值是0。l int execvp(const char*file,char*const argv):加載一個(gè)可執(zhí)行程序到調(diào)用進(jìn)程的地址空間中,然后執(zhí)行這個(gè)程序。如果成功,它就會(huì)覆蓋當(dāng)前運(yùn)行的進(jìn)程內(nèi)容。有若干個(gè)類似的exec系統(tǒng)調(diào)用。l void exit(int status):退出程序,使調(diào)用進(jìn)程退出,程序結(jié)束。它把status作為返回值返回父進(jìn)程,父進(jìn)程通過(guò)wait系統(tǒng)調(diào)用獲得返回值。鏈接器會(huì)為每一個(gè)程序結(jié)尾鏈接一個(gè)exit系統(tǒng)調(diào)用。l i

19、nt wait(int*stat_loc):如果有退出的子進(jìn)程,則返回退出的子進(jìn)程的狀態(tài);如果沒(méi)有任何子進(jìn)程在運(yùn)行,則返回錯(cuò)誤。如果當(dāng)前有子進(jìn)程正在運(yùn)行,則函數(shù)會(huì)一直阻塞直到有一個(gè)子進(jìn)程退出。l pid_t waitpid(pid_t pid,int*stat_loc,int options):類似于函數(shù)wait,但允許用戶等待某個(gè)進(jìn)程組的特定進(jìn)程,并可以設(shè)置等待選項(xiàng),例如 WNOHANG。l int tcsetpgrp(int fildes,pid_t pgid_id):將前臺(tái)進(jìn)程組ID設(shè)置為pgid_id,fildes是與控制終端相聯(lián)系的文件描述符。終端通常指標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸

20、出(文件描述符為0、1、2)。l int setpgid(pid_t pid,pid_t pgid):把pid進(jìn)程的進(jìn)程組ID設(shè)置為pgid。l int dup2(int fildes,int flides2):把ftildes文件描述符復(fù)制到fildes2。如果fildes2已經(jīng)打開(kāi),則先將其關(guān)閉,然后進(jìn)行復(fù)制,使filedes和fildes2指向同一文件。l int pipe(int fildes2):創(chuàng)建一個(gè)管道,把管道的讀和寫(xiě)文件描述符放到數(shù)組fildes中。1進(jìn)程創(chuàng)建使用fork系統(tǒng)調(diào)用創(chuàng)建新的進(jìn)程。fork克隆了調(diào)用進(jìn)程,兩者之間只有很少的差別。新進(jìn)程的進(jìn)程號(hào)pid和父進(jìn)程號(hào)ppi

21、d與原來(lái)的進(jìn)程不同。其他不同之處可以查看man手冊(cè)得到。fork的返回值是程序中惟一能夠區(qū)別父進(jìn)程和子進(jìn)程的地方。fork對(duì)父進(jìn)程返回子進(jìn)程的進(jìn)程號(hào),對(duì)子進(jìn)程則返回0。利用這個(gè)細(xì)小的區(qū)別可以使兩個(gè)進(jìn)程執(zhí)行不同的程序段。wait函數(shù)族允許父進(jìn)程等待子進(jìn)程執(zhí)行結(jié)束。在ysh創(chuàng)建一個(gè)前臺(tái)進(jìn)程時(shí)會(huì)用到它。須特別注意的是wait函數(shù)族會(huì)在子進(jìn)程狀態(tài)改變時(shí)返回,而不僅僅是在子進(jìn)程運(yùn)行結(jié)束或者退出時(shí)才返回,其中有些狀態(tài)的變化可以被忽略。在man手冊(cè)中有關(guān)于函數(shù)waitpid的參數(shù)說(shuō)明,其中有WNOHANG和其他一些有用的參數(shù)(WNOHANG指定在沒(méi)有子進(jìn)程退出時(shí),父進(jìn)程不阻塞等待)。下面的例子介紹創(chuàng)建進(jìn)程和

22、等待子進(jìn)程運(yùn)行結(jié)束。int main(int argc,char*argv) int status; int pid; char*prog_arv4; /*建立參數(shù)表*/ prog_argv0=”/bin/ls”; prog_argv1=”-1”; prog_argv2=”/”; prog_argv3=NULL;/* *為程序ls創(chuàng)建進(jìn)程*/ if(pid=fork()<0) perror(”Fork failed”); exit(errno); if(!pid) /*這是子進(jìn)程,執(zhí)行程序ls*/ execvp(prog_argv0,prog_argv);if(pid) /* *這是父進(jìn)

23、程,等待子進(jìn)程執(zhí)行結(jié)束 */ waitpid(pid,NULL,0);shell程序等待子進(jìn)程執(zhí)行結(jié)束是很重要的。對(duì)一個(gè)作業(yè)等待子進(jìn)程發(fā)出信號(hào)進(jìn)行處理可以采用阻塞等待的方式,或是采用非阻塞的方式。盡管在進(jìn)程死亡時(shí)它的許多資源都會(huì)被釋放,但是進(jìn)程控制塊和其他的一些信息還沒(méi)有釋放,這種狀態(tài)被稱為defunct。進(jìn)程控制塊包含了退出的狀態(tài)信息,它可以通過(guò)wait函數(shù)族獲得。在wait pid函數(shù)調(diào)用完之后,進(jìn)程控制塊就被釋放了。如果父進(jìn)程在子進(jìn)程之前結(jié)束,那么子進(jìn)程就會(huì)成為init進(jìn)程的孩子,init進(jìn)程會(huì)等待任何子進(jìn)程的結(jié)束,釋放進(jìn)程控制塊。那些已經(jīng)終止,但父進(jìn)程尚未對(duì)其進(jìn)行狀態(tài)搜集的進(jìn)程就成為僵

24、尸進(jìn)程。2exec系統(tǒng)調(diào)用exec函數(shù)族允許當(dāng)前進(jìn)程執(zhí)行另外一個(gè)程序。典型的應(yīng)用是一個(gè)程序調(diào)用fork生成自身的一個(gè)副本,然后子進(jìn)程調(diào)用exec執(zhí)行另外一個(gè)程序。exec有許多不同形式,它們最終都是調(diào)用內(nèi)核中的同一個(gè)函數(shù),只是它們給用戶提供了更多的調(diào)用形式。調(diào)用exec的進(jìn)程不是和原來(lái)完全不同,而是調(diào)用后進(jìn)程繼承了原來(lái)的父進(jìn)程標(biāo)識(shí)、組標(biāo)識(shí)和信號(hào)掩碼,但不包括信號(hào)處理程序。詳細(xì)信息可以查看man手冊(cè)。除非產(chǎn)生錯(cuò)誤,否則exec函數(shù)從來(lái)不返回(從此時(shí)開(kāi)始執(zhí)行新的程序代碼)。上面的例子介紹了函數(shù)execvp被系統(tǒng)調(diào)用的使用方法。3. I/0重定向?yàn)榱藢?shí)現(xiàn)I/0重定向,需要使用函數(shù)dup2:int d

25、up2(int fiides,int flides2);每個(gè)進(jìn)程都有一張它所打開(kāi)的文件描述符表,每個(gè)表項(xiàng)包含了文件描述符標(biāo)識(shí)和指向系統(tǒng)文件表中相對(duì)應(yīng)表項(xiàng)的指針。這個(gè)系統(tǒng)文件表是由內(nèi)核維護(hù),它記錄了系統(tǒng)當(dāng)前打開(kāi)的所有文件的信息,其中包括打開(kāi)這個(gè)文件的進(jìn)程數(shù)目、文件狀態(tài)標(biāo)志(讀、寫(xiě)、非阻塞等)、當(dāng)前文件指針、指向該文件inode節(jié)點(diǎn)表項(xiàng)的指針。還應(yīng)當(dāng)認(rèn)識(shí)到許多非文件類的機(jī)制也使用文件接口,只是它們的操作被包裝了。例如很多場(chǎng)合終端也被當(dāng)做文件來(lái)操作。默認(rèn)情況下,進(jìn)程的文件列表中的前三個(gè)入口都指向終端:標(biāo)準(zhǔn)輸入(0),標(biāo)準(zhǔn)輸出(1)和標(biāo)準(zhǔn)錯(cuò)誤輸出(2)。為實(shí)現(xiàn)I/O重定向,我們要打開(kāi)一個(gè)文件,并把它的

26、文件描述符入口復(fù)制給標(biāo)準(zhǔn)輸入或者標(biāo)準(zhǔn)輸出(或者標(biāo)準(zhǔn)錯(cuò)誤輸出)。如果需要在后面恢復(fù)原來(lái)的入口項(xiàng),我們可以事先把它保存在文件列表中別的地方。4信號(hào)信號(hào)是最簡(jiǎn)單的進(jìn)程間通信(IPC)原語(yǔ)。信號(hào)允許一個(gè)進(jìn)程在某一事件發(fā)生時(shí)與另一個(gè)進(jìn)程通信。信號(hào)的值表明發(fā)生了哪種事件。信號(hào)對(duì)本實(shí)驗(yàn)來(lái)說(shuō)是很重要的,它們指出了后臺(tái)運(yùn)行的子進(jìn)程狀態(tài)發(fā)生的變化,比如子進(jìn)程的正常終止。當(dāng)一個(gè)進(jìn)程接收到一個(gè)信號(hào)時(shí),它會(huì)采取某些動(dòng)作。許多信號(hào)都有默認(rèn)的動(dòng)作。比如,某些信號(hào)默認(rèn)產(chǎn)生core dumps,或者進(jìn)程自身掛起。我們也可以聲明讓自己的進(jìn)程處理某個(gè)信號(hào)。通過(guò)聲明一個(gè)信號(hào)處理程序可以做到這一點(diǎn)。Linux主要有兩個(gè)函數(shù)實(shí)現(xiàn)對(duì)信號(hào)的

27、處理,即signal和sigaction。其中signal是庫(kù)函數(shù),在可靠信號(hào)系統(tǒng)調(diào)用的基礎(chǔ)上實(shí)現(xiàn)。它只有兩個(gè)參數(shù),不支持信號(hào)傳遞信息;而sigaction是較新的函數(shù),有三個(gè)參數(shù),支持信號(hào)傳遞信息,同樣支持非實(shí)時(shí)信號(hào)的安裝。函數(shù)sigaction優(yōu)于signal,主要體現(xiàn)在支持信號(hào)帶有參數(shù)。1)函數(shù)signal格式#include<signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum,sighandler_t handler);參數(shù)說(shuō)明signum指定信號(hào)的值。handler指定針對(duì)前面

28、信號(hào)值的處理,可以忽略該信號(hào)(參數(shù)設(shè)置為SIG-IGN);可以采用系統(tǒng)默認(rèn)方式處理信號(hào)(參數(shù)設(shè)置為SIG_DFL);也可以自己實(shí)現(xiàn)處理方式(參數(shù)指定一個(gè)函數(shù)地址)。如果signal調(diào)用成功,返回最后一次為安裝信號(hào)signum而調(diào)用signal時(shí)的handler值;失敗則返回SIG_ERR。2)函數(shù)sigaction梧式#include<signal.h>int sigaction(int signum,const struct sigaction*act,struct sigaction *oldact);參數(shù)說(shuō)明sigaction函數(shù)用于改變進(jìn)程接收到特定信號(hào)后的行為。signu

29、m為信號(hào)的值,可以為除SIGKILL及SIGSTOP外的任何一個(gè)特定有效的信號(hào)(為這兩個(gè)信號(hào)定義自己的處理函數(shù),將導(dǎo)致錯(cuò)誤)。act是指向結(jié)構(gòu)sigaction的一個(gè)實(shí)例的指針,在結(jié)構(gòu)sigaction的實(shí)例中,指定了對(duì)特定信號(hào)的處理,可以為空,進(jìn)程會(huì)以默認(rèn)方式對(duì)信號(hào)處理。這個(gè)參數(shù)最為重要,其中包含了對(duì)指定信號(hào)的處理、信號(hào)所傳遞的信息、信號(hào)處理函數(shù)執(zhí)行過(guò)程中應(yīng)屏蔽哪些函數(shù)。oldact是指向的對(duì)象用來(lái)保存原來(lái)對(duì)相應(yīng)信號(hào)的處理,可指定oldact為NULL。如果把signum和act都設(shè)置為NULL,那么該函數(shù)可用于檢查信號(hào)的有效性。sigact i on結(jié)構(gòu)定義 struct sigactio

30、n void(*sa_handler)(int); void(*sa_sigaction)(int,siginfo_t*,void*); sigset_t sa_mask; unsigned long sa_flags; void(*sa_restorer)(void);數(shù)據(jù)結(jié)構(gòu)中的兩個(gè)元素sa_hanlder和sa_sigaction是指定信號(hào)關(guān)聯(lián)函數(shù)。除了可以是用戶自定義的處理函數(shù)外,還可以為SIGDFL(采用默認(rèn)的處理方式),也可以為SIG_DFL(忽略信號(hào))參數(shù)說(shuō)明sa_handler:由它指定的處理函數(shù)只有一個(gè)參數(shù),即信號(hào)值,所以信號(hào)不能傳遞除信號(hào)值之外的任何信息sa_sigacti

31、on:由它指定的信號(hào)處理函數(shù)帶有三個(gè)參數(shù),是為實(shí)時(shí)信號(hào)而設(shè)的(當(dāng)然同樣支持非實(shí)時(shí)信號(hào)),這個(gè)信號(hào)處理函數(shù)的第一個(gè)參數(shù)(int)為信號(hào)值,第三個(gè)參數(shù)(void*)沒(méi)有使用(POSIX標(biāo)準(zhǔn)中沒(méi)有規(guī)范使用該參數(shù)的標(biāo)準(zhǔn)),第二個(gè)參數(shù)(siginfo_t* )是指向siginfo_t結(jié)構(gòu)的指針,結(jié)構(gòu)中包含信號(hào)攜帶的數(shù)據(jù)值,參數(shù)所指向的結(jié)構(gòu)如下:siginfo_t int si_signo; /*信號(hào)值*/ int si_errno; int si_code; pid_t si_pid;/*發(fā)送信號(hào)的進(jìn)程ID/ uid_t si_uid; int si_status;clock_t si_utime

32、60;clock_t si_stime sigval_t si_value int si_int void* si_ptr void* si_addr int si_band int si_fd sa_mask:指定在信號(hào)處理程序執(zhí)行過(guò)程中,哪些信號(hào)應(yīng)當(dāng)屏蔽,如果不指定SA_NODEFER或者SA_NOMASK標(biāo)志位,默認(rèn)情況下則屏蔽當(dāng)前信號(hào),防止信號(hào)的嵌套發(fā)送。sa_flags :其中包含了許多標(biāo)志位,包括前面提到的SA_NODEFER及SA_NOMASK標(biāo)志位。另一個(gè)比較重要的標(biāo)志位是SA_SIGNFO,當(dāng)設(shè)定

33、了該標(biāo)志位時(shí),表示信號(hào)附帶的參數(shù)可以被傳遞到信號(hào)處理函數(shù)中,因此,應(yīng)該為sigaction結(jié)構(gòu)中的sa_sigaction指定處理函數(shù),而不是為sa_handler指定信號(hào)處理函數(shù),否則設(shè)置該標(biāo)志無(wú)意義。即使為sa_sigaction指定了信號(hào)處理函數(shù),如果不設(shè)置SA_SIGNFO,信號(hào)處理函數(shù)同樣不能得到信號(hào)傳遞過(guò)來(lái)的數(shù)據(jù),在信號(hào)處理函數(shù)中對(duì)這些信息的訪問(wèn)都將導(dǎo)致段錯(cuò)誤(Segment fault)。sa_restorer :現(xiàn)在已經(jīng)不是用了,POSIX標(biāo)準(zhǔn)中并未定義它。下面是一個(gè)信號(hào)處理程序的例子:void ChildHandler (int sig,siginfo_t*sip,

34、 void*notused) int status;printf ( “The process generating the signal is PID:%dn”,sip->si-pid);fflush(stdout); status=0; /*WNOHANG標(biāo)識(shí)如果沒(méi)有子進(jìn)程退出,就不等待*/ if(sip->si_pid=waitpid(sip->si_pid,&status,WNOHANG) /*SIGCHLD并不意味著子進(jìn)程執(zhí)行結(jié)束*/ i f(WIFEXITED(status)|WTERMSIG(status) printf(“The child

35、is gonen”);/*子進(jìn)程退出*/else printf (“Uninterestingn”); /*alive*/else printf (“Uninterestingn”); int main() struct sigaction action;action.sa_sigaction=ChildHandler; /*注冊(cè)信號(hào)處理函數(shù)*/sigfillset(&action.sa_mask);action.sa_flags=SA_SIGINFO; /*向處理函數(shù)傳遞信息*/sigaction(SIGCHLD,&action,NULL;fork();while(1) pr

36、intf(“PID:dn”,getpid(); sleep(1);5.管道將一個(gè)程序或命令的輸出作為另一個(gè)程序或命令的輸入,有兩種方法,一種是通過(guò)一個(gè)臨時(shí)文件將兩個(gè)命令或程序結(jié)合在一起,這種方法由于需要臨時(shí)文件而顯得很累贅;另一種是Linux所提供的管道功能,這種方法比前一種方法好。管道是更加復(fù)雜的IPC(進(jìn)程間通信)工具,它允許數(shù)據(jù)從一個(gè)進(jìn)程向另一個(gè)進(jìn)程單向地流動(dòng)。管道實(shí)際上是利用文件系統(tǒng)中的循環(huán)緩沖區(qū)。我們以生產(chǎn)者消費(fèi)者模式來(lái)使用它,寫(xiě)進(jìn)程相當(dāng)于生產(chǎn)者,而讀進(jìn)程相當(dāng)于消費(fèi)者。一個(gè)進(jìn)程向管道寫(xiě)數(shù)據(jù),如果緩沖區(qū)寫(xiě)滿了,則阻塞寫(xiě)進(jìn)程。另一個(gè)進(jìn)程從管道中讀數(shù)據(jù),如果管道為空,則讀進(jìn)程阻塞。當(dāng)寫(xiě)進(jìn)程

37、關(guān)閉了管道或者該進(jìn)程死掉時(shí),讀進(jìn)程就會(huì)失敗。如果讀進(jìn)程關(guān)閉管道或者該進(jìn)程死掉時(shí),寫(xiě)進(jìn)程就會(huì)失敗。下面說(shuō)明管道如何工作。我們?cè)诟高M(jìn)程中使用pipe系統(tǒng)調(diào)用創(chuàng)建一個(gè)管道,系統(tǒng)調(diào)用的參數(shù)是包含兩個(gè)文件描述符數(shù)組:pfd0和pfd1。(1)和文件描述符一樣,使用pfd0作為管道輸入描述符,使用pfd1作為管道輸出描述符。(2)fork產(chǎn)生子進(jìn)程。(3)現(xiàn)在父進(jìn)程和子進(jìn)程都共享了管道文件描述符。每個(gè)進(jìn)程都關(guān)閉管道的一端(至于哪一端則取決于誰(shuí)作為讀進(jìn)程,誰(shuí)作為寫(xiě)進(jìn)程)。(4)每個(gè)進(jìn)程使用dup2把打開(kāi)的管道描述符復(fù)制給標(biāo)準(zhǔn)輸入或者標(biāo)準(zhǔn)輸出,然后關(guān)閉管道描述符(如果后面還要恢復(fù)標(biāo)準(zhǔn)輸入或者標(biāo)準(zhǔn)輸出,則要把它

38、們事先保留起來(lái))。(5)現(xiàn)在兩個(gè)進(jìn)程可以使用管道通過(guò)標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出進(jìn)行通信了。如果我們?cè)趂ork和exec之間完成這些動(dòng)作,那么可以將進(jìn)程通過(guò)管道連接起來(lái)。下面是一個(gè)管道的例子:int main(int argc,char*argv) int status; int pid2; int pipe_fd2;char*progl_argv4;char*prog2_argv2;/*建立參數(shù)表*/progl_argv0=”*/usr/local/bin/ls”;progl_argv1=”1”;progl_argv2=”/”;progl_argv3NULL;prog2_argv0=”/usr/bin

39、/more”;prog2_argv1=NULL;/*創(chuàng)建管道*/if(pipe(pipe_fd)<0) perror(”pipe failed”); exit(errno);/*為ls命令創(chuàng)建進(jìn)程*/i f(pid0=fork()<0) perror(”Fork failed”); exit(errno);if(!pid0) /*將管道的寫(xiě)描述符復(fù)制給標(biāo)準(zhǔn)輸出,然后關(guān)閉*/close(pipe_fd0);dup2(pipe_fd1,1);close(pipe_fd1);/*執(zhí)行l(wèi)s命令*/execvp(proglargv0,progl_argv);if(pid0) /*父進(jìn)程*/

40、/*為命令more創(chuàng)建子進(jìn)程*/ if(pid1=fork()<0)perror(“Fork failed”);exit(errno);if(!pid1) /*在子進(jìn)程*/ /*將管道的讀描述符復(fù)制給標(biāo)準(zhǔn)輸入*/ close(pipe_fd1); dup2(pipe_fd0,0); close(pipe_fd0); /*執(zhí)行more命令*/ execvp(prog2一argv0,prog2_argv);/*父進(jìn)程*/close(pipe_fd0);close(pipe_fd1);waitpid(pid1,&status,0);printf(”Done waiting for mo

41、re.n”);6.進(jìn)程組、會(huì)話和進(jìn)程組、會(huì)話和作業(yè)控制當(dāng)用戶登錄操作系統(tǒng)以后,操作系統(tǒng)為會(huì)話(sesson)分配一個(gè)終端。會(huì)話是指進(jìn)程運(yùn)行的環(huán)境,即與進(jìn)程相關(guān)的控制終端。shell被分配到前臺(tái)的進(jìn)程組中。進(jìn)程組是指若干個(gè)相關(guān)進(jìn)程的集合它們通常是通過(guò)管道連接的。一個(gè)終端至多能與一個(gè)進(jìn)程組相關(guān)聯(lián)。前臺(tái)進(jìn)程組是會(huì)話中能夠訪問(wèn)控制終端的進(jìn)程組。因?yàn)槊總€(gè)會(huì)話只有一個(gè)控制終端,所以只存在一個(gè)前臺(tái)進(jìn)程組。前臺(tái)進(jìn)程組中的進(jìn)程可以訪問(wèn)標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出。這也意味著組合鍵會(huì)使控制終端把產(chǎn)生的信號(hào)發(fā)送給前臺(tái)進(jìn)程組中的所有進(jìn)程;在Ctrl+C的操作下信號(hào)SIGINT會(huì)發(fā)送給前臺(tái)的每一個(gè)進(jìn)程;在Crtl+Z的操作下信號(hào)

42、SIGTSTP會(huì)發(fā)送給前臺(tái)的每一個(gè)進(jìn)程。這些組合鍵不會(huì)顯示在終端上。同時(shí)還存在著后臺(tái)進(jìn)程組,它們不能訪問(wèn)會(huì)話中控制終端的進(jìn)程組。因?yàn)樗鼈儾荒茉L問(wèn)控制終端,所以不能進(jìn)行終端的I/O操作。如果一個(gè)后臺(tái)進(jìn)程組試圖與控制終端交互,則產(chǎn)生SIGTTOU或者SIGTTIN信號(hào)。默認(rèn)情況下,這些信號(hào)像SIGTSTP一樣會(huì)掛起進(jìn)程。ysh必須處理其子進(jìn)程中發(fā)生的這些變化。進(jìn)程可以使用函數(shù)setpgid,使其加入到某一進(jìn)程組中。進(jìn)程組ID是以組長(zhǎng)的進(jìn)程ID命名的。進(jìn)程組長(zhǎng)是創(chuàng)建該組的第一個(gè)進(jìn)程,用它的進(jìn)程號(hào)作為進(jìn)程組的組號(hào)。進(jìn)程組長(zhǎng)死后進(jìn)程組仍然可以存在。一個(gè)進(jìn)程組可以使用函數(shù)tcsetpgrp成為前臺(tái)進(jìn)程組。

43、這個(gè)函數(shù)調(diào)用使指定的進(jìn)程組成為前臺(tái)進(jìn)程組,這不僅會(huì)影響到組本身也會(huì)影響到該組的任何一個(gè)子進(jìn)程。如果進(jìn)程調(diào)用函數(shù)setsid創(chuàng)建了一個(gè)新的會(huì)話,那么它就成為了會(huì)話組長(zhǎng)和進(jìn)程組組長(zhǎng)。對(duì)于一個(gè)要與終端交互的新會(huì)話來(lái)說(shuō),必須為它分配一個(gè)新的終端。由于是在系統(tǒng)shell(csh、sh、bash等)下執(zhí)行ysh,這會(huì)使ysh代替原來(lái)的shell,成為前臺(tái)進(jìn)程組中的惟一進(jìn)程。讓某個(gè)進(jìn)程組成為前臺(tái)進(jìn)程組,不僅保證它能夠與控制終端保持聯(lián)系,還保證了前臺(tái)進(jìn)程組中的每一個(gè)進(jìn)程都能從終端接收到控制信號(hào),如SIGTSTP。簡(jiǎn)單的方法是,把所有的子進(jìn)程放在同一個(gè)進(jìn)程組中,在創(chuàng)建它們時(shí)屏蔽SIGTSTP信號(hào),這樣只有she

44、ll才能接收到這個(gè)信號(hào),然后shell發(fā)出與SIGTSTP作用相同的(但沒(méi)有屏蔽的)SIGSTOP信號(hào)給某個(gè)子進(jìn)程。這種辦法在某些時(shí)候會(huì)使程序運(yùn)行失敗,比如在自己的ysh中再次運(yùn)行ysh,但這種辦法對(duì)于本實(shí)驗(yàn)來(lái)說(shuō)是不涉及的。下面是一個(gè)進(jìn)程組的例子:#include<stdio.h>#include<signal.h>#include<stddef.h>#include<sys/wait.h>#include<sys/ioctl.h>#include<sys/termios.h>/*本程序體現(xiàn)了函數(shù)tcsetgrp和setp

45、grp的用法,但沒(méi)處理SIGTTIN、SIGTTOU信號(hào)*/int main() int status; int cpid; int ppid; char buf256; sigset_t blocked;ppid=getpid();if (!(cpid=fork() setpgid(0,0); tcsetpgrp (0, getpid(); execl (“/bin/vi”,”vi”,NULL); exit (1); if(cpid<0) exit(1);setpgid(cpid,cpid); /*設(shè)置進(jìn)程組*/tcsetpgrp(0,cpid); /*設(shè)置控制終端為子進(jìn)程擁有*/wa

46、itpid(cpid,NULL,0);tcsetpgrp(0,ppid)jwhile(1) memset(buf,0,256); fgets(buf,256,stdin); puts(”ECHO:”); puts(buf); puts(”n”);1.4實(shí)驗(yàn)環(huán)境本實(shí)驗(yàn)的程序用C語(yǔ)言編寫(xiě),使用makefile文件編譯整個(gè)程序,生成一個(gè)名為ysh可執(zhí)行程序,在終端輸入“./ysh”即可執(zhí)行。makefile文件的內(nèi)容如下:ysh: ysh.ccc ysh.co ysh1.5程序的實(shí)現(xiàn)1.5.1數(shù)據(jù)結(jié)構(gòu)在這個(gè)程序中,我們用到的數(shù)據(jù)結(jié)構(gòu)主要有循環(huán)數(shù)組和鏈表。1循環(huán)數(shù)組在history命令中,用數(shù)組來(lái)存放

47、我們輸入過(guò)的歷史命令。假設(shè)我們?cè)O(shè)定一個(gè)能夠記錄12條歷史紀(jì)錄的數(shù)組如圖5.2所示。數(shù)組的定義如下: typedef struct ENV_HISTROY int start=0; int end=0; char his_cmd12100; ENV_HISTORY; ENV_HISTORY envhis:可以看到,每個(gè)his_cmdi對(duì)應(yīng)圖中一塊圓環(huán)(end不一定為12),一共12塊,能存放十二條命令。當(dāng)用戶輸入一命令時(shí),只須執(zhí)行如下語(yǔ)句即可將輸入命令存放進(jìn)相應(yīng)數(shù)組中:envhis.end=envhisend+l;strcpy(his_cmdenvhis.end,input)。但是還需要考慮如圖

48、5.3所示的情況 在這種情況下,end=12,當(dāng)我們?cè)谳斎胍粭l命令時(shí),如果還是用上述兩條命令進(jìn)行處理end=end+l,則end=13就會(huì)出錯(cuò)。所以應(yīng)對(duì)程序進(jìn)一步修改:envhis.end=(envhis.end+1)%12;if(envhis.end=envhis.start) envhis.start=(envhis.start+1)12;strcpy(envhis.his_cmdenvhisend,input);經(jīng)過(guò)這樣的處理,就可以達(dá)到循環(huán)的目的了2鏈表由于我們把作業(yè)以鏈表的形式保存起來(lái),所以在處理jobs命令時(shí),實(shí)際上就是對(duì)鏈表的操作。 首先定義鏈表的節(jié)點(diǎn):typedef struc

49、t NODE(pid_t pid;/*進(jìn)程號(hào)*/char cmd100;/*命令名*/char state10; /*作業(yè)狀態(tài)*/struct NODE 1ink;/*下一節(jié)點(diǎn)指針*/ )NODE;NODE*head,*end;head指針指向鏈表表頭,end指針指向鏈表尾 1.5.2程序結(jié)構(gòu)在shell命令里,我們將ysh中的命令分成4種:普通命令,重定向命令,管道命令和內(nèi)部命令。這4種命令的分析和執(zhí)行各有不同,每一種命令的分析執(zhí)行程序都應(yīng)包括:初始化環(huán)境,打印提示符,獲取用戶輸入的命令,解析命令,尋找命令文件和執(zhí)行命令幾個(gè)步驟,具體程序流程如圖5.4所示。1初始化環(huán)境程序一開(kāi)始,需要對(duì)一些

50、環(huán)境變量進(jìn)行初始化。比如將查找路徑放入envpath中,初始化history和jobs的頭尾指針等。這部分工作在程序中由函數(shù)init_environ來(lái)完成。void init_environ() int fd,n,i; char buf8 0; if(fd=open(”ysh_profile”,O_RDONLY,660)=-1) printf(”init environ variable errorn”); exit(1); while(n=getline(fd,buf) getenviron(n,buf); envhis.start=0; envhis.end=0; head=end=NUL

51、L;可以看到這個(gè)函數(shù)打開(kāi)一個(gè)名為ysh_profile的文件,它是我們定義的配置文件。然后調(diào)用了另外兩個(gè)函數(shù)getline(fd,buf)和getenviron(n,buf)。getline(fd,buf)的作用是讀取一行的信息到buf中。getenviron(n,buf)的作用是將getline(fd,buf)讀到buf中的信息以冒號(hào)分開(kāi),分別放于envpath中(見(jiàn)ysh.h中的定義),為后面查找命令做準(zhǔn)備。這樣命令查找的準(zhǔn)備工作就做好了。接下來(lái)函數(shù)返回到函數(shù)init_environ。接下來(lái)的語(yǔ)句將envhis.start和envhis.end置0。這兩條語(yǔ)句的作用是初始化保存histor

52、y命令鏈表的頭尾指針。而語(yǔ)句head=end=NULL是初始化保存jobs命令鏈表的頭尾指針。至此,程序的初始化工作基本完成。接下來(lái)就進(jìn)入一個(gè)while循環(huán),就和一般的shell一樣,當(dāng)一個(gè)命令執(zhí)行完或放到后臺(tái)后,shell就可獲取新的用戶輸入命令。2解析命令在這里對(duì)讀到input數(shù)組中的命令進(jìn)行分析,以獲取命令和參數(shù)。ysh中的命令分成4種:普通命令,重定向命令,管道命令和內(nèi)部命令。我們對(duì)管道和重定向命令單獨(dú)處理。 for(i=0,j=0,k=0;i<=input_len;i+) i f(inputi=<| inputi=>| inputi=|) if(inputi=|) pipel(input,input_len); add_history(input); free(input); else redirect(input,input_len); add_history(input); free(input); is_pr=l; break; 該for循環(huán)就把帶“>”、“<和|符號(hào)的管道和重定向命令單獨(dú)處理。同時(shí)把is_pr這個(gè)管道和重定向命令的標(biāo)志置l。然后分別調(diào)用redirect(input,inpuuen)和pipel(input, input_len)兩個(gè)函數(shù)來(lái)處理這兩類命令。這兩個(gè)函數(shù)在后面會(huì)討論。對(duì)于

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論