Auf einem Linux-System versuche ich, ein Programm zur Laufzeit mit der system()
anrufen. Der Systemaufruf wird mit einem Rückgabewert ungleich Null beendet.
Aufruf von WEXITSTATUS
auf den Fehlercode gibt "127".
Laut der Manpage des Systems zeigt dieser Code an, dass /bin/sh
konnte nicht aufgerufen werden:
Für den Fall /bin/sh
konnte nicht ausgeführt werden, wird der Exit-Status der eines Befehls sein, der exit(127)
.
Ich habe es überprüft: /bin/sh
ist ein Link zu bash
. bash
ist da. Ich kann es über die Shell ausführen.
Wie kann ich nun herausfinden, warum /bin/sh
konnte nicht aufgerufen werden ? Gibt es eine Kernelgeschichte oder ähnliches?
Editer :
Nach dem sehr hilfreichen Tipp (siehe unten) i strace -f -p <PID>
den Prozess. Das erhalte ich während der system
anrufen:
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
Wenn es um den Aufruf zu /bin/sh
es wird eine falsche Adresse angezeigt. Warum das?
Editer :
Hier der ganze Teil, der das Scheitern betrifft system
(hier ist bereits die sichere Kopie in einen Puffer vorhanden):
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=%d\n", WEXITSTATUS(ret));
} else if (WIFSIGNALED(ret)) {
printf("killed by signal %d\n", WTERMSIG(ret));
} else if (WIFSTOPPED(ret)) {
printf("stopped by signal %d\n", WSTOPSIG(ret));
} else if (WIFCONTINUED(ret)) {
printf("continued\n");
} else {
printf("not recognized\n");
}
cout << "Checking shell.. ";
if(system(NULL))
cout << "ok!\n";
else
cout << "nope!\n";
__error_exit("Nvcc error\n");
}
delete[] cmd;
return true;
Sortie :
/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!
Bearbeiten (erste Version des Codes):
string gen = jit_command.str();
cout << gen << endl;
int ret;
if (ret=system(gen.c_str())) {
....
Die Komplexität der String-Erstellung ist hier nicht das Problem. Wie strace
zeigt, dass eine "falsche Adresse" das Problem ist. Es ist eine legale Zeichenfolge. Eine "schlechte Adresse" sollte nicht vorkommen.
Soweit ich weiß, ist die std::string::c_str()
gibt eine const char *
die auf einen Scratch-Space von libc++ verweisen könnte, in dem eine schreibgeschützte Kopie der Zeichenkette aufbewahrt werden könnte.
Leider ist der Fehler nicht wirklich reproduzierbar. Der Aufruf von system
mehrmals erfolgreich ist, bevor es scheitert.
Ich will nicht voreilig sein, aber es riecht nach einem Fehler entweder im Kernel, in der libc oder in der Hardware.
Editer :
Ich habe ein ausführlicheres strace
Ausgabe ( strace -f -v -s 2048 -e trace=process -p $!
) des ausfallenden execve
Systemaufruf:
Zunächst ein anschließender Anruf:
[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
Und jetzt das Scheitern:
[pid 17398] execve("/bin/sh", ["sh", "-c", 0x14595af0], <list of vars>) = -1 EFAULT (Bad address)
Hier <list of vars>
ist identisch. Es scheint nicht die Liste der Umgebungsvariablen zu sein, die die falsche Adresse verursacht. Wie Chris Dodd erwähnte, ist das dritte Argument von execve der rohe Zeiger 0x14595af0, den strace für ungültig hält (und der Kernel stimmt dem zu). strace
nicht als Zeichenkette erkennt (und daher den Hex-Wert und nicht die Zeichenkette ausgibt).
Editer :
Ich habe den Ausdruck des Zeigerwerts eingefügt cmd
um zu sehen, welchen Wert dieser Zeiger im übergeordneten Prozess hat:
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: ";
Ausgabe (für den fehlgeschlagenen Aufruf):
cmd = 0x14595af0
failed cmd = 0x14595af0
Problems calling nvcc jitter: exited, status=127
Checking shell.. ok!
Es ist derselbe Zeigerwert wie das 3. Argument von strace
. (Ich habe die strace
Ausgabe oben).
Bezüglich des 32bit-Aussehens des cmd
Zeiger: Ich habe den Wert des cmd
Zeiger für einen nachfolgenden Aufruf. Ich kann keinen Unterschied in der Struktur erkennen. Das ist einer der Werte von cmd
wenn dann system
Der Aufruf ist erfolgreich:
cmd = 0x145d4f20
Also, bevor die system
Aufruf ist der Zeiger gültig. Da der strace
Die obige Ausgabe legt nahe, dass der Kindprozess (nach dem Aufruf von fork
) erhält den richtigen Zeigerwert. Aber aus irgendeinem Grund wird der Zeigerwert im Kindprozess als ungültig markiert.
Im Moment denken wir, dass es entweder:
- libc/Kernel-Fehler
- Hardware-Problem
Editer :
In der Zwischenzeit möchte ich eine Problemlösung anbieten. Es ist so dumm, gezwungen zu sein, so etwas zu implementieren... aber es funktioniert. Der folgende Codeblock wird also ausgeführt, wenn die system
Anruf scheitert. Er ordnet neue Befehlszeichenfolgen zu und versucht es so lange, bis er erfolgreich ist (allerdings nicht unbegrenzt).
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();
}
Editer :
Ich habe gerade gesehen, dass diese Abhilfe in einem bestimmten Durchlauf nicht funktioniert hat. Es ging den ganzen Weg, 1000 Versuche, alle mit neu zugewiesenen cmd
Befehlszeichenfolgen. Alle 1000 schlugen fehl. Nicht nur dies. Ich habe es auf einem anderen Linux-Host versucht (allerdings mit derselben Linux-/Softwarekonfiguration).
Wenn man dies berücksichtigt, könnte man ein Hardwareproblem ausschließen. (Dann muss es sich um 2 physisch verschiedene Hosts handeln). Bleibt ein Kernel-Bug ??
Editer :
torek, ich werde versuchen, eine modifizierte system
anrufen. Geben Sie mir dafür etwas Zeit.