




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、一、 實驗內容 Linux系統中,shell是用戶和系統內核溝通的中介,它為用戶使用操作系統的服務提供了一個命令界面。用戶在命令提示符($或是)下輸入的每一個命令都由shell解釋,然后傳給內核執行。 本實驗是實現一個簡單的shell,能在虛擬shell界面下響應一些簡單的shell命令,不考慮輸入輸出重定向以及管道。二、 簡單shell命令解釋器的分析開始初始化虛擬shell界面獲取用戶指令解析指令尋找命令文件執行命令結束 命令解釋執行的過程為:1.初始化虛擬shell界面;2.獲取用戶指令(命令和參數);3.解析指令;4.尋找命令文件;5.執行命令。三、 shell命令解析器的設計 (1)
2、在虛擬的shell界面上出現命令提示符($或); (2)獲取用戶指令:獲取用戶在命令提示符后面輸入的命令及其參數,并注意命令輸入的最大長度; (3)解析指令:對用戶輸入的命令進行解析,解析出命令名和參數; (4)尋找命令文件:每個命令的執行都必須依靠對應的可執行文件,這些文件的存放路徑存放在用戶的PATH環境變量里; (5)執行命令:可通過fork( )系統調用創建一個進程來完成執行命令的任務,具體的命令執行用execv( )函數。 注:為了簡化程序設計的難度,對涉及輸入輸出重定向以及管道的命令不予考慮。四、 shell命令解析器實現的詳細步驟 1.在虛擬的shell界面上打印出命令提示符“#
3、”或者“$”。 通常在Linux命名提示符前會有路徑或者機器名,我們可以先獲取到當前執行的路徑,由此調用一個外部函數get_current_dir_path(),返回值是一個指向當前目錄絕對路徑的字符串指針,然后連接到命令提示符之前即可。這樣就得到了虛擬shell的命令提示行。因為用戶不是只執行一個指令就結束了,還需要等待用戶繼續輸入,所以用一個while死循環來循環執行該解析器。代碼如下: char *path;while (1)path = get_current_dir_name();printf("%s>fangxu$",path); 2.獲取用戶輸入的指令及
4、其參數 通過getchar()或者gets()方法來獲取用戶輸入的指令,設置一個緩沖區buffer來存儲用戶的輸入,并定義緩沖區大小,如果用戶輸入的指令太長超出緩沖區大小,則提示用戶輸入指令太長并重新輸入。定義一個臨時變量inputLen來記錄用戶輸入字符串的長度,如果超出緩沖區大小BUFFERSIZE,則發出提示并重復執行該程序允許用戶再次重新輸入指令;若沒有超出,則把緩沖區的內容復制到input中,以此來存儲用戶輸入的指令。代碼如下(接上段代碼printf之后):/開始獲取輸入inputLen = 0;in_char = getchar();while (in_char !='n&
5、#39;)if(inputLen < BUFFERSIZE)bufferinputLen+ = in_char;in_char = getchar();/*命令超長處理*/if (inputLen >= BUFFERSIZE)printf("Your command is too long! Please re-enter your command!n");inputLen = 0;continue;elsebufferinputLen = '0'/*加上串結束符號,形成字串*/*將命令從緩存拷貝到input中*/input = (char *)
6、 malloc(sizeof(char) * (inputLen+1);strcpy(input,buffer);3. 解析指令:對用戶輸入的命令進行解析,解析出命令和參數 在完成了指令的輸入后自然是要解析該指令,以獲取命令和參數了。在這個過程中我們需要區分命令和參數,通過for循環,找到用戶輸入字符串中的空格或者t,以此來區分開命令名和參數,并保存在指針數組char *arg中,其中arg0保存的為命令名,其后參數從arg1開始依次保存。最后,獲取到命令名和參數后再釋放input空間。 /* 獲取命令和參數并保存在arg中*/for (i = 0,j = 0,k = 0;i <= in
7、putLen;i+)/把輸入的命令分開為各條命令和參數if (inputi = ' ' | inputi ='t' | inputi = '0')if (j = 0) /*這個條件可以略去連在一起的多個空格或者tab*/continue;elsebufferj+ = '0'argk = (char *) malloc(sizeof(char)*j);/*將指令或參數從緩存拷貝到arg中*/strcpy(argk,buffer);j = 0;/*準備取下一個參數*/k+; else/*如果字串最后是&',則置后臺運行
8、標記為1*/if (inputi = '&' && inputi+1 = '0')isBk = 1;continue;bufferj+ = inputi;free(input);/*釋放空間*/4. 尋找命令文件 接下來我們就該完成尋找命令文件的工作了。那么,到哪里去找用戶輸入的命令呢?我們知道shell為每個用戶提供了一組環境變量。這些變量定義在用戶的.login文件中。其中路徑變量是一組絕對路徑的列表,表明shell將如何搜索命令文件。也就是說,只要我們獲取了路徑變量PATH,然后依次搜索各路徑,就可以確定用戶輸入的命令文件的位置了。
9、此外,我們還需要在用戶輸入退出指令的時候能退出我們的shell回到Linux的shell下。在這里,我們設定了exit指令來完成這一功能。其中的函數is fileexist就是用來判斷輸入的指令是否存在。存在返回0,并將指令的路徑保存在buffer中,否則返回一1。程序如下:/如果輸入的指令是exit則退出while,即退出程序if (strcmp(arg0,"exit") = 0 )printf("Bye byen");break;if (isRd = 0) /非管道、重定向指令/在使用exec執行命令的時候,最后的參數必須是NULL指針,/所以將最后
10、一個參數置成空值argk = (char *) 0;/判斷指令arg0是否存在if (is_fileexist(arg0) = -1 )printf("This command is not found!n");for(i=0;i<k;i+)free(argi);continue;/*釋放申請的空間*/for (i=0;i<k;i+)free(argi);int is_fileexist(char *comm)char *path,*p;int i;i = 0;/*使用getenv函數來獲取系統環境變量,用參數PATH表示獲取路徑*/path = getenv(
11、"PATH");p = path;while (*p != '0')/*路徑列表使用:來分隔路徑*/if (*p != ':')bufferi+ = *p;elsebufferi+ = '/'bufferi = '0'/*將指令和路徑合成,形成pathname,并使用access函數來判斷該文件是否存在*/strcat(buffer,comm);if (access(buffer,F_OK) = 0)/*文件被找到*/return 0;else/*繼續尋找其它路徑*/i = 0;p+; /*搜索完所有路徑,依然
12、沒有找到則返回-1*/return -1;5. 執行命令 當我們得到了命令的路徑,就可以執行命令了。通過調用fork()創建一個子進程,在子進程中執行命令。fork()會創建一個新的子進程,其子進程會復制父進程的數據與堆棧空間并繼承父進程的用戶代碼、組代碼、環境變量、已打開的文件代碼、工作目錄和資源限制等。Linux使用COW (copy onwrite)技術,只有當其中一個進程試圖修改欲復制的空間時才會做真正的復制動作。由于這些繼承的信息是復制而來,并非指相同的內存空間,因此子進程對這些變量的修改和父進程不會同步。此外,子進程不會繼承父進程的文件鎖定和未處理的信號。需要注意的是,Linux不
13、保證子進程會比父進程先執行或晚執行,因此編寫程序時需注意死鎖和競爭的發生。如果fork成功,則父進程會返回新建子進程的ID (PID,而在新建子進程中返回0,如果失敗則返回-1。執行指令是通過調用exec函數。exec有一系列的變型函數:execl(), execle(), execlp(), execv(), execve()和execvp()。這里用到execv()函數,execv有兩個參數,分別是指向命令路徑的字符串指針和存放命令參數的數組指針。execv如果成功則不會返回,失敗則返回-1。程序如下:if (pid = fork() =0) /*子進程*/execv(buffer,arg
14、);else/*父進程*/if (isBk = 0) /*并非后臺執行指令*/waitpid(pid,&status,0);/*釋放申請的空間*/for (i=0;i<k;i+)free(argi); 后臺執行指令和普通指令之間的區別就在于父進程是否執行waitpid( )。也就是說如果是后臺執行指令,那么父進程不等到子進程結束就繼續執行下去了,否則父進程一直要等到子進程結束才繼續執行。一個執行命令的正常過程是,父進程創建子進程,并在執行命令的時候等待,一直等到子進程終結。如果在命令行的末尾添加“&”,那么shell將會創建一個子進程,并且啟動它執行指定的命令,但是父進程
15、將不會等到子進程結束。也就是說,父進程和子進程將會同時執行。當子進程執行命令的時候,父進程將會在標準輸出設備(stdout)上打出另一個提示符并等待用戶輸入其他命令。 至此,在不考慮管道以及輸入輸出重定向的情況下,shell命令解釋器的功能已經完成。五、 運行界面及結果分析1.編譯并執行shell.c文件,執行結果如下: 2.執行ls -l列舉當前文件夾中的文件:3.用mkdir命令創建文件夾files: 4.用rmdir命令刪除文件夾abc: 5.用date命令顯示系統日期時間(沒有調整虛擬機Linux系統的時間,顯示不太正確哈): 6.用ps e&命令顯示前臺和后天所有進程(命令后
16、加&使之運行為后臺進程): 7.輸入非Linux系統命令(提示找不到該命令): 8.輸入exit自定義命令退出程序進入Linux系統的shell:六、 總結 本次實驗完成了簡單的對shell命令的解析器。由于對于linux環境下的C語言編程的不熟悉,導致完成過程頗為曲折。但同時,也使自己對于Linux操作系統有了更為深入的了解。自己之前用的編程語言基本上都是完全面向對象的Java語言,偶爾使用C#語言,很少很少用C來寫程序了。雖然照著書上敲了C代碼,但為了讓其正確運行,依舊費了不少力氣。上網查資料,翻書等等,最終還是修改成功并使其運行了。在對C更為熟悉的同時,也使自己對于Liunx的一
17、些基本命令更加了解。學習Linux,不能只通過看書本上的知識,還要多上機編寫程序,才會有深刻的體會,同時自己對應Linux各種機制才會有更好的理解,才能更好的運用編程,同時這樣也能使自己的編程能力得到較大的提高。 學習不能僅僅局限于書本以及老師的講解,還有更多的在于自己的動手查找以及實踐的過程中所學到的知識。附源碼:#include <linux/unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <stdlib.h>#include <sys/stat.h>#in
18、clude <fcntl.h>#include <stdio.h>#include <string.h>#define BUFFERSIZE 50extern char *get_current_dir_name(void);extern char *getenv(const char *name);extern pid_t waitpid(pid_t pid, int *status, int options);char bufferBUFFERSIZE+1;main()char *path, *arg10, *input;int inputLen, is
19、Rd, isBk, i, j, k, pid, status;char in_char;while (1)isRd = 0; /是否重定向標志isBk = 0; /是否后臺運行標志path = get_current_dir_name();printf("%s>fangxu>$",path);/開始獲取輸入inputLen = 0;in_char = getchar();while (in_char !='n')if(inputLen < BUFFERSIZE)bufferinputLen+ = in_char;in_char = getc
20、har();/*命令超長處理*/if (inputLen >= BUFFERSIZE)printf("Your command is too long! Please re-enter your command!n");inputLen = 0;continue;elsebufferinputLen = '0'/*加上串結束符號,形成字串*/*將命令從緩存拷貝到input中*/input = (char *) malloc(sizeof(char) * (inputLen+1);strcpy(input,buffer);/* 獲取命令和參數并保存在ar
21、g中*/for (i = 0,j = 0,k = 0;i <= inputLen;i+)/把輸入的命令分開為各條命令和參數if (inputi = ' ' | inputi ='t' | inputi = '0')if (j = 0) /*這個條件可以略去連在一起的多個空格或者tab*/continue;elsebufferj+ = '0'argk = (char *) malloc(sizeof(char)*j);/*將指令或參數從緩存拷貝到arg中*/strcpy(argk,buffer);j = 0;/*準備取下一個參
22、數*/k+; else/*如果字串最后是&',則置后臺運行標記為1*/if (inputi = '&' && inputi+1 = '0')isBk = 1;continue;bufferj+ = inputi;free(input);/*釋放空間*/如果輸入的指令是exit則退出while,即退出程序if (strcmp(arg0,"exit") = 0 )printf("Bye byen");break;if (isRd = 0) /非管道、重定向指令/在使用exec執行命令的時候,最后的參數必須是NULL指針,/所
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- fms考試題及答案
- 智能算法檢測中的自適應技術探討考核試卷
- 矩陣宣傳面試題及答案
- 汽車S店空調設備安全規范考核試卷
- javaweb面試題及答案
- 百威亞太面試題及答案
- 足球思維測試題及答案
- 《推銷實務》課件 項目6 處理顧客異議-維系推銷顧客關系
- 《數據流通區塊鏈智能合約API技術規范》征求意見稿
- 改善政務服務助力統一大市場
- 廣東省2025年中考英語模擬試卷試題及答案詳解
- 人工智能在股票預測中的應用-全面剖析
- 2025年病例書寫規范
- 課題申報書:基于OBE理念指導下的課程內容設計及其考核體系研究
- 代扣代繳費用合同范例
- 溫州市鹿城區2025年六年級下學期小升初招生數學試卷含解析
- 特種設備事故應急處置
- 《剪映+即夢Dreamina:AI文案、圖片與視頻生成技巧大全》 課件全套 第1-14章 通過剪映生成AI文案-AI商業設計與視頻實戰
- 手提式國產汽油發電機安全操作規程
- 安徽省合肥市廬陽區南門小學-2024-2025年第一學期辦公室工作總結(層峰辟新天)【課件】
- 國家社科基金申報經驗分享-課件
評論
0/150
提交評論