为什么 system() 失败并显示错误代码 127
Why does system() fail with error code 127?
在Linux系统上,我试图在运行时使用system()
调用调用程序。系统调用以不等于零的返回代码退出。
对错误代码调用WEXITSTATUS
会给出"127"。
根据系统的手册页,此代码指示无法调用/bin/sh
:
如果无法执行/bin/sh
,退出状态将为执行exit(127)
的命令的状态。
我检查了:/bin/sh
是指向bash
的链接。 bash
就在那里。我可以从外壳执行它。
现在,我怎样才能知道为什么不能调用/bin/sh
?任何内核历史记录或其他东西?
编辑:
在非常有用的提示(见下文(之后,我strace -f -p <PID>
该过程。这是我在system
通话中得到的:
Process 16080 detached
[pid 11779] <... select resumed> ) = ? ERESTARTNOHAND (To be restarted)
[pid 11774] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 127}], 0, NULL) = 16080
[pid 11779] --- SIGCHLD (Child exited) @ 0 (0) ---
[pid 11779] rt_sigaction(SIGCHLD, {0x2ae0ff898ae2, [CHLD], SA_RESTORER|SA_RESTART, 0x32dd2302d0}, <unfinished ...>
[pid 11774] rt_sigaction(SIGINT, {0x2ae1042070f0, [], SA_RESTORER|SA_SIGINFO, 0x32dd2302d0}, <unfinished ...>
[pid 11779] <... rt_sigaction resumed> {0x2ae0ff898ae2, [CHLD], SA_RESTORER|SA_RESTART, 0x32dd2302d0}, 8) = 0
[pid 11779] sendto(5, "a", 1, 0, NULL, 0 <unfinished ...>
[pid 11774] <... rt_sigaction resumed> NULL, 8) = 0
[pid 11779] <... sendto resumed> ) = 1
[pid 11779] rt_sigreturn(0x2 <unfinished ...>
[pid 11774] rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x32dd2302d0}, <unfinished ...>
[pid 11779] <... rt_sigreturn resumed> ) = -1 EINTR (Interrupted system call)
[pid 11779] select(16, [9 15], [], NULL, NULL <unfinished ...>
[pid 11774] <... rt_sigaction resumed> NULL, 8) = 0
[pid 11774] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid 11774] write(1, "Problems calling nvcc jitter: ex"..., 49) = 49
[pid 11774] rt_sigaction(SIGINT, {0x1, [], SA_RESTORER, 0x32dd2302d0}, {0x2ae1042070f0, [], SA_RESTORER|SA_SIGINFO, 0x32dd2302d0}, 8) = 0
[pid 11774] rt_sigaction(SIGQUIT, {0x1, [], SA_RESTORER, 0x32dd2302d0}, {SIG_DFL, [], SA_RESTORER, 0x32dd2302d0}, 8) = 0
[pid 11774] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
[pid 11774] clone(Process 16081 attached (waiting for parent)
Process 16081 resumed (parent 11774 ready)
child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fff0177ab68) = 16081
[pid 16081] rt_sigaction(SIGINT, {0x2ae1042070f0, [], SA_RESTORER|SA_SIGINFO, 0x32dd2302d0}, <unfinished ...>
[pid 11774] wait4(16081, Process 11774 suspended
<unfinished ...>
[pid 16081] <... rt_sigaction resumed> NULL, 8) = 0
[pid 16081] rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x32dd2302d0}, NULL, 8) = 0
[pid 16081] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid 16081] execve("/bin/sh", ["sh", "-c", 0xdda1d98], [/* 58 vars */]) = -1 EFAULT (Bad address)
[pid 16081] exit_group(127) = ?
Process 11774 resumed
当涉及到/bin/sh
的电话时,它说的是错误的地址。为什么呢?
编辑:
这里涉及失败system
的整个部分(这里已经安全复制到缓冲区(:
std::ostringstream jit_command;
jit_command << string(CUDA_DIR) << "/bin/nvcc -v --ptxas-options=-v ";
jit_command << "-arch=" << string(GPUARCH);
jit_command << " -m64 --compiler-options -fPIC,-shared -link ";
jit_command << fname_src << " -I$LIB_PATH/include -o " << fname_dest;
string gen = jit_command.str();
cout << gen << endl;
char* cmd = new(nothrow) char[gen.size()+1];
if (!cmd) ___error_exit("no memory for jitter command");
strcpy(cmd,gen.c_str());
int ret;
if (ret=system(cmd)) {
cout << "Problems calling nvcc jitter: ";
if (WIFEXITED(ret)) {
printf("exited, status=%dn", WEXITSTATUS(ret));
} else if (WIFSIGNALED(ret)) {
printf("killed by signal %dn", WTERMSIG(ret));
} else if (WIFSTOPPED(ret)) {
printf("stopped by signal %dn", WSTOPSIG(ret));
} else if (WIFCONTINUED(ret)) {
printf("continuedn");
} else {
printf("not recognizedn");
}
cout << "Checking shell.. ";
if(system(NULL))
cout << "ok!n";
else
cout << "nope!n";
__error_exit("Nvcc errorn");
}
delete[] cmd;
return true;
输出:
/usr/local/cuda/bin/nvcc -v --ptxas-options=-v -arch=sm_20 -m64 --compiler-options -fPIC,-shared -link bench_cudp_Oku2fm.cu -I$LIB_PATH/include -o bench_cudp_Oku2fm.o
Problems calling nvcc jitter: exited, status=127
Checking shell.. ok!
编辑(代码的第一个版本(:
string gen = jit_command.str();
cout << gen << endl;
int ret;
if (ret=system(gen.c_str())) {
....
字符串创建的复杂性不是这里的问题。正如strace
所示,"错误地址"是问题所在。这是一个合法的字符串。不应出现"错误地址"。
据我所知,std::string::c_str()
返回一个const char *
,该可能指向libc ++的暂存空间,其中可能保留了字符串的只读副本。
不幸的是,该错误实际上无法重现。对system
的调用在失败之前会成功几次。
我不想仓促行事,但它闻起来像内核、libc 或硬件中的错误。
编辑:
我生成了一个更详细的strace
输出(strace -f -v -s 2048 -e trace=process -p $!
失败的execve
系统调用:
首先是后续调用:
[pid 2506] execve("/bin/sh", ["sh", "-c", "/usr/local/cuda/bin/nvcc -v --ptxas-options=-v -arch=sm_20 -m64 --compiler-options -fPIC,-shared -link /home/user/toolchain/kernels-empty/bench_cudp_U11PSy.cu -I$LIB_PATH/include -o /home/user/toolchain/kernels-empty/bench_cudp_U11PSy.o"], ["MODULE_VERSION_STACK=3.2.8", ... ]) = 0
现在失败的:
[pid 17398] execve("/bin/sh", ["sh", "-c", 0x14595af0], <list of vars>) = -1 EFAULT (Bad address)
在这里<list of vars>
是相同的。似乎不是导致错误地址的环境变量列表。正如 Chris Dodd 提到的,执行的第 3 个参数是原始指针0x14595af0,strace 认为(内核同意(它是无效的。 strace
无法将其识别为字符串(因此它打印十六进制值而不是字符串(。
编辑:
我插入了指针值的打印cmd
以查看此指针在父进程中的值是什么:
string gen = jit_command.str();
cout << gen << endl;
char* cmd = new(nothrow) char[gen.size()+1];
if (!cmd) __error_exit("no memory for jitter command");
strcpy(cmd,gen.c_str());
cout << "cmd = " << (void*)cmd << endl;
int ret;
if (ret=system(cmd)) {
cout << "failed cmd = " << (void*)cmd << endl;
cout << "Problems calling nvcc jitter: ";
输出(对于失败的呼叫(:
cmd = 0x14595af0
failed cmd = 0x14595af0
Problems calling nvcc jitter: exited, status=127
Checking shell.. ok!
它与 strace
中的第三个参数相同的指针值。(我更新了上面的strace
输出(。
关于cmd
指针的 32 位外观:我检查了后续调用的cmd
指针的值。看不出结构上有任何差异。这是调用成功时system
cmd
的值之一:
cmd = 0x145d4f20
因此,在system
调用之前,指针是有效的。正如上面的strace
输出所示,子进程(在调用fork
之后(接收正确的指针值。但是,由于某种原因,指针值在子进程中被标记为无效。
现在我们认为它要么:
- libc/kernel bug
- 硬件问题
编辑:
同时,让我发布一个解决方法。被迫实施这样的事情太愚蠢了......但它有效。因此,如果system
调用失败,将执行以下代码块。它分配新的命令字符串并重试,直到成功(不是无限期(。
list<char*> listPtr;
int maxtry=1000;
do{
char* tmp = new(nothrow) char[gen.size()+1];
if (!tmp) __error_exit("no memory for jitter command");
strcpy(tmp,gen.c_str());
listPtr.push_back( tmp );
} while ((ret=system(listPtr.back())) && (--maxtry>0));
while(listPtr.size()) {
delete[] listPtr.back();
listPtr.pop_back();
}
编辑:
我刚刚看到这种解决方法在一次特定运行中不起作用。它一路走来,1000次尝试,全部使用新分配的cmd
命令字符串。所有 1000 个都失败了。不仅如此。我尝试了不同的Linux主机(相同的Linux/软件配置(。
考虑到这一点,可能会排除硬件问题。(必须在 2 个物理上不同的主机上(。仍然是内核错误??
编辑:
托雷克,我将尝试安装修改后的system
调用。给我一些时间。
这是一个奇怪的问题。 strace
理解要执行的参数是(指向(字符串,因此它会打印出指向的字符串,除非指针无效 - 在这种情况下,它会打印出指针的原始十六进制值。 所以跟踪线
[pid 16081] execve("/bin/sh", ["sh", "-c", 0xdda1d98], [/* 58 vars */]) = -1 EFAULT (Bad address)
完全有道理 - 执行的第 3 个参数是原始指针0xdda1d98,strace 认为(内核同意(它是无效的。 所以问题是,无效指针是如何到达这里的。 这应该是 cmd,它刚刚从新回来。
我建议把这条线放
printf("cmd=%pn", cmd);
就在系统调用之前,弄清楚 C 代码认为指针是什么。
查看 strace 的其余部分,看起来您正在 64 位系统上运行(从打印的指针(,而无效0xdda1d98看起来像一个 32 位指针,所以它似乎是某种 32/64 位搞砸了(有人只保存和恢复 32 位寄存器的 64 位, 或诸如此类(。
捎带/扩展 @Chris多德的答案,考虑system
本身看起来像这样(故意过度简化(:
int system(char *cmd) {
pid_t pid = fork();
char *argv[4];
extern char **environ;
if (pid == 0) { /* child */
argv[0] = "sh";
argv[1] = "-c";
argv[2] = cmd;
argv[3] = NULL;
execve("/bin/sh", argv, environ);
_exit(127);
}
if (pid < 0) ... handle error ...
... use OS wait() calls to wait for result from child process ...
return status; /* as provided by sh -c, or from _exit(127) above */
}
给定"64 位系统"和"寄存器似乎在 32 位时被切断",可能值得对代码进行 objdump 并查看 argv[2] 是否是从寄存器设置的,该寄存器在clone
调用期间可能会以某种方式丢失其上位(我在上面fork
,glibc
正在使用clone
提高效率(。
更新:根据 strace 输出,克隆调用不使用
CLONE_VM
和CLONE_VFORK
(不知道为什么不,这些应该使调用更有效率(,所以孩子是一个"正常"的孩子(一个古老的 Unix 风格的fork
(。 一位同事建议,失败的地址可能位于设置为不复制到子进程中的映射中。 失败后,/proc/self/maps
的内容会很有趣;我们可以看看失败的地址是如何映射的。 将这些地图与孩子身上的地图进行比较会更有趣。 但是,要获得子项中的那些,您需要覆盖glibc
版本的system
,并在execve
失败后添加一些内容以读取/proc/self/maps
,然后再执行_exit
。
- 我是 c++ 的新手.学习基本知识后,我想做井字游戏.对于印刷板,我在下面写代码,但它显示错误
- Clion显示错误,但可以使用Cmake成功构建代码
- 代码使用向量成功运行,但使用数组显示错误
- 与clang++一起使用的VS代码在构建良好的C++文件中显示错误
- 当我选择大于 720 的矩阵大小时,程序退出并显示错误代码.可能是什么原因?
- 我的代码厨师提交显示错误的结果
- C++代码编译,但在 Zorin OS 上运行时显示错误
- 为什么下面的代码在通过引用传递时没有显示错误
- 在语法文件上运行C 的ANTL4解析器显示错误33:缺少代码生成模板非局部trrefheader
- C++ 为什么不使用已删除的函数时编译器失败并显示错误代码 C2280
- 我的 MergeSort 显示错误的输出,您认为我的代码有什么问题
- 从互联网下载文件,HttpSendRequest失败并显示错误代码
- 为什么代码::块显示错误的多个定义
- 如果显示错误,如何阻止代码运行
- 为什么 system() 失败并显示错误代码 127
- 为什么以下代码编译良好,但使用static时链接显示错误
- 此代码显示错误"stu undeclared" ??我该怎么办
- 为什么 LoadUserProfile() 在系统服务中运行的此代码中失败并显示错误 5 "Denied Access"?
- 代码块在程序中显示错误,但我找不到它
- 为什么我的代码显示错误分段错误(核心转储)