2016-09-04 70 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