我在C ++ STL容器中有一个存储file
,argv
和envp
的结构。该结构还具有将它们转换为execve
调用的c样式指针的方法。
struct Foo {
std::string file;
std::vector <std::string> argv;
std::unordered_map <std::string, std::string> envp;
inline auto c_file() const;
inline auto c_argv() const;
inline auto c_envp() const;
}
// Function: c_file
inline auto Foo::c_file() const {
return file.c_str();
}
// Function: c_argv
inline auto Foo::c_argv() const {
auto ptr = std::make_unique<char*[]>(argv.size() + 1);
for(size_t i=0; i<argv.size(); ++i) {
ptr[i] = const_cast<char*>(argv[i].c_str());
}
ptr[argv.size()] = nullptr;
return ptr;
}
// Function: c_envp
inline auto Foo::c_envp() const {
std::unique_ptr<char*, std::function<void(char**)>> ptr(
new char*[envp.size() + 1],
[sz=envp.size()+1](char** ptr) {
for(size_t i=0; i<sz; ++i) {
delete [] ptr[i];
}
delete [] ptr;
}
);
auto idx = size_t{0};
for(const auto& kvp : envp) {
auto entry = kvp.first + "=" + kvp.second;
ptr.get()[idx] = new char[entry.size() + 1];
::strncpy(ptr.get()[idx], entry.c_str(), entry.size() + 1);
++idx;
}
ptr.get()[idx] = nullptr;
return ptr;
}
在我的程序中,我使用以下方式调用execve
。
void in_parent_process(const Foo& foo) {
char stack[1024*1024];
::clone(child_entry, stack + 1024*1024, myflags, &foo)
}
void child_entry(void* ptr) {
const auto& foo = *static_cast<Foo*>(ptr);
::execve(foo.c_file(), foo.c_argv().get(), foo.c_envp().get());
}
但是,对execve
的调用有时会挂起而没有任何回复。这使得我的父进程无法与子进程同步(通过管道close-on-exec)。我在这里使用unique_ptr
的想法有什么问题吗?或者我对将堆栈设置为clone
调用的理解有问题吗?
答案 0 :(得分:0)
或者我对将堆栈设置为克隆调用的理解有问题吗?
是的,你的代码错了。您错误地使用了clone(2)作为堆栈区域automatic variable,它位于当前call stack中(因此一旦调用函数返回就会失效;这是典型的undefined behavior )。但是,堆栈区域通常应该是某些mmap(2)(或至少某些malloc(3))的结果,并且不应该是调用线程的调用堆栈的一部分。
我认为您不应该使用clone
,因为它已被pthreads(7)使用(Linux上的C ++ 11线程也使用它)
正如我评论的那样,你应该使用strace(1)来进行调试(特别是要了解当“对execve的调用有时会挂起”时会发生什么)。此外,当execve(2)失败时,它会返回并继续,因此您应该使用perror
(或更好的方法,记录errno
...)。否则execve
成功并重新初始化您的流程(包括其整个virtual address space)以运行新程序。最后,为什么不使用现有的容器化机器(例如Docker)或者至少仔细研究它们的源代码(它们是免费软件)?
对execve的调用有时会挂起而没有任何响应
BTW,execve
是system call。因此它要么成功而不返回(通过重新初始化您的进程以启动新程序),要么快速失败(并且execve
仅在失败时返回)。在任何一种情况下,execve
都由kernel在最多几毫秒内完成。所以我无法想象它是如何“挂”的。因此, execve
无法自行挂起。
(换句话说,它不是execve
挂起而是其他东西)
您需要在execve
之后添加错误处理,至少使用
void child_entry(void* ptr) {
const auto& foo = *static_cast<Foo*>(ptr);
::execve(foo.c_file(), foo.c_argv().get(), foo.c_envp().get());
perror("execve");
exit(EXIT_FAILURE);
}
您可以将perror("execve");
替换为perror((std::string{"execve of "}+foo.c_file()).c_str());
之类的更详细的内容;你也可以考虑使用_exit(2)而不是exit(3),但是你可能应该刷新缓冲的IO(例如使用fflush(3) ...)
您可能还想使用日志记录工具,请参阅syslog(3)。
请注意PATH variable(您可以使用environ(7) getenv("PATH")
作为execve
在getenv(3)中查询... {也许你想要execvp(3)而不是Command
。