2023年全國碩士研究生考試考研英語一試題真題(含答案詳解+作文范文)_第1頁
已閱讀1頁,還剩40頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、<p>  本科畢業(yè)設計(論文)</p><p><b>  (二零 屆)</b></p><p>  Linux Shell的設計與實現(xiàn)</p><p>  所在學院 </p><p>  專業(yè)班級 計算機科學與技術 </p

2、><p>  學生姓名 學號 </p><p>  指導教師 職稱 </p><p>  完成日期 年 月 </p><p>  摘要:shell是用戶和Linux操作系統(tǒng)之間的接口。shell是一個命令語言解釋器。無

3、論何時用戶輸入一個命令都將由Linux shell解釋。當用戶登錄一個Unix系統(tǒng)時,用戶可以訪問事件日志文件來了解什么是shell及其工作原理。用戶通常通過Unix機器上的終端端口(無論是一個老式的串行終端或例如xterm的終端仿真程序)來訪問該機器的Unix系統(tǒng)。</p><p>  本文在UBUNTU下編寫的shell程序具有如下幾個功能:</p><p><b>  程序

4、執(zhí)行:</b></p><p>  用戶在終端所有請求執(zhí)行的程序都由shell負責。用戶每次輸入一行命令后,shell分析它并決定如何執(zhí)行。每一行命令都要遵循shell規(guī)定的基本格式:程序名 參數(shù)</p><p><b>  變量和文件名替換:</b></p><p>  像其他任何的編程語言一樣,shell允許我們指定變量的值

5、。在$符號前定義了變量的值,shell則在命令行中用指定的值替換變量。</p><p>  標準輸入/輸出和I/O重定向:</p><p>  標準輸入/輸出和重定向是shell在命令行中的主要責任。當發(fā)生特別事件時它掃描命令行中的重定向字符<或>或>>。</p><p><b>  管道:</b></p>

6、<p>  正如shell掃描命令行中的重定向字符,它也掃描管道字符|。對于每一次找到這樣的字符,它連接程序標準輸出前面的|和程序標準輸入后面的“|”。</p><p>  關鍵詞:Linux;Shell;標準輸入輸出;I/O重定向;管道</p><p>  Design and Implementation of Linux Shell</p><p&g

7、t;  Abstract:The shell is a program used to interface between the user and the Linux kernel. The shell is a command line interpreter. Whenever we enter a command it is interpreted by the Linux shell. To understand what t

8、he shell is, and what it actually does,we review the sequence of events that happen when we log into a Unix system.We normally access a Unix system through a terminal (be it an old fashioned serial terminal, or a termina

9、l emulator such as a xterm window) connected to certain ports on</p><p>  The shell has several responsibilities namely:</p><p>  Program execution</p><p>  The shell is responsible

10、 for the execution of all programs that we request from our terminal. Each time we type in a line to the shell, the shell analyses the line and then determines what to do. As far as the shell is concerned, each line foll

11、ows the same basic format:</p><p>  program-name arguments</p><p>  Variable and File name substitution</p><p>  Like any other programming language, the shell allows us to assign

12、values to variables. Whenever we specify one of these variables on the command line, preceded by an dollar sign, the shell substitues the value that was assigned to the variable at that point</p><p>  Standa

13、rd Input/Output and I/O redirection</p><p>  It is the shells’ responsibility to take care of input and output redirection on the command line. It scans the command line for the occurrence of the special red

14、irection characters <, > or >>.</p><p><b>  Pipes</b></p><p>  Just as the shell scans the command line for redirection characters, it also scans for the pipe character |

15、. For each such character it finds, it connects the standard output from the program preceding the | to the standard input of the program following the” |”.</p><p>  Keywords: Linux; Shell; Standard Input/Ou

16、tput; I/O redirection; Pipes</p><p><b>  目錄</b></p><p><b>  1.引 言1</b></p><p>  1.1Linux系統(tǒng)中Shell的相關概念1</p><p>  1.2Shell的歷史1</p>&l

17、t;p>  2.開發(fā)環(huán)境與開發(fā)工具介紹3</p><p>  2.1開發(fā)環(huán)境--Ubuntu3</p><p>  2.2開發(fā)工具—GCC3</p><p>  3.基本功能和實現(xiàn)方法4</p><p>  3.1shell程序形式4</p><p>  3.2外部命令和內部命令4</p

18、><p><b>  3.3命令行5</b></p><p>  3.4前臺和后臺作業(yè)5</p><p><b>  3.5特殊鍵5</b></p><p>  3.6分析用戶輸入5</p><p>  3.6.1分隔符和特殊字符5</p>&l

19、t;p>  3.6.2內部命令6</p><p>  3.6.3I/O重定向6</p><p>  3.6.4管道和協(xié)同程序6</p><p>  3.6.5后臺作業(yè)6</p><p>  3.6.6語法7</p><p>  3.7程序的實現(xiàn)7</p><p> 

20、 3.7.1數(shù)據(jù)結構7</p><p>  3.7.2程序結構8</p><p>  4.主要源代碼介紹16</p><p><b>  5.結束語22</b></p><p><b>  參考文獻23</b></p><p>  致謝錯誤!未定義書簽。&

21、lt;/p><p><b>  引 言</b></p><p>  Linux系統(tǒng)中Shell的相關概念</p><p>  Linux系統(tǒng)的shell作為操作系統(tǒng)的外殼,為用戶提供使用操作系統(tǒng)的接口。它是命令語言、命令解釋程序及程序設計語言的統(tǒng)稱[1]。</p><p>  shell是用戶和Linux內核之間的接口程序,

22、如果把Linux內核想象成一個球體的中心,shell就是圍繞內核的外層。當從shell或其他程序向Linux傳遞命令時,內核會做出相應的反應。</p><p>  實際上Shell是一個命令解釋器,它解釋由用戶輸入的命令并且把它們送到內核。不僅如此,Shell有自己的編程語言用于對命令的編輯,它允許用戶編寫由shell命令組成的程序。Shell編程語言具有普通編程語言的很多特點,比如它也有循環(huán)結構和分支控制結構等

23、,用這種編程語言編寫的Shell程序與其他應用程序具有同樣的效果[2]。</p><p>  管道是Linux 支持的最初Unix IPC形式之一,具有以下特點: </p><p>  管道是半雙工的,數(shù)據(jù)只能向一個方向流動;需要雙方通信時,需要建立起兩個管道; 只能用于父子進程或者兄弟進程之間(具有親緣關系的進程);單獨構成一種獨立的文件系統(tǒng):管道對于管道兩端的進程而言,就是一個文件,但

24、它不是普通的文件,它不屬于某種文件系統(tǒng),而是自立門戶,單獨構成一種文件系統(tǒng),并且只存在于內存中。一個進程向管道中寫的內容被管道另一端的進程讀出,寫入的內容每次都添加在管道緩沖區(qū)的末尾,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)。</p><p>  執(zhí)行一個shell命令行時通常會自動打開三個標準文件,即標準輸入文件(stdin),通常對應終端的鍵盤;標準輸出文件(stdout)和標準錯誤輸出文件(stderr),這兩個

25、文件都對應終端的屏幕。進程將從標準輸入文件中得到輸入數(shù)據(jù),將正常輸出數(shù)據(jù)輸出到標準輸出文件,而將錯誤信息送到標準錯誤文件中。</p><p>  輸入重定向是指把命令(或可執(zhí)行程序)的標準輸入重定向到指定的文件中。也就是說,輸入可以不來自鍵盤,而來自一個指定的文件。輸出重定向是指把命令(或可執(zhí)行程序)的標準輸出或標準錯誤輸出重新定向到指定文件中。這樣,該命令的輸出就不顯示在屏幕上,而是寫入到指定</p>

26、;<p><b>  文件中[3]。</b></p><p><b>  Shell的歷史</b></p><p>  標準的 UNIX shell 是V7(AT&T的第七版)UNIX,在1979 年底被提出,且以它的創(chuàng)造者 Stephen Bourne 來命名。Bourne shell 是以 Algol 這種語言為基礎來設

27、計,主要被用來做自動化系統(tǒng)管理工作。雖然 Bourne shell 以簡單和速度而受歡迎,但它缺少許多交談性使用的特色,例如歷程、別名和工作控制。</p><p>  C shell 是在加州大學柏克來分校于70年代末期發(fā)展而成,而以2BSD UNIX的部分發(fā)行。這個 shell 主要是由 Bill Joy 寫成,提供了一些在標準 Bourne shell 所看不到的額外特色。C shell 是以C 程序語言作為

28、基礎,且它被用來當程序語言時,能共享類似的語法。它也提供在交談式運用上的改進,例如命令列歷程、別名和工作控制。因為 C shell 是在大型機器上設計出來,且增加了一些額外功能,所以 C shell 有在小型機器上跑得較慢,即使在大型機器上跟 Bourne shell 比起來也顯得緩慢。</p><p>  有了 Bourne shell 和 C shell 之后,UNIX 使用者就有了選擇,且爭論那一個 she

29、ll 較好。AT&T 的David Korn 在 80 年代中期發(fā)明了 Korn shell,在 1986 年發(fā)行且在 1988 年成為正式的部分 SVR4 UNIX。Korn shell 實際上是 Bourne shell 的超集,且不只可在 UNIX 系統(tǒng)上執(zhí)行,同時也可在 OS/2、VMS、和 DOS上執(zhí)行。它提供了和 Bourne shell 向上兼容的能力,且增加了許多在 C shell 上受歡迎的特色,更增加了速度和

30、效率。 Korn shell 已歷經許多修正版,要找尋用戶使用的是那一個版本可在 ksh 提示符號下按 Ctrl-v 鍵。</p><p>  在大部份的UNIX系統(tǒng)中,三種著名且廣被支持的shell 是Bourne shell(AT&T shell,在 Linux 下是BASH)、C shell(Berkeley shell,在 Linux 下是TCSH)和 Korn shell(Bourne shel

31、l的超集)。這三種 shell 在交互(interactive)模式下的表現(xiàn)相當類似,但作為命令文件語言時,在語法和執(zhí)行效率上就有些不同了。</p><p>  Bourne shell 是標準的 UNIX shell,以前常被用來做為管理系統(tǒng)之用。大部份的系統(tǒng)管理命令文件,例如 rc start、stop 與shutdown 都是Bourne shell 的命令,且在單用戶模式(single user mode

32、)下以 root 登錄時它常被系統(tǒng)管理者使用。Bourne shell 是由 AT&T 發(fā)展的,以簡潔、快速著名。 Bourne shell 提示符號的默認值是 $。</p><p>  C shell 是柏克萊大學(Berkeley)所開發(fā)的,且加入了一些新特性,如命令列歷程(history)、別名(alias)、內建算術、文件名完成(filename completion)和作業(yè)控制(job cont

33、rol)。對于常在交互模式下執(zhí)行 shell 的使用者而言,他們較喜愛使用 C shell;但對于系統(tǒng)管理者而言,則較偏好以 Bourne shell 來做命令,因為 Bourne shell 命令比 C shell 命令來的簡單及快速。C shell 提示符號的默認值是 %。</p><p>  Korn shell 是Bourne shell 的超集(superset),由 AT&T 的 David

34、Korn 所開發(fā)。它增加了一些特色,比 C shell 更為先進。Korn shell 的特色包括了可編輯的歷程、別名、函式、正規(guī)表達式萬用字符(regular expression wildcard)、內建算術、作業(yè)控制(job control)、協(xié)作處理(coprocessing)和特殊的糾錯功能。Bourne shell 幾乎和 Korn shell 完全向上兼容(upward compatible),所以在 Bourne she

35、ll 下開發(fā)的程序仍能在 Korn shell 上執(zhí)行。Korn shell 提示符號的默認值也是 $。在 Linux 系統(tǒng)使用的 Korn shell 叫做 pdksh,它是指 Public Domain Korn Shell。</p><p>  除了執(zhí)行效率稍差外,Korn shell 在許多方面都比 Bourne shell 為佳;但是,若將 Korn shell 與 C shell 相比就很困難,因為二

36、者在許多方面都各有所長,就效率和容易使用上看,Korn shell 是優(yōu)于 C shell,相信許多使用者對于 C Shell 的執(zhí)行效率都有負面的印象[4][5]。</p><p>  在shell 的語法方面,Korn shell 是比較接近一般程序語言,而且它具有子程序的功能及提供較多的資料型態(tài)。至于 Bourne shell,它所擁有的資料型態(tài)是三種 shell 中最少的,僅提供字符串變量和布爾型態(tài)。在整

37、體考量下 Korn shell 是三者中表現(xiàn)最佳者,其次為 C shell,最后才是 Bourne shell,但是在實際使用中仍有其它應列入考慮的因素,如速度是最重要的選擇時,很可能應該采用 Bourne shell,因它是最基本的 shell,執(zhí)行的速度最快。</p><p>  開發(fā)環(huán)境與開發(fā)工具介紹</p><p>  2.1 開發(fā)環(huán)境--Ubuntu</p>&l

38、t;p>  Ubuntu 項目完全遵從開源軟件開發(fā)的原則;并且鼓勵人們使用、完善并傳播開源軟件。也就是Ubuntu目前是并將永遠是免費的。 然而,這并不僅僅意味著零成本,自由軟件的理念是人們應該以所有“對社會有用”的方式自由地使用軟件。“自由軟件”并不只意味著您不需要為其支付費用,它也意味著您可以以自己想要的方式使用軟件:任何人可以任意方式下載、修改、修正和使用組成自由軟件的代碼。因此,除去自由軟件常以免費方式提供這一事實外,這種

39、自由也有著技術上的優(yōu)勢:進行程序開發(fā)時,就可以使用其他人的成果或以此為基礎進行開發(fā)。對于非自由軟件而言,這點就無法實現(xiàn),進行程序開發(fā)時,人們總得白手起家[6]?;谏鲜鲈?,自由軟件的開發(fā)是迅捷、高效和激動人心的!</p><p>  2.2 開發(fā)工具—GCC</p><p>  GNU C 編譯器(GCC)是一個在Unix或Linux等系統(tǒng)上運行的功能強大的編譯器,是最重要的軟件開發(fā)工具

40、,是自由軟件項目中極具代表性的項目。最初,GCC被認為是GNU C Compiler的縮寫。GCC編譯后代的執(zhí)行效率比一般的編譯器效率要高20%-30%[6][7]。</p><p>  經過10多年的發(fā)展,GCC不僅支持C語言,還支持Ada、C++、Java、Objective C、Pascal、COBOL等開發(fā)語言。目前,人們不再將GCC看成是一個純粹的C語言的編譯器。GCC也變成GNU Compiler C

41、ollection(即GNU編譯器族)的縮寫。</p><p>  GCC能夠對幾乎所有的硬件平臺提供支持,并有極好的編譯性能。GCC的編譯過程分為預處理、生成匯編代碼、生成目標代碼和鏈接成可執(zhí)行文件等4個步驟。使用GCC編譯代碼時,可以通過參數(shù)控制其編譯過程,讓其停止在上訴4個步驟之一。</p><p>  由于Unix平臺的高度可移植性,GCC基本上在各種常見的Unix平臺上都能見到。

42、尤其近些年來,伴隨著Linux系統(tǒng)的迅速推廣和流行,在其上運行的GCC也愈發(fā)受到人們的青睞,它的不斷完善和發(fā)展使許多商業(yè)編譯器都相形見絀。</p><p><b>  基本功能和實現(xiàn)方法</b></p><p><b>  shell程序形式</b></p><p>  shell程序設計不包括對配置文件和命令行參數(shù)的支持

43、。shell提供一個命令提示符,如lee>,表示接收用戶的輸入,每次執(zhí)行完成后再打印下一個命令提示符lee>。但用戶沒有輸入時,shell需要隨時處于等待輸入狀態(tài) ,同時在屏幕上顯示一些必要的信息。</p><p><b>  外部命令和內部命令</b></p><p>  在大多數(shù)情況下,用戶輸入的命令時執(zhí)行存儲在文件系統(tǒng)中的可執(zhí)行程序,稱之為外部命令或

44、外部程序[8]。shell應當支持在執(zhí)行這些程序時可以將輸入/輸出重新定向到一個文件,并允許若干個程序使用管道串聯(lián)起來。本設計把管道連接起來的符合命令以及單獨使用的命令統(tǒng)稱為作業(yè)。</p><p>  外部命令的形式是一系列分隔的字符串。第一個字符串是可執(zhí)行程序的名字,其他的是傳給這個外部程序的參數(shù)。如果第一個字符串所聲明的可執(zhí)行文件并不存在或者不可執(zhí)行,則認為這個命令是錯誤的。</p><p

45、>  解釋器支持一些內部命令,這些命令在shell程序內部實現(xiàn)特定的動作,下面是一些內部命令,如果用戶提交了一個內部命令,shell將按照下面的描述執(zhí)行相應的動作。</p><p>  1.exit:結束所有的子進程并退出shell。</p><p>  2.jobs:打印當前正在后臺執(zhí)行的作業(yè)和掛起的作業(yè)信息。輸出信息應采用便于用戶理解的格式。jobs自身是一條內部命令,所以不需要

46、顯示在輸出上。</p><p>  3.fg %<int>:把<int>所標識的作業(yè)放到前臺運行。如果這個作業(yè)原來已停止,那么讓它繼續(xù)運行。Shell在打印新的命令提示符之前等待前臺運行的子進程結束。</p><p>  4.bg %<int>:在后臺執(zhí)行<int>標識的已掛起的進程。</p><p>  5.ls

47、 列出目錄中所有文件的名字,以及這些文件的其他信息。</p><p>  ls – a 列出的內容包含以“.”開頭的文件</p><p>  ls – lu 顯示最后訪問時間</p><p>  ls – s 顯示以塊為單位的文件大小</p><p>  ls – t 輸出時按時間排序</p><p> 

48、 ls – F 顯示文件類型</p><p>  6.cp 能夠復制文件,典型的用法是</p><p>  $ cp source-file target-file</p><p>  如果target-file所指定的文件不存在,cp 就創(chuàng)建這個文件,如果已經存在就覆蓋,target-file的內容和source-file相同。</p>&l

49、t;p>  6.rm 用戶可以用rm命令刪除不需要的目錄及文件。該命令的功能是刪除一個目錄中的一個或多個文件或目錄,他也可以將某個目錄及其下的所有文件及子目錄均刪除。對于鏈接文件,只是斷開了鏈接,源文件保持不變。</p><p>  7.cat 顯示或連結一般的ascii文本文件</p><p>  cat text 顯示text這個文件; </p&g

50、t;<p>  cat file1 file2 依順序顯示file1,file2的內容; </p><p>  cat file1 file2>file3 把file1,file2的內容結合起來,再“重定向(>)”到file3文件中。</p><p><b>  命令行</b></p><p>  當用戶在提

51、示符后面輸入命令時,輸入的整行內容叫做“命令行字符串”,shell保存每一條命令行字符串,直到它表示的作業(yè)執(zhí)行結束,其中包括后臺作業(yè)和被掛起的作業(yè)。</p><p>  shell給每一個命令行字符串賦一個非負整數(shù)標識符。這個整數(shù)用來標識存儲作業(yè)的數(shù)據(jù)結構,作業(yè)的數(shù)據(jù)結構包含整個命令行字符串所表示的內容。一旦命令行字符串代表的作業(yè)執(zhí)行結束,shell就刪掉表示這個作業(yè)的數(shù)據(jù)結構。標識符可以循環(huán)使用。</p&

52、gt;<p>  對于包含內部命令的命令行字符串。不為它們建立作業(yè)的數(shù)據(jù)結構,因為它們本身的內容全部包含在shell程序中。</p><p><b>  前臺和后臺作業(yè)</b></p><p>  shell能夠執(zhí)行前臺和后臺作業(yè)。shell在前臺作業(yè)執(zhí)行結束之前要一直等待。而在開始執(zhí)行后臺作業(yè)時立刻打印出提示符lee>,讓用戶輸入下一條命令。&l

53、t;/p><p>  前臺作業(yè)的執(zhí)行總是優(yōu)先于執(zhí)行一個后臺作業(yè),shell不需要在打印下一個提示符前等待后臺作業(yè)的完成,無論是否有后臺作業(yè)的執(zhí)行,只要完成一個前臺作業(yè),便立即輸出提示符lee>。一個后臺作業(yè)結束時,shell在作業(yè)執(zhí)行結束后立刻打印出一條提示信息。下面語法中會在命令語法分析程序中介紹相應的語法來支持后臺作業(yè)。</p><p><b>  特殊鍵</b>

54、;</p><p>  又稱組合鍵。通過終端驅動程序,特殊的組合鍵可以產生信號給shell,程序對這些信號做出適當?shù)捻憫?lt;/p><p>  Ctrl+Z:產生SIGTSTP信號,這個信號不是掛起shell,而是讓shell掛起在前臺運行的作業(yè),如果沒有任何前臺作業(yè),則該特殊鍵無效。</p><p>  Ctrl+C:產生SIGINT信號,這個信號不是中止shel

55、l,而是通過shell發(fā)出信號殺死前臺作業(yè)中的進程。如果沒有任何前臺作業(yè),則該特殊鍵無效。</p><p><b>  分析用戶輸入</b></p><p><b>  分隔符和特殊字符</b></p><p>  分析用戶輸入的語法分析器具有下面介紹的功能,它能夠檢查用戶的輸入錯誤。如果用戶輸入的某些地方出錯了,shel

56、l提供合理的出錯信息。</p><p>  shell每次接受用戶輸入的一行命令,在用戶按下回車鍵(Enter)后開始執(zhí)行分析動作。空命令不能產生任何操作,而只是打印一個新提示符。</p><p>  定義空格符為分隔符,shell可以處理命令行中間和前后出現(xiàn)的重復空格符。</p><p>  某些字符被稱作“元字符”,它們在用戶輸入的上下文中具有特定的含義。這些字

57、符包括“&、|、<、>”。shell假設這些字符不會出現(xiàn)在程序名、參數(shù)名和文件名中,它們是shell的保留字符。</p><p><b>  內部命令</b></p><p>  如果命令行字符串符合前面介紹的內部命令的格式,它就作為一個內部命令被解釋。如果不是,就要考慮可能是外部程序的執(zhí)行,或者是錯誤的。</p><p>

58、<b>  I/O重定向</b></p><p>  輸入重定向是指把命令(或可執(zhí)行程序)的標準輸入重定向到指定的文件中。也就是說,輸入可以不來自鍵盤,而來自一個指定的文件。輸出重定向是指把命令(或可執(zhí)行程序)的標準輸出或標準錯誤輸出重新定向到指定文件中。這樣,該命令的輸出就不顯示在屏幕上,而是寫入到指定文件中。</p><p>  一個程序命令后面可能還跟有元字符“

59、<”或“>”,它們是重定向符號,而在重定向符號后面還跟著一個文件名。在“<”的情況下,程序的輸入被重定向到一個指定的文件中。在“>”的情況下,程序的輸出被重定向到一個指定的文件中。如果輸入文件不存在,則認為是出現(xiàn)了錯誤。</p><p><b>  管道和協(xié)同程序</b></p><p>  管道是半雙工的,數(shù)據(jù)只能向一個方向流動;需要雙方通信

60、時,需要建立起兩個管道; 只能用于父子進程或者兄弟進程之間(具有親緣關系的進程); 單獨構成一種獨立的文件系統(tǒng):管道對于管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬于某種文件系統(tǒng),而是自立門戶,單獨構成一種文件系統(tǒng),并且只存在與內存中。 數(shù)據(jù)的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區(qū)的末尾,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)。</p><p> 

61、 在一條命令行中當若干個命令被元字符“|”分隔開時,這個元字符代表著管道符號。在這種情況下,shell為每一個命令都創(chuàng)建一個進程,并把它們的輸入/輸出用管道連接起來。例如下面這條命令行:</p><p>  progA argA1 argA2 < inflie | progB argB1 > outflie</p><p>  應生成progA和progB兩個進程,progA的

62、輸入來自文件inflie,progA的輸出時progB的輸入,并且progB的輸出是文件outfile。這種命令行可以通過進程間通信中的管道來實現(xiàn)。</p><p>  含有一個和多個管道的命令會在如下幾種情況下產生錯誤:</p><p>  當其任何一個子程序執(zhí)行出錯時。</p><p>  除了第一個子程序以外的其他子程序的輸入被重定向。</p>

63、<p>  除了最后一個子程序以外的其他子程序的輸出被重定向。</p><p>  由管道連接的多個進程所組成的作業(yè)只有當其所有的子進程都執(zhí)行完畢后才算結束。</p><p><b>  后臺作業(yè)</b></p><p>  當用戶需要在后臺執(zhí)行一個作業(yè)時,可以在作業(yè)命令的后面加上元字符“&”。用戶以該種方式輸入的作業(yè)命令都必

64、須放在后臺執(zhí)行,同時并不影響用戶與終端的交互。</p><p><b>  語法</b></p><p>  下面給出的語法規(guī)則描述圖提供了控制用戶輸入的一種更規(guī)范的描述形式。如果使用Linux中的現(xiàn)有工具lex和yacc來建立命令分析器,還需對這個語法進行修改,以便使它支持LALR(1)(Look Ahead Left Reduction)分析方法。這個語法并不包括

65、特殊鍵,因為它們不會再用戶輸入時顯示出來,而是需要單獨處理。</p><p>  CommandLine代表用戶的合法輸入,它作為shell要執(zhí)行的一條“指令”。這里假設存在一個詞法分析器,它將空格符作為分隔符,并識別元字符作為一個詞法記號等。</p><p>  CommandLine:=NULL</p><p>  FgCommandLine</p&g

66、t;<p>  FgCommandLine &</p><p>  FgCommandLine:=SimpleCommand</p><p>  FirstCommand MidCommand LastCommand</p><p>  SimpleCommand:=ProInvocation InputRedirect OutputR

67、edirect</p><p>  FirstCommand:=ProInvocation InputRedirect</p><p>  MidCommand:=NULL</p><p>  | ProInvocation MidCommand </p><p>  LastCommand:=| ProInvocation Ou

68、tputRedirect</p><p>  ProInvocation:=ExecFile Args</p><p>  InputRedirec:=NULL</p><p><b>  <STRING</b></p><p>  OutputRedirect:=NULL</p><

69、p><b>  >STRING</b></p><p>  ExecFile:=STRING</p><p>  Args:=NULL</p><p>  STRING Args</p><p>  STRING:=NULL</p><p>  CHAR STRI

70、NG</p><p>  CHAR:=0|1|...|9|a|b|...|z|A|B|...|Z</p><p><b>  程序的實現(xiàn)</b></p><p><b>  數(shù)據(jù)結構</b></p><p>  本設計中用到的數(shù)據(jù)結構主要有循環(huán)數(shù)組和鏈表。</p><p&

71、gt;<b>  循環(huán)數(shù)組</b></p><p>  在history命令中,用數(shù)組來存放輸入過的歷史命令。</p><p><b>  鏈表</b></p><p>  由于要把作業(yè)以鏈表的形式保存起來,所以在處理jobs命令時,實際上就是對鏈表的操作。</p><p>  首先定義鏈表的節(jié)點:

72、</p><p>  typedef struct NODE{</p><p>  pid_t pid;//進程號</p><p>  char cmd[100];//命令名</p><p>  char state[10];//作業(yè)狀態(tài)</p><p>  struct NODE *link;//下一節(jié)點

73、指針</p><p><b>  }NODE;</b></p><p>  NODE *head,*end;</p><p>  head指針指向鏈表表頭,end指針指向鏈表尾。</p><p><b>  程序結構</b></p><p>  在shell命令里,shell

74、中的命令分成4種:普通命令,重定向命令,管道命令和內部命令。這4種命令的分析和執(zhí)行各有不同,每一種命令的分析執(zhí)行程序都包括:初始化環(huán)境,打印提示符,獲取用戶輸入的命令,解析命令,尋找命令文件和執(zhí)行命令幾個步驟,具體程序流程圖如下圖所示。</p><p><b> ?。ǔ绦蛟O計流程圖)</b></p><p><b>  初始化環(huán)境</b><

75、/p><p>  程序一開始,需要對一些環(huán)境變量進行初始化。比如將查找路徑放入envpath[]中,初始化history和jobs的頭尾指針等。這部分工作在程序中由函數(shù)init_environ來完成。</p><p>  void init_environ()</p><p><b>  {</b></p><p>  in

76、t fd,n,i;</p><p>  char buf[80];</p><p>  if((fd=open("lee_profile",O_RDONLY,600))==-1){//打開保存查找路徑的lee_profile文件</p><p>  printf("init environ variable error \n"

77、;);</p><p><b>  exit(1);</b></p><p><b>  }</b></p><p>  while(n=getline1(fd,buf)){</p><p>  getenviron(n,buf);//getenviron()函數(shù)</p><

78、p><b>  }</b></p><p>  envhis.start=0;//初始化history循環(huán)數(shù)組</p><p>  envhis.end=0;</p><p>  head=end=NULL;//初始化jobs鏈表指針</p><p><b>  }</b><

79、/p><p>  可以看到這個函數(shù)打開一個名為lee_profile的文件,它是配置文件。然后調用了另外兩個函數(shù)getline1(fd,buf)和getenviron(n,buf)。getline1(fd,buf)的作用是讀取一行的信息到buf中。getenviron(n,buf)的作用是將getline1(fd,buf)讀到buf中的信息以冒號分開,分別放于envpath[]中,為后面查找命令做準備。這樣命令查找的

80、準備工作就做好了。接下來函數(shù)返回到函數(shù)init_environ。接下里的語句將envhis.start和envhis.end置0.這兩條語句的作用是初始化保存history命令鏈表的頭尾指針。而語句head=end=NULL是初始化保存jobs命令鏈表的頭尾指針。至此,程序的初始化工作基本完成。</p><p>  接下來就進入一個while循環(huán),就和一般的shell一樣,當一個命令執(zhí)行完或放到后臺后,shell

81、就可獲取新的用戶輸入命令。</p><p><b>  解析命令</b></p><p>  在這里對讀到input數(shù)組中的命令進行分析,以獲取命令和參數(shù)。shell中的命令分成4種:普通命令,重定向命令,管道命令和內部命令。對管道和重定向命令單獨處理。</p><p>  for(i=0,j=0,k=0;i<=input_len;i++

82、){</p><p>  if(input[i]=='<'|| input[i]=='>'||input[i]=='|'){</p><p>  if(input[i]=='|'){</p><p>  pipel(input,input_len);//管道命令</p>&l

83、t;p>  add_history(input);</p><p>  free(input);</p><p><b>  }else{</b></p><p>  redirect(input,input_len); //重定向命令</p><p>  add_history(input);</p>

84、;<p>  free(input);</p><p><b>  }</b></p><p><b>  is_pr=1;</b></p><p><b>  break;</b></p><p><b>  }</b></p>

85、<p><b>  }</b></p><p>  該for循環(huán)就把帶“>”,“<”和“|”符號的管道和重定向命令單獨處理。同時把is_pr這個管道和重定向命令的標志置1。然后分別調用redirect(input,input_len)和pipel(input,input_len)兩個函數(shù)來處理這兩類命令。</p><p>  對于一般命令,當

86、is_pr的值為零時,則繼續(xù)執(zhí)行下步程序。下面是分析和處理普通命令和內部命令。</p><p>  for(i=0,j=0,k=0;i<=input_len;i++){</p><p>  if(input[i]==' '||input[i]=='\0'){</p><p>  if(j==0)//這個條件略去連在一起的多個

87、空格</p><p><b>  continue;</b></p><p><b>  else{</b></p><p>  buf[j++]='\0';</p><p>  arg[k]=(char *)malloc(sizeof(char)*j);</p>&l

88、t;p>  strcpy(arg[k++],buf);//將指令或參數(shù)復制到arg中</p><p>  j=0;//準備取下個參數(shù)</p><p><b>  }</b></p><p><b>  }else{</b></p><p>  if(input[1]=='&

89、amp;' && input[i+1]=='\0'){</p><p><b>  is_bg=1;</b></p><p>  continue;//如果字符串最后是&,將后臺命令標志置</p><p><b>  }</b></p><p>

90、  buf[j++]=input[i];</p><p><b>  }</b></p><p><b>  }</b></p><p>  該for循環(huán)的作用是對input中的輸入命令進行解釋。用戶的輸入以空格區(qū)分命令和參數(shù),如:“l(fā)s -l”。這個循環(huán)的作用就是分析這條輸入命令,把命令ls放進arg[0]中,而參數(shù)-1

91、則在arg[1]中。整個程序對input的分析都是遵循以下的原則:以空格分段,分別放在arg[j]中。</p><p>  經過這個for循環(huán),argp[0]中的命令就顯示出重要性了,可以通過判斷arg[0]來判斷用戶的輸入是普通命令還是內部命令(history、jobs、exit、cd等)。如果是內部命令,則根據(jù)不同的命令,執(zhí)行不同的動作。</p><p><b>  查找外部

92、程序</b></p><p>  既然不是管道和重定向命令,也不是內部命令,那么一定是普通命令了。對于普通命令的處理就是查找命令的可執(zhí)行文件。</p><p>  if(is_pr==0){</p><p>  //非管道、重定向命令</p><p>  //在使用exec執(zhí)行命令時,最后的參數(shù)必須是NULL指針,所以將其置空&l

93、t;/p><p>  arg[k]=(char *)malloc(sizeof(char));</p><p>  arg[k]=NULL;</p><p>  if(is_founded(arg[0])==0){</p><p>  //查找arg[0]中的命令是否存在</p><p>  printf("Th

94、is command is not founded ! \n");</p><p>  for(i=0;i<=k;i++)</p><p>  free(arg[i]);</p><p><b>  continue;</b></p><p><b>  }</b></p>

95、;<p><b>  }</b></p><p>  這里的關鍵是調用了一個查找命令文件的函數(shù)is_founded來判斷輸入的命令是否存在。</p><p><b>  函數(shù)如下:</b></p><p>  int is_founded(char *cmd)</p><p><

96、b>  {</b></p><p><b>  int k=0;</b></p><p>  while(envpath[k]!=NULL){//查找路徑已在程序初始化時設定在envpath[i]中</p><p>  strcpy(buf,envpath[k]);</p><p>  strcat

97、(buf,cmd);</p><p>  if(access(buf,F_OK)==0)//文件被找到</p><p><b>  return 1;</b></p><p><b>  k++;</b></p><p><b>  }</b></p><

98、;p><b>  return 0;</b></p><p><b>  }</b></p><p>  回顧前面程序初始化時,已經將命令可能存在的路徑置于envpath[i]數(shù)組中,函數(shù)is_founded做的工作就是在相應的路徑下查找、判斷命令是否存在。如果找到返回1,沒有則返回0。判斷時用到了系統(tǒng)調用函數(shù)access。</p&g

99、t;<p><b>  格式</b></p><p>  #include<unistd.h></p><p>  int access(const char *pathname,int mode);</p><p><b>  參數(shù)說明</b></p><p>  pat

100、hname是文件名稱。</p><p>  mode是要判斷的屬性,可以取以下值或者是它們的組合。R_OK表示文件可以讀,W_OK表示文件可以寫,X_OK表示文件可以執(zhí)行,F(xiàn)_OK表示文件存在。當測試成功時,函數(shù)返回零,否則如果有一個條件不符時,返回-1。</p><p>  如果命令找到,就在函數(shù)is_founded中把路徑和命令存放到buf數(shù)組中。下面就可以開始執(zhí)行命令的工作了。<

101、;/p><p><b>  執(zhí)行命令</b></p><p>  當命令文件在指定的路徑下查找成功時,就可以執(zhí)行命令了。通過調用函數(shù)fork創(chuàng)建一個子進程,在子進程中執(zhí)行命令。函數(shù)fork創(chuàng)建一個新的子進程,其子進程會復制父進程的數(shù)據(jù)與堆棧空間并繼承父進程的用戶ID、組ID、環(huán)境變量,以及打開的文件、工作目錄和資源限制等。</p><p>  Li

102、nux使用COW(copy on write)技術,只有當其中一個進程試圖修改欲復制的空間時才會做真正的復制動作。由于這些繼承的信息是復制而來的,并非指向相同的內存空間,因此應當注意:子進程中對變量的修改,在父進程中不能與其同步!也就是說,即使子進程中修改了一個變量的值,在父進程中,這個變量的值仍為修改前的值!</p><p>  如果函數(shù)fork創(chuàng)建成功,則父進程返回新建子進程的ID(pid)。值得注意的是:p

103、id在子進程中為0,而在父進程中pid則為子進程真實的ID,如果失敗則返回-1。</p><p><b>  這一段程序如下:</b></p><p>  if((pid=fork())==0)//子進程</p><p>  execv(buf,arg);</p><p>  else//父進程</

104、p><p>  if(is_bg==0)//非后臺執(zhí)行命令</p><p>  waitpid(pid,&status,0);</p><p>  上面幾行語句就可以執(zhí)行shell的普通命令了。這里用到了兩個系統(tǒng)調用函數(shù)execv和waitpid。</p><p>  函數(shù)waitpid格式:</p><p>

105、;  pid_t waitpid(pid_t pid , int *status ,int options);</p><p>  如果是前臺執(zhí)行的命令,則父進程必須執(zhí)行函數(shù)waitpid。等待子進程完成后才繼續(xù)執(zhí)行。而后臺執(zhí)行的命令,父進程不用執(zhí)行函數(shù)waitpid,不用等待子進程的完成就可以繼續(xù)執(zhí)行。這就是前后臺命令的差別。而函數(shù)waitpid等待哪個子進程執(zhí)行完畢呢?這由它的第一個參數(shù)pid決定。Pid就是

106、該父進程fork的子進程的進程號。只有當進程號為pid的子進程執(zhí)行完后,父進程才能繼續(xù)。</p><p>  到這里,一個簡單的shell就算完成了,不過它只能分析普通的外部程序命令。為了使shell支持其他功能,還需要做很多工作。</p><p><b>  管道</b></p><p>  Shell管道程序對應的代碼中的函數(shù)int pip

107、el。這個函數(shù)的基本步驟也和上面基本相同,包括命令解析、尋找命令文件、命令的執(zhí)行和釋放空間等。</p><p>  管道的實現(xiàn),同重定向I/O的機制基本相同,都用到了系統(tǒng)調用函數(shù)dup2來改變子進程的文件描述符。</p><p><b>  初始化文件描述符</b></p><p>  #define NO_PIPE -1</p>

108、<p>  #define FD_READ 0</p><p>  #define FD_WRITE 1</p><p>  for (i=0;i<=10;i++){//初始化文件描述符</p><p>  fd[i][FD_READ]=NO_PIPE;</p><p>  fd[i][FD_WRITE]=NO_PIPE

109、;</p><p><b>  }</b></p><p>  其中fd[i][FD_READ]和fd[i][FD_WRITE]都是int類型的文件描述符。fd[i][0]用于指向管道的讀端,fd[i][1]用于指向管道的寫端。用fd[i][FD_READ]和fd[i][FD_WRITE]是為了方便閱讀。這個循環(huán)初始化了10對文件描述符,但它們都是指向NO_PIPE(

110、-1)的。</p><p><b>  建立管道</b></p><p>  這里用到了pipe_int和pipe_out兩個文件描述符來分別代表程序的管道的讀端和寫端。管道的讀端是程序的輸入端,管道的寫端是程序的輸出端。</p><p>  pipe_in指向到管道的讀端(第一條命令除外):</p><p><b

111、>  if(i!=0)</b></p><p>  pipe_in=fd[i-1][FD_READ];</p><p><b>  else </b></p><p>  pipe_in=NO_PIPE;</p><p>  將pipe_out指向到管道的寫端(最后一條命令除外):</p>

112、<p>  if(i!=li_cmd)</p><p>  pipe_out=fd[i][FD_WRITE];</p><p><b>  else </b></p><p>  pipe_out=NO_PIPE;</p><p>  在總程序中這兩段程序時放在一個for循環(huán)中的。經過循環(huán),將 pipe_

113、in指向管道的讀端(第一條命令除外),將pipe_out指向管道的寫端(最后一條命令除外)。</p><p>  由于管道已經建立好,剩下的工作就是將標準輸出重定向到管道的寫端(pipe_out),進程的輸出就寫入管道:將標準輸入重定向到管道的讀端(pipe_in),管道的數(shù)據(jù)就可以被讀到。</p><p>  if((pid=fork())<0){</p><p

114、>  printf("Fork failed ! \n");</p><p>  return 0 ;</p><p><b>  }</b></p><p>  if(pid==0){</p><p>  if(pipe_in==NO_PIPE)</p><p>  c

115、lose(pipe_in);//關閉讀端</p><p>  if(pipe_out==NO_PIPE)</p><p>  close(pipe_out);//關閉寫端</p><p>  if(pipe_out!=NO_PIPE){//將標準輸出重定向到管道的寫端,這樣該子進程的輸出就寫入管道了</p><p>  dup

116、2(pipe_out,1);</p><p>  close(pipe_out);//關閉寫端</p><p><b>  }</b></p><p>  if(pipe_in!=NO_PIPE){//將標準輸出重定向到管道的讀端,這樣管道的數(shù)據(jù)就可以被讀到了</p><p>  dup2(pipe_in,0)

117、;</p><p>  close(pipe_in);//關閉讀端</p><p><b>  }</b></p><p>  execv(buf,argv[i]);</p><p><b>  }else{</b></p><p>  if(is_bg==0)<

118、/p><p>  waitpid(pid,NULL,0);</p><p>  close(pipe_in);//關閉讀端</p><p>  close(pipe_out);//關閉寫端</p><p><b>  }</b></p><p><b>  }</b>

溫馨提示

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

最新文檔

評論

0/150

提交評論