2016-09-04 36 views
3

我的程序應該使用fork和exec系統調用。 exec應更改子進程,以便將另一個命令作爲參數並執行該命令。例如,顯示當日消息:fork和execve分段錯誤

./myexec cat /etc/motd 

這是我當前的代碼

extern char **environ;  /* environment info */ 
main(int argc, char **argv) { 
    /* argc -- number of arguments */ 
    /* argv -- an array of strings */ 

    char *argvNew[argc + 1]; 
    int pid; 

    for(int i=0; i<argc; i++){ 
     argvNew[i] = argv[i]; 
    } 
    argvNew[argc + 1] = NULL; 
    printf("After For: %s\n",argvNew[0]); 
    printf("After For: %s\n",argvNew[1]); 
    printf("After For: %s\n",argvNew[2]); 
    printf("After For: %s\n",argvNew[3]); 


    if ((pid = fork()) < 0) { 
     fprintf(stderr, "Fork error%sstrerror\n", strerror(errno)); 

     exit(1); 
    } 
    else if (pid == 0) { 
     /* child process */ 
     if (execve(argvNew[0], argvNew, environ) < 0) { 
      fprintf(stderr, "Execve error%d %s\n",errno,strerror(errno)); 
      exit(1); 
     } 
    } 
    else { 
     /* parent */ 
    wait(0);  /* wait for the child to finish */ 
    } 

} 

運行./myexecv cat etc/motd沒有任何反應後,只是打印報表。任何建議前進?

+0

絕對不需要複製參數列表。除非有一個正式的命令來使用'execve()'而不是'execv()',那就使用它。你的整個代碼應該是:'execv(argv [1],&argv [1]);' - 沒有分支,沒有拷貝,沒有等待,沒有別的。原始代碼中fork/exec/wait機制的唯一邊際'好處'是來自父代的返回代碼始終爲0,無論執行的命令是否成功(假設您使用C99或更好的編譯器,關閉main的結尾相當於'return 0',但是'main'應該有一個明確的'int'返回類型)。 –

+0

謝謝你的回答。然而,在寫入命令後仍然沒有任何反應。我得到的輸出是@ubuntu:〜/ Documents $ ./myfork ls -l 適用於:ls 適用於:-l 適用於:(null) 適用於:(null) 執行錯誤2無此類文件或目錄 – SolRac

+0

啊...使用'execv()'或'execve()',您必須指定可執行文件的絕對名稱(或相對於當前目錄的名稱)。試試:'./myexec/bin/cat/etc/motd'(或者'./myexec/usr/bin/cat/etc/motd',如果這是'cat'的話)。這應該工作。要執行類似於shell的基於PATH的'cat'搜索,可以使用'execvp()' - 或者,如果你能找到代碼並且你真的想要['execvpe()'](http:// stackoverflow。 com/questions/7789750),但由於您不改變環境,因此使用環境設置變體確實沒有意義。 –

回答

2

顯示的代碼中有多個錯誤。

for(int i=0; i<argc; i++){ 
      argvNew[i] = argv[i]; 
    } 
    argvNew[argc+1] = NULL; 

在它的面值,零分配是錯誤的,並且將導致不確定的行爲,因爲argvNew被聲明爲

char *argvNew[argc + 1]; 

所以數組包含值argvNew[0]通過argvNew[argc],並argvNew[argc+1]=NULL;逃跑過去數組的末尾,導致未定義的行爲。這顯然應

argvNew[argc] = NULL; 

但即使這樣,也將是錯誤的,因爲:

execve(argvNew[0], argvNew, environ); 

argvNew[0]argv[0]複製,這是該項目的正在執行的名字。這將在子進程中分叉並運行相同的程序。

你最終會攻擊你自己。如果這是一個共享服務器,你會讓系統管理員非常生氣。

您需要從等式中移除argv[0],並僅拷貝argv[1],然後繼續。正確的循環,複製,就是:

int pid; 
    char *argvNew[argc]; 

    for(int i=1; i<argc; i++){ 
      argvNew[i-1] = argv[i]; 
    } 
    argvNew[argc-1] = NULL; 
+0

複製的好處是什麼?爲什麼不只是'execve(argv [1],&argv [1],environ);'或'execv(argv [1],&argv [1]);'? –

+0

沒有好處。但這只是[mcve],而OP的實際代碼可能會對'argv'進行額外的操作。 –

1

execve()主叫參數要求第一個參數是要執行的文件名。不幸的是,您通過了argvNew[0],這與argv[0]的值相同。這意味着你再次打電話給你自己的程序,而不是腳本。您需要將參數移動一個:

... 
for(int i=1; i<argc; i++){ 
    argvNew[i-1] = argv[i]; 
} 
argvNew[argc-1] = NULL; 
... 
+0

複製的好處是什麼?爲什麼不只是'execve(argv [1],&argv [1],environ);'或'execv(argv [1],&argv [1]);'? –

+0

@JonathanLeffler優秀的建議!我保留了該副本,因爲我認爲OP已簡化了該案。我想現實中他會添加/更改/刪除一些參數;否則,這個程序會增加什麼價值? – Christophe