1. gzyueqian
      13352868059

      學(xué)習(xí)c++開發(fā)課程需要先開始學(xué)習(xí)哪些C語言?

      更新時間: 2018-10-04 12:00:00來源: c++開發(fā)瀏覽量:3139

          在部分中我們討論了 fork 系統(tǒng)調(diào)用以及它的注意事項。在本文中,我們將研究怎樣執(zhí)行命令。
          這里將介紹 exec 函數(shù)家族。即以下函數(shù):
          execl
          execv
          execle
          execve
          execlp
          execvp

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


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


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

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

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


      execvp.c
      #include <unistd.h>
      int main() {
          char *argv[] = {"ls", "-l", "-h", "-a", NULL};
          execvp(argv[0], argv);
          return 0;
      }

      #include <unistd.h>
       
      int main() {
          char *argv[] = {"ls", "-l", "-h", "-a", NULL};
          execvp(argv[0], argv);
       
          return 0;
      }

          關(guān)于 execvp 函數(shù),有幾點需要注意:
          個參數(shù)是命令名。
           它將當(dāng)前進(jìn)程的映像交換為被執(zhí)行的命令的映像,后面再展開說明。
          如果你編譯并執(zhí)行上面的代碼,你會看到類似于下面的輸出:

      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



      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 中手動執(zhí)行l(wèi)s -l -h -a的結(jié)果完全相同。

          既然我們能執(zhí)行命令了,我們需要使用在部分中學(xué)到的fork 系統(tǒng)調(diào)用構(gòu)建有用的東西。事實上我們要做到以下這些:
          當(dāng)用戶輸入時接受命令。
          調(diào)用 fork 以創(chuàng)建一個子進(jìn)程。
          在子進(jìn)程中執(zhí)行命令,同時父進(jìn)程等待命令完成。
          回到步。

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


      include <stdlib.h>
      #include <string.h>
      
      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;
      }

      include <stdlib.h>
      #include <string.h>
       
      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ù)將會創(chuàng)建這樣形式的一個數(shù)組:["ls", "-l", "-h", "-a", NULL],并且返回指向此隊列的指針。
          現(xiàn)在,我們在主函數(shù)中調(diào)用 readline 來讀取用戶的輸入,并將它傳給我們剛剛在上面定義的 get_input。一旦輸入被解析,我們在子進(jìn)程中調(diào)用 fork 和 execvp。在研究代碼以前,看一下下面的圖片,先理解 execvp 的含義:


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

      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <readline/readline.h>
      #include <unistd.h>
      #include <sys/wait.h>
      
      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;
      }

      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <readline/readline.h>
      #include <unistd.h>
      #include <sys/wait.h>
       
      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;
      }


          全部代碼可在此處的單個文件中獲取。如果你用 gcc -g -lreadline shell.c 編譯它,并執(zhí)行二進(jìn)制文件,你會得到一個小的可工作 shell,你可以用它來運行系統(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>

      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)程。
          錯誤處理
          到目前為止,我們一直假設(shè)我們的命令總會完美的運行,還沒有處理錯誤。所以我們要對 shell.c做一點改動:
          fork – 如果操作系統(tǒng)內(nèi)存耗盡或是進(jìn)程數(shù)量已經(jīng)到了允許的值,子進(jìn)程就無法創(chuàng)建,會返回 -1。我們在代碼里加上以下內(nèi)容:

      ...
          while (1) {
              input = readline("unixsh> ");
              command = get_input(input);
      
      
              child_pid = fork();
              if (child_pid < 0) {
                  perror("Fork failed");
                  exit(1);
              }
          ...

      ...
          while (1) {
              input = readline("unixsh> ");
              command = get_input(input);
       
              child_pid = fork();
              if (child_pid < 0) {
                  perror("Fork failed");
                  exit(1);
              }
          ...

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

          ...
              if (execvp(command[0], command) < 0) {
                  perror(command[0]);
                  exit(1);
              }
      ...

          ...
              if (execvp(command[0], command) < 0) {
                  perror(command[0]);
                  exit(1);
              }
      ...

          注意:雖然fork之后的exit調(diào)用終止整個程序,但execvp之后的exit 調(diào)用只會終止子進(jìn)程,因為這段代碼只屬于子進(jìn)程。
          malloc – 如果操作系統(tǒng)內(nèi)存耗盡,它就會失敗。在這種情況下,我們應(yīng)該退出程序:

      char **get_input(char *input) {
      char **command = malloc(8 * sizeof(char *));
      if (command == NULL) {
      perror("malloc failed");
      exit(1);
      }
      ...

      char **get_input(char *input) {
      char **command = malloc(8 * sizeof(char *));
      if (command == NULL) {
      perror("malloc failed");
      exit(1);
      }
      ...

          動態(tài)內(nèi)存分配 – 目前我們的命令緩沖區(qū)只分配了8個塊。如果我們輸入的命令超過8個單詞,命令就無法像預(yù)期的那樣工作。這么做是為了讓例子便于理解,如何解決這個問題留給讀者作為一個練習(xí)。
      上面帶有錯誤處理的代碼可在這里獲取。
          內(nèi)建命令
          如果你試著執(zhí)行 cd 命令,你會得到這樣的錯誤:

      cd: No such file or directory
      
      cd: No such file or directory

          我們的 shell 現(xiàn)在還不能識別cd命令。這背后的原因是:cd不是ls或pwd這樣的系統(tǒng)程序。讓我們后退一步,暫時假設(shè)cd 也是一個系統(tǒng)程序。你認(rèn)為執(zhí)行流程會是什么樣?在繼續(xù)閱讀之前,你可能想要思考一下。
          流程是這樣的:
          用戶輸入 cd /。
          shell對當(dāng)前進(jìn)程作 fork,并在子進(jìn)程中執(zhí)行命令。
          在成功調(diào)用后,子進(jìn)程退出,控制權(quán)還給父進(jìn)程。
          父進(jìn)程的當(dāng)前工作目錄沒有改變,因為命令是在子進(jìn)程中執(zhí)行的。因此,cd 命令雖然成功了,但并沒有產(chǎn)生我們想要的結(jié)果。
          因此,要支持 cd,我們必須自己實現(xiàn)它。我們也需要確保,如果用戶輸入的命令是 cd(或?qū)儆陬A(yù)定義的內(nèi)建命令),我們根本不要 fork 進(jìn)程。相反地,我們將執(zhí)行我們對 cd(或任何其它內(nèi)建命令)的實現(xiàn),并繼續(xù)等待用戶的下一次輸入。,幸運的是我們可以利用 chdir 函數(shù)調(diào)用,它用起來很簡單。它接受路徑作為參數(shù),如果成功則返回0,失敗則返回 -1。我們定義函數(shù):

      int cd(char *path) {
              return chdir(path);
          }

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

        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;
              }
          ...

          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;
              }
          ...

          帶有以上更改的代碼可從這里獲取,如果你編譯并執(zhí)行它,你將能運行 cd 命令。這里是一個示例輸出:

      Shell
      
      unixsh> pwd
      /Users/dhanush/github.com/indradhanush.github.io/code/shell-part-2
      unixsh> cd /
      unixsh> pwd
      /
      unixsh>

      unixsh> pwd
      /Users/dhanush/github.com/indradhanush.github.io/code/shell-part-2
      unixsh> cd /
      unixsh> pwd
      /
      unixsh>

          第二部分到此結(jié)束。在下一篇文章中,我們將探討信號的主題以及實現(xiàn)對用戶中斷(Ctrl-C)的處理。敬請期待。

          想要了解更多的C語言應(yīng)用技術(shù)那就加入我們吧!

      免費預(yù)約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 亚洲国产欧美日韩精品一区二区三区 | 在线不卡长片AV | 中文字幕一本到在线网站 | 偷窥国产在线91 | 亚洲国产GⅤ精品一区二区 日本中文字幕久久 | 在线人成网站免费 |