在 execve 函数中,参数通过指针数组传递。如果这些指针指向前一个堆栈中的内存,这些内存是否仍然可以在新的过程映像中访问。
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char filename[20] = "a.out";
char str[20] = "hello\n";
char *argv[3];
argv[0] = filename;
argv[1] = str;
argv[2] = NULL;
execve("/hel/a.out", argv, NULL);
return 0;
}
/* /hel/a.out code */
#include <stdio.h>
int main(int argc, char *argv[], char *envp[])
{
printf("%s\n", argv[1]); /** Here, should the memory pointed by argv[1]
* be freed after execve has been called?
*/
return 0;
}
Run Code Online (Sandbox Code Playgroud)
仔细阅读execve(2)的文档(以及Advanced Linux Programming以获得更广阔的视野)。阅读有关虚拟内存、分页、MMU、进程的信息。
该execve 系统调用是安装一个全新的虚拟地址空间中的进程(所以程序做旧的虚拟地址空间execve 成功地消失,被新的不被覆盖),所以你不共享与前一个的任何数据(和成功execve不会返回,因为新程序已启动)。您的新程序稍后将能够更改虚拟地址空间,例如使用mmap(2) ...
地址argv在新的虚拟地址空间字符串是独立的参数的地址execve; 字符串内容相同。没有数据被共享旧的虚拟地址空间和新的之间,而且参数的新程序(和程序环境)的复制。另请阅读有关ASLR 的信息
的参数execve是在其起始函数( 在crt0中调用)的新虚拟地址空间的新调用堆栈上复制的字符串(连同它们的副本被推送)。当然,你不应该有任何i - 那将是未定义的行为。_startmainfreeargv[]
因此int a; argv[1]=(char*)&a;... execvewith argv, 是未定义的行为,因为您不能保证地址处的内存区域a是正确的以空字符结尾的字符串。阅读有关字节顺序和ABI 的信息。
所以execve 想要一个NULL终止argv的正确字符串数组(不是任意指针),以及另一个NULL 终止env的字符串数组,每个字符串应该以零字节终止。ARG_MAX通过argv&从旧地址空间复制到新地址空间的总内存空间有一个相当小的限制(通常为 128 KB)env。
您可能会使用共享内存(请参阅shm_overview(7))在各个进程之间共享内存(并且您将与信号量同步,请参阅sem_overview(7) ...);但您通常更喜欢其他进程间通信技术(例如pipe(7) -s、fifo(7) -s、socket(7) -s 等...)。
顺便说一句,还使用strace(1)来了解您的程序涉及哪些系统调用,并使用proc(5),特别是通过运行cat /proc/$$/maps并了解有关虚拟地址空间的更多信息。cat /proc/$pidofyourprogram/maps
你甚至可以把你的两个main函数(execve在第一个之前,return 0;在第二个之前)像
char cmd[64];
snprintf(cmd, sizeof(cmd), "/bin/cat /proc/%d/maps", (int)getpid());
printf("before running %s\n", cmd);
fflush(NULL);
int err = system(cmd);
if (err) fprintf(stderr, "system failed err=%d\n", err);
else printf("system %s done\n", cmd);
Run Code Online (Sandbox Code Playgroud)
这将显示虚拟地址空间的视图。当然,更严肃的程序应该fopen是一个/proc/1234/maps文件,然后循环fgets读取每一行直到EOF那时fclose。
请耐心阅读此处的所有参考资料,并花时间了解有关 POSIX 编程的更多信息。研究一些免费软件源代码(例如在http://github.com/ 上,您可以选择一些有趣的项目...)并为它们做出贡献应该是值得的。