在 C 中使用 Execve
本篇文章将讨论使用 execve
来运行 Linux 标准命令和我们在 C 中的可执行文件。
首先,我们将讨论 exec
系统调用和 exec 的家族函数。 接下来,我们将在 C 中调用我们的可执行文件,最后,我们将讨论标准 Linux 命令和我们的可执行文件。
C中的exec系统调用
exec
系统调用用其他一些可执行进程替换正在运行的进程。 正在运行的进程的地址空间被替换为新进程的地址空间。
重要的是要注意新程序被加载到相同的地址空间。 进程 ID 保持不变。
新程序将独立运行; 也就是说,起点将是新程序的入口点。 exec
系统调用有许多变体。
- execl
- execle
- execlp
- execv
- execve
- execvp
这些函数使用相同的 base exec 后跟一个或多个字母。 额外字母的详细信息如下。
- e - 这里,e 是环境变量; 这个函数有一个指向环境变量的指针数组。 环境变量列表被显式传递给新加载的程序。
- l - 在这里,l 用于命令行参数。 我们可以将命令行参数列表提供给函数。
- p - 这里,p 是环境变量路径。 在此函数中,路径变量有助于查找文件,并作为参数传递给新加载的进程。
- v - 这里,v 也用于命令行参数。 但是,在此函数中,命令行参数作为指针数组传递给新加载的进程。
在基本的exec系统调用中,当前进程的地址空间被新加载程序的地址空间所取代; 结果,当前运行的进程被终止。 新加载的进程在该系统调用中作为参数传递。
新加载的进程具有相同的进程 ID、相同的环境变量和相同的文件描述符集。 但是,CPU 统计信息和虚拟内存会受到影响。
exec 调用的语法
在这里,我们有六个系统调用的语法,基本 exec
系统调用的变体。
int execl(const char* path, const char* arg, ...)
int execle(const char* path, const char* arg, ..., char* const envp[])
int execlp(const char* file, const char* arg, ...)
int execv(const char* path, const char* argv[])
int execve(const char* path, const char* argv[], char* const envp[])
int execvp(const char* file, const char* argv[])
int execvpe(const char* file, const char* argv[], char* const envp[])
首先,所有函数的返回类型都是 int。 但是,如果操作成功(即加载并替换新程序),则不会返回任何内容,因为当前程序不再接收返回值。
由于某些错误而失败,新程序未加载,-1 返回给现有程序。
在第一个参数中,路径和文件之间存在差异。 p、execlp、execvp 和 execvpe 有一个文件而不是路径。
该路径指定要执行/加载的文件的完整路径。 该文件指定路径名,这有助于找到新程序的文件。
在第二个参数中,不同之处在于带有 v 的函数有一个 char
类型的二维数组,其中包含多个字符串(包括文件名)。
相反,其他程序有一个或多个 char 类型的一维数组,其中此列表的第一个元素包含文件名,第二个元素可能包含一些参数等。
最后,对于具有 e
的函数,第三个/最后一个参数具有一个环境变量作为指针数组。
exec 系统调用的编码示例
在进一步讨论之前最好先看一个例子。 在这个例子中,我们使用程序的源代码作为程序的输入。
在这里,我们将这个程序(名称为 execl0.c )保存在可执行代码目录中。 这意味着源代码和可执行代码都存在于同一目录中。
#include <stdio.h>
#include <unistd.h>
int main(void) {
char binaryPath[] = "/bin/wc";
char arg1[]="wc";
char arg2[]="-w";
char arg3[]="execl0.c";
printf ("First line of current program\n");
execl(binaryPath, arg1, arg2, arg3, NULL);
printf ("Last line of current program\n");
return 1;
}
上面的代码使用了一个 execl
系统调用,只有几个简单的 char*
类型的变量。 第一个变量包含新程序(要执行)的路径和名称,第二个变量有一个参数 wc(同样是程序的名称)。
第三个变量有一个参数 -w 来运行命令作为 wc -w
来计算源文件中的单词数。
同样重要的是要注意两个额外的打印语句,第一个在系统调用之前,第二个在程序结束时。
输出:
First line of current program
32 execl0.c
输出显示我们的新程序已成功加载并执行。 但是,请注意第一个打印语句已执行(请参阅输出的第一行(“当前程序的第一行”)。
最后一条打印语句没有运行,因为当前程序在成功加载新程序时自动结束。
第二个输出行显示文件 execl0.c 中的字数。
C中的execve系统调用
现在,我们将详细讨论 execve 调用。
语法:
int execve(const char* path, const char* argv[], char* const envp[])
这里,第一个参数是路径; 如前所述,路径环境变量有助于找到要作为新程序执行的程序。
第二个参数是二维字符数组或具有命令行参数列表的一维字符串数组。
第三个参数也是一个二维字符数组或一个具有环境变量列表的一维字符串数组。
在 exec 系列中,execve
是一个引人注目的命令,具有三个参数路径、一个命令行参数列表和一个环境变量列表。 让我们看看从程序中执行 echo 命令的代码。
#include <stdio.h>
#include <unistd.h>
int main(void) {
char *binaryPath = "/bin/bash";
char *args[]={binaryPath, "-c", "echo visit $HOSTNAME:Fun with your browser","",NULL};
char *const env[]={"HOSTNAME=www.jiyik.com","port=8080",NULL};
execve(binaryPath, args, env);
return 1;
}
在 primary
函数的第一行,/bin/bash
是命令所在的路径。 在第二行中,命令行参数列表在终止参数的 NULL 之前包含三个参数。
同样,第一个参数是路径,第二个参数 -c 代表 cmd,它允许将代码作为字符串传递。
第三个参数是命令; 就像在代码中一样,echo
是命令。
代码的第三行有两个字符串和两个环境变量,HOSTNAME 和 port。 最后,代码的输出是:
visit www.jiyik.com:Fun with your browser
在这段代码中,我们从我们的程序中执行了一个 Linux 命令。 接下来,我们将在当前程序内部执行一个外部可执行程序。
首先看这个程序:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *args[]){
int i;
int count = atoi(args[1]);
for (i = 1 ; i <= count ; i++)
printf ("[%d]", i);
printf ("\n");
return 0;
}
该程序采用命令行参数。 命令行参数(作为字符串传递)在主函数的第二行中转换为整数。
接下来,我们运行一个从 one 到 count 的循环,并在方括号中打印计数。 查看此代码的输出。
$ ./test 4
[1][2][3][4]
我们已经创建了名为 test 的可执行文件。 我们从命令提示符处执行了带有参数 4 的测试文件。
我们可以在输出的方括号中看到从一到四的计数。
接下来,我们必须将这个程序测试作为来自另一个程序的外部命令来运行。 为此,我们必须指定可执行测试程序的路径。
这里,完整的路径是 /home/mateen/Documents/test 。 因此,我们将在下一个程序中指定此路径来定位可执行文件。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *ar[]) {
printf("This is the first line\n");
char *binaryPath = "/bin/bash";
char name[80] = "/home/mateen/Documents/test ";
strcat(name, ar[1]);
char *args[]={binaryPath, "-c", name, NULL};
char *env_args[] = {"/bin/bash", (char*)0};
execve(binaryPath, args, env_args);
printf("This is the last line\n");
return 1;
}
我们包含了另一个库来使用该函数来连接字符串。 在 main
函数的第三行,我们有完整的路径和文件名,因为这不是 Linux 命令; 相反,这是用户定义的可执行程序(已经详细讨论过)。
在下一行中,我们将传递给当前程序的命令行参数与新程序的名称连接起来。 同样,在第五行中,我们的命令行参数具有路径 -c。
第三个参数是具有路径的变量名称+可执行文件的名称+传递给当前程序的参数。
输出:
$ ./a.out 5
This is the first line
[1][2][3][4][5]
我们正在使用命令行参数 5 运行当前程序。输出的第一行包含第一条打印语句。
接下来就可以看到我们的测试程序被执行了。 从 1 到 5 的计数写在方括号中。
最后,结论是我们可以使用 execve 运行 Linux 命令和我们的可执行程序。 以 Linux 命令为例,我们可以通过路径来定位Linux程序。
对于一些其他/外部可执行文件,我们可以给出带有文件名的完整路径; 在这种情况下,程序将自动位于给定路径。 在这种情况下,该命令将忽略主函数第三行中的路径变量。
相关文章
在 C 语言中使用 typedef enum
发布时间:2023/05/07 浏览次数:181 分类:C语言
-
本文介绍了如何在 C 语言中使用 typedef enum。使用 enum 在 C 语言中定义命名整数常量 enum 关键字定义了一种叫做枚举的特殊类型。
C 语言中的 extern 关键字
发布时间:2023/05/07 浏览次数:114 分类:C语言
-
本文介绍了如何在 C 语言中使用 extern 关键字。C 语言中使用 extern 关键字来声明一个在其他文件中定义的变量
C 语言中的 #ifndef
发布时间:2023/05/07 浏览次数:186 分类:C语言
-
本文介绍了如何在 C 语言中使用 ifndef。在 C 语言中使用 ifndef 保护头文件不被多次包含 C 语言中的头文件用于定义同名源文件中实现的函数的接口。