执行其他程序
我们派生一个子进程来完成某项工作的时候,经常需要让另外一个程序来完成。函数exec()可以用来执行一个可执行文件来代替当前进程的执行映像。需要注意的是,该调用并没有生成新的进程,而是在原有进程的基础上,替换原有进程的正文,调用前后是同一个进程,进程号PID不变,但执行的程序变了。在Linux中,有6种调用的形式,他们的函数原型如下:
exec()的六个函数实现的功能是一样的,只是在传递参数和设置环境变量方面提供了不同的方式。6个函数都是以exec四个字母开头的,后面的字母表示了其用法上的区别:
表明后面的参数列表是要传递给程序的参数列表,参数列表的第一个参数
事实上,只有execve是真正的系统调用,其它五个函数最终都调用execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。
用execl()执行一个程序,源代码execl_test.c
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char** argv)
{
char* arg[3];
arg[0] = "/bin/ls";
arg[1] = "/";
arg[2] = NULL;
printf("this is execl test\n");
execl(arg[0], arg[1], arg[2]);
printf("running after the execvp() call");
return 0;
}
从输出来看,execl()执行了新的程序,代替了当前进程的程序,因此除非execl()调用失败,否则后面的代码永远不会被调用。
用execvp执行一个程序,源代码:execvp_test.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
char* arg[3];
arg[0] = "ls";
arg[1] = "/";
arg[2] = NULL;
printf("this is execvp test\n");
execvp("ls", arg);
return 0;
}
第一个参数由 /bin/ls 变成了 ls ,第二个参数是一个字符串数组。运行结果与execl_test的一模一样。
exec函数通常和fork()一起配合使用,由fork派生一个子进程,在子进程执行新的程序。例如:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char** argv)
{
int ret;
pid_t pid;
pid_t pid_c;
pid = fork();
if(pid == 0)
{
printf("child PID=[%d]\n", getpid());
execlp("ls", "ls", "-ls", NULL);
}
else if(pid != -1)
{
pid_c = wait(&ret)
printf("child PID=[%d] wait return = [%d]\n", pid_c, ret);
}
return 0;
}
实现一个自己的shell
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
pid_t pid;
char buf[1024];
char * arg[10];
int i, ret;
AGAIN:
/*读取命令行*/
printf("myshell:%s$", getcwd(NULL, 1024));
fflush(stdout);
ret = read(STDIN_FILENO, buf, sizeof(buf) - 1);
if(ret == -1)
{
perror("read");
exit(1);
}
buf[ret - 1] = '\0';
/*处理exit命令*/
if(strcmp(buf, "exit") == 0)
{
exit(0);
}
/*分割参数*/
i = 0;
arg[i] = strtok(buf, " ");
while(arg[i] != NULL)
{
if(++i >= sizeof(arg))
break;
arg[i] = strtok(NULL, " ");
}
/*处理cd命令*/
if(i == 2 && strcmp(arg[0], "cd") == 0)
{
chdir(arg[1]);
goto AGAIN;
}
/*处理其他命令*/
pid = fork();
if(pid == 0)
{
execvp(arg[0], arg);
return 0;
}
else if(pid != -1)
{
wait(NULL);
goto AGAIN;
}
return 0;
}