www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 單片機(jī) > CPP開(kāi)發(fā)者


在第一部分中我們討論了 fork 系統(tǒng)調(diào)用以及它的注意事項(xiàng)。在本文中,我們將研究怎樣執(zhí)行命令。


這里將介紹 exec 函數(shù)家族。即以下函數(shù):


  • execl

  • execv

  • execle

  • execve

  • execlp

  • execvp


為了滿足需要,我們將使用 execvp,它的簽名看起來(lái)像這樣:


int execvp(const char *file, char *const argv[]);


函數(shù)名中的 vp 表明:它接受一個(gè)文件名,將在系統(tǒng) $PATH 變量中搜索此文件名,它還接受將要執(zhí)行的一組參數(shù)。


你可以閱讀 exec 的 man 頁(yè)面 以得到其它函數(shù)的更多信息。


讓我們看一下以下代碼,它執(zhí)行命令 ls -l -h -a:


execvp.c


#include

int main() {

char *argv[] = {"ls", "-l", "-h", "-a", NULL};

execvp(argv[0], argv);

return 0;

}


關(guān)于 execvp 函數(shù),有幾點(diǎn)需要注意:


  1. 第一個(gè)參數(shù)是命令名。

  2. 第二個(gè)參數(shù)由命令名和傳遞給命令自身的參數(shù)組成。并且它必須以 NULL 結(jié)束。

  3. 它將當(dāng)前進(jìn)程的映像交換為被執(zhí)行的命令的映像,后面再展開(kāi)說(shuō)明。


如果你編譯并執(zhí)行上面的代碼,你會(huì)看到類(lèi)似于下面的輸出:


total 32

drwxr-xr-x 5 dhanush staff 170B Jun 11 11:32 .

drwxr-xr-x 4 dhanush staff 136B Jun 11 11:30 ..

-rwxr-xr-x 1 dhanush staff 8.7K Jun 11 11:32 a.out

drwxr-xr-x 3 dhanush staff 102B Jun 11 11:32 a.out.dSYM

-rw-r--r-- 1 dhanush staff 130B Jun 11 11:32


它和你在你的主 shell 中手動(dòng)執(zhí)行l(wèi)s -l -h -a的結(jié)果完全相同。


既然我們能執(zhí)行命令了,我們需要使用在第一部分中學(xué)到的fork 系統(tǒng)調(diào)用構(gòu)建有用的東西。事實(shí)上我們要做到以下這些:


  1. 當(dāng)用戶輸入時(shí)接受命令。

  2. 調(diào)用 fork 以創(chuàng)建一個(gè)子進(jìn)程。

  3. 在子進(jìn)程中執(zhí)行命令,同時(shí)父進(jìn)程等待命令完成。

  4. 回到第一步。


我們看看下面的函數(shù),它接收一個(gè)字符串作為輸入。我們使用庫(kù)函數(shù) strtok 以空格分割該字符串,然后返回一個(gè)字符串?dāng)?shù)組,數(shù)組也用 NULL來(lái)終結(jié)。


include <stdlib.h>

#include

char **get_input(char *input) {

char **command = malloc(8 * sizeof(char *));

char *separator = " ";

char *parsed;

int index = 0;

parsed = strtok(input, separator);

while (parsed != NULL) {

command[index] = parsed;

index++;

parsed = strtok(NULL, separator);

}

command[index] = NULL;

return command;

}


如果該函數(shù)的輸入是字符串 "ls -l -h -a",那么函數(shù)將會(huì)創(chuàng)建這樣形式的一個(gè)數(shù)組:["ls", "-l", "-h", "-a", NULL],并且返回指向此隊(duì)列的指針。


現(xiàn)在,我們?cè)谥骱瘮?shù)中調(diào)用 readline 來(lái)讀取用戶的輸入,并將它傳給我們剛剛在上面定義的 get_input。一旦輸入被解析,我們?cè)谧舆M(jìn)程中調(diào)用 fork 和 execvp。在研究代碼以前,看一下下面的圖片,先理解 execvp 的含義:



當(dāng) fork 命令完成后,子進(jìn)程是父進(jìn)程的一份精確的拷貝。然而,當(dāng)我們調(diào)用 execvp 時(shí),它將當(dāng)前程序替換為在參數(shù)中傳遞給它的程序。這意味著,雖然進(jìn)程的當(dāng)前文本、數(shù)據(jù)、堆棧段被替換了,進(jìn)程 id 仍保持不變,但程序完全被覆蓋了。如果調(diào)用成功了,那么 execvp 將不會(huì)返回,并且子進(jìn)程中在這之后的任何代碼都不會(huì)被執(zhí)行。這里是主函數(shù):


#include

#include

#include

#include

#include

#include

int main() {

char **command;

char *input;

pid_t child_pid;

int stat_loc;

while (1) {

input = readline("unixsh> ");

command = get_input(input);

child_pid = fork();

if (child_pid == 0) {

/* Never returns if the call is successful */

execvp(command[0], command);

printf("This won't be printed if execvp is successuln");

} else {

waitpid(child_pid, &stat_loc, WUNTRACED);

}

free(input);

free(command);

}

return 0;

}


全部代碼可在此處的單個(gè)文件中獲取。如果你用 gcc -g -lreadline shell.c 編譯它,并執(zhí)行二進(jìn)制文件,你會(huì)得到一個(gè)最小的可工作 shell,你可以用它來(lái)運(yùn)行系統(tǒng)命令,比如 pwd 和 ls -lha:


unixsh> pwd

/Users/dhanush/github.com/indradhanush.github.io/code/shell-part-2

unixsh> ls -lha

total 28K

drwxr-xr-x 6 root root 204 Jun 11 18:27 .

drwxr-xr-x 3 root root 4.0K Jun 11 16:50 ..

-rwxr-xr-x 1 root root 16K Jun 11 18:27 a.out

drwxr-xr-x 3 root root 102 Jun 11 15:32 a.out.dSYM

-rw-r--r-- 1 root root 130 Jun 11 15:38 execvp.c

-rw-r--r-- 1 root root 997 Jun 11 18:25 shell.c

unixsh>


注意:fork 只有在用戶輸入命令后才被調(diào)用,這意味著接受用戶輸入的用戶提示符是父進(jìn)程。


錯(cuò)誤處理


到目前為止,我們一直假設(shè)我們的命令總會(huì)完美的運(yùn)行,還沒(méi)有處理錯(cuò)誤。所以我們要對(duì) shell.c做一點(diǎn)改動(dòng):


  • fork – 如果操作系統(tǒng)內(nèi)存耗盡或是進(jìn)程數(shù)量已經(jīng)到了允許的最大值,子進(jìn)程就無(wú)法創(chuàng)建,會(huì)返回 -1。我們?cè)诖a里加上以下內(nèi)容:


...

while (1) {

input = readline("unixsh> ");

command = get_input(input);

child_pid = fork();

if (child_pid < 0) {

perror("Fork failed");

exit(1);

}

...


  • execvp – 就像上面解釋過(guò)的,被成功調(diào)用后它不會(huì)返回。然而,如果執(zhí)行失敗它會(huì)返回 -1。同樣地,我們修改 execvp 調(diào)用:


...

if (execvp(command[0], command) < 0) {

perror(command[0]);

exit(1);

}

...


注意:雖然fork之后的exit調(diào)用終止整個(gè)程序,但execvp之后的exit 調(diào)用只會(huì)終止子進(jìn)程,因?yàn)檫@段代碼只屬于子進(jìn)程。


  • malloc – 如果操作系統(tǒng)內(nèi)存耗盡,它就會(huì)失敗。在這種情況下,我們應(yīng)該退出程序:


char **get_input(char *input) {

char **command = malloc(8 * sizeof(char *));

if (command == NULL) {

perror("malloc failed");

exit(1);

}

...


  • 動(dòng)態(tài)內(nèi)存分配 – 目前我們的命令緩沖區(qū)只分配了8個(gè)塊。如果我們輸入的命令超過(guò)8個(gè)單詞,命令就無(wú)法像預(yù)期的那樣工作。這么做是為了讓例子便于理解,如何解決這個(gè)問(wèn)題留給讀者作為一個(gè)練習(xí)。


上面帶有錯(cuò)誤處理的代碼可在這里(https://indradhanush.github.io/code/shell-part-2/shell_with_error_handling.c)獲取。


內(nèi)建命令


如果你試著執(zhí)行 cd 命令,你會(huì)得到這樣的錯(cuò)誤:


cd: No such file or directory


我們的 shell 現(xiàn)在還不能識(shí)別cd命令。這背后的原因是:cd不是ls或pwd這樣的系統(tǒng)程序。讓我們后退一步,暫時(shí)假設(shè)cd 也是一個(gè)系統(tǒng)程序。你認(rèn)為執(zhí)行流程會(huì)是什么樣?在繼續(xù)閱讀之前,你可能想要思考一下。


流程是這樣的:


  1. 用戶輸入 cd /。

  2. shell對(duì)當(dāng)前進(jìn)程作 fork,并在子進(jìn)程中執(zhí)行命令。

  3. 在成功調(diào)用后,子進(jìn)程退出,控制權(quán)還給父進(jìn)程。

  4. 父進(jìn)程的當(dāng)前工作目錄沒(méi)有改變,因?yàn)槊钍窃谧舆M(jìn)程中執(zhí)行的。因此,cd 命令雖然成功了,但并沒(méi)有產(chǎn)生我們想要的結(jié)果。


因此,要支持 cd,我們必須自己實(shí)現(xiàn)它。我們也需要確保,如果用戶輸入的命令是 cd(或?qū)儆陬A(yù)定義的內(nèi)建命令),我們根本不要 fork 進(jìn)程。相反地,我們將執(zhí)行我們對(duì) cd(或任何其它內(nèi)建命令)的實(shí)現(xiàn),并繼續(xù)等待用戶的下一次輸入。,幸運(yùn)的是我們可以利用 chdir 函數(shù)調(diào)用,它用起來(lái)很簡(jiǎn)單。它接受路徑作為參數(shù),如果成功則返回0,失敗則返回 -1。我們定義函數(shù):


int cd(char *path) {

return chdir(path);

}


并且在我們的主函數(shù)中為它加入一個(gè)檢查:


while (1) {

input = readline("unixsh> ");

command = get_input(input);

if (strcmp(command[0], "cd") == 0) {

if (cd(command[1]) < 0) {

perror(command[1]);

}

/* Skip the fork */

continue;

}

...


帶有以上更改的代碼可從這里(https://indradhanush.github.io/code/shell-part-2/shell_with_builtin.c)獲取,如果你編譯并執(zhí)行它,你將能運(yùn)行 cd 命令。這里是一個(gè)示例輸出:


unixsh> pwd

/Users/dhanush/github.com/indradhanush.github.io/code/shell-part-2

unixsh> cd /

unixsh> pwd

/

unixsh>


第二部分到此結(jié)束。

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉