Неверный статус при вызове _exit (errno) от дочернего

Я зову execvp() с намеренно неправильным аргументом в fork()Эд ребенок. errno номер правильно установлен на ENOENT в дочернем процессе. Затем я завершаю дочерний процесс _exit(errno);,

Мой основной процесс звонки wait(), Когда я проверяю возвращенный статус с WIFEXITED а также WEXITSTATUS я всегда получить EINVAL для первого вызова. Все остальные вызовы возвращают правильный ENOENT код.

Я не могу объяснить это поведение. Ниже приведена полная функция, которая выполняет все вышеперечисленные действия, но немного сложнее.

QVariantMap
System::exec(const QString & prog, const QStringList & args)
{
pid_t pid = fork();

if (pid == 0) {
int cargs_len = args.length() + 2;
char * cargs[cargs_len];
cargs[cargs_len - 1] = NULL;

QByteArrayList as;
as.push_back(prog.toLocal8Bit());

std::transform(args.begin(), args.end(), std::back_inserter(as),
[](const QString & s) { return s.toLocal8Bit(); });

for (int i = 0; i < as.length(); ++i) {
cargs[i] = as[i].data();
}

execvp(cargs[0], cargs);

// in case execvp fails, terminate the child process immediately
qDebug() << "(" << errno << ") " << strerror(errno);  // <----------
_exit(errno);

} else if (pid < 0) {
goto fail;

} else {

sigset_t mask;
sigset_t orig_mask;

sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);

if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
goto fail;
}

struct timespec timeout;
timeout.tv_sec = 0;
timeout.tv_nsec = 10 * 1000 * 1000;

while (true) {
int ret = sigtimedwait(&mask, NULL, &timeout);

if (ret < 0) {
if (errno == EAGAIN) {
// timeout
goto win;
} else {
// error
goto fail;
}

} else {
if (errno == EINTR) {
// not SIGCHLD
continue;
} else {
int status = 0;
if (wait(&status) == pid) {
if (WIFEXITED(status)) {
return { { "error", strerror(WEXITSTATUS(status)) } };
} else {
goto fail;
}
} else {
goto fail;
}
}
}
}
}

win:
return {};

fail:
return { { "error", strerror(errno) } };
}

Оказывается, что удаление строки с qDebug() вызов заставляет проблему уйти. Почему добавление отладочного вызова меняет поведение программы?

2

Решение

qDebug() << "(" << errno << ") " << strerror(errno);
_exit(errno);

Практически любой вызов стандартной библиотечной функции может изменить errno, Вероятно, что qDebug вызывает некоторые функции ввода / вывода, которые устанавливают errnoили, может быть, даже << Оператор ввода / вывода. errno не изменяется большинством успешных звонков, но чем выше уровень, тем меньше вы можете знать, что под капотом нет нормальных неудавшихся звонков. Так что ценность errno что вы печатаете не ценность errno что вы передаете _exit,

Как общий принцип с errnoЕсли вы делаете что-то более сложное, чем просто распечатываете его один раз, сохраните значение в переменной, прежде чем делать что-либо еще.

Как уже отмечалось в комментариях, обратите внимание, что большинство систем Unix, включая все распространенные, передают только 8-битные значения как состояния выхода, но errno может быть больше 255. Например, если вы запустите эту программу в системе, где 256 — это возможный код ошибки, вызов _exit(256) приведет к тому, что вызывающая сторона увидит код возврата 0 и, следовательно, неверно поверит в успех.

Обычно достаточно свернуть все значения ошибок до успеха / неудачи. Если вам нужно различать больше, убедитесь, что информация, которую вы передаете через exit/wait подходит в диапазоне 0–255.

int exec_error = errno;
qDebug() << "(" << exec_error << ") " << strerror(exec_error);
_exit(!!exec_error);
2

Другие решения


По вопросам рекламы [email protected]