Можно ли перезапустить программу изнутри программы?
Я разрабатываю программу на C++, и было бы полезно использовать какую-то функцию, скрипт или что-то, что заставляет программу перезапускаться. Это большая программа, поэтому перезапуск всех переменных вручную займет у меня много времени...
Я не знаю, есть ли способ достичь этого или это возможно.
10 ответов
Если вам действительно нужно перезапустить всю программу (то есть снова "закрыть" и "открыть"), "правильным" способом будет иметь отдельную программу с единственной целью перезапустить основную. AFAIK многие приложения с функцией автообновления работают таким образом. Поэтому, когда вам нужно перезапустить основную программу, вы просто вызываете "перезапуск" и выходите.
Вы можете использовать цикл в вашем main
функция:
int main()
{
while(!i_want_to_exit_now) {
// code
}
}
Или, если вы действительно хотите перезапустить программу, запустите ее из жгута:
program "$@"
while [ $? -e 42 ]; do
program "$@"
done
где 42
код возврата, означающий "перезагрузите, пожалуйста".
Тогда внутри программы ваш restart
функция будет выглядеть так:
void restart() {
std::exit(42);
}
На Unicies или где-либо еще у вас есть execve
и это работает, как указано на странице руководства, вы можете просто... убить меня за использование atoi
потому что это вообще ужасно, за исключением такого рода случая.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char** argv) {
(void) argc;
printf("arg: %s\n", argv[1]);
int count = atoi(argv[1]);
if ( getchar() == 'y' ) {
++count;
char buf[20];
sprintf(buf, "%d", count);
char* newargv[3];
newargv[0] = argv[0];
newargv[1] = buf;
newargv[2] = NULL;
execve(argv[0], newargv, NULL);
}
return count;
}
Пример:
$ ./res 1
arg: 1
y
arg: 2
y
arg: 3
y
arg: 4
y
arg: 5
y
arg: 6
y
arg: 7
n
7 | $
(7 был код возврата).
Он ни рекурсивно, ни явно не зацикливается - вместо этого он просто вызывает себя, заменяя свое собственное пространство памяти новой версией самого себя.
Таким образом, стек никогда не будет переполнен, хотя все предыдущие переменные будут переопределены, как и при любой повторной вызове - getchar
вызов предотвращает 100% загрузку процессора.
В случае самообновляющегося двоичного файла, поскольку весь двоичный файл (по крайней мере, для Unix-подобных, я не знаю о Windows) будет скопирован в память во время выполнения, то, если файл изменяется на диске до execve(argv[0], ...
вызов, вместо этого будет запущен новый двоичный файл, найденный на диске, а не тот же самый старый.
Как отмечают @CarstenS и @bishop в комментариях, благодаря уникальному способу разработки Unix дескрипторы открытых файлов сохраняются fork
/ exec
и, как следствие, во избежание утечки открытых файловых дескрипторов при обращении к execve
, вы должны либо закрыть их раньше execve
или открыть их с помощью e
, FD_CLOEXEC
/ O_CLOEXEC
в первую очередь - больше информации можно найти в блоге Дэна Уолша.
Это очень специфичный для ОС вопрос. В Windows вы можете использовать API перезапуска приложений или MFC Restart Manager. В Linux вы могли бы сделать exec()
Однако в большинстве случаев есть лучшее решение. Скорее всего, вам лучше использовать цикл, как предлагается в других ответах.
Это звучит как неправильный подход, так как все ваше состояние является глобальным, и поэтому единственный четкий метод сброса всего (кроме ручного назначения значений "по умолчанию" для каждой переменной) - это перезапуск всей программы.
Вместо этого ваше состояние должно храниться в объектах (типа класса или чего-либо еще). Тогда вы можете создавать и уничтожать эти объекты в любое время. Каждый новый объект имеет новое состояние со значениями по умолчанию.
Не боритесь с C++; используй это!
Вам, вероятно, нужен цикл:
int main()
{
while (true)
{
//.... Program....
}
}
Каждый раз, когда вам нужно перезагрузить, звоните continue;
в цикле, и чтобы завершить вашу программу, используйте break;
,
Когда я разрабатываю системы реального времени, мой подход обычно является "производным main()", где я пишу весь код, вызываемый из реального main(), что-то вроде:
Программа main.cpp:
int main (int argc, char *argv[])
{
while (true)
{
if (programMain(argc, argv) == 1)
break;
}
}
Программа main.cpp, где написан весь код:
int programMain(int argc, char *argv[])
{
// Do whatever - the main logic goes here
// When you need to restart the program, call
return 0;
// When you need to exit the program, call
return 1;
}
Таким образом, каждый раз, когда мы решаем выйти из программы, программа будет перезапущена.
Подробно: все переменные, глобалы и логика должны быть записаны внутри programMain()
- ничего внутри "main()"
кроме перезапуска управления.
Этот подход работает в системах Linux и Windows.
Мне кажется, что вы задаете неправильный вопрос, потому что вы недостаточно знаете о кодировании, чтобы задать правильный вопрос.
Звучит так, будто вы спрашиваете, как написать некоторый код, при котором при пропущенном вызове он возвращается к исходному состоянию и перезапускает всю последовательность вызова / местоположения. В этом случае вам нужно использовать конечный автомат. Посмотрите, что это такое, и как написать. Это ключевая концепция программного обеспечения, и вы должны знать, если ваши учителя хорошо справляются со своей работой.
В качестве примечания: если вашей программе для инициализации всех ваших переменных требуется 5 секунд, то при перезапуске все равно потребуется 5 секунд. Вы не можете сократить это. Из этого должно быть ясно, что вы на самом деле не хотите убивать и перезапускать свою программу, потому что тогда вы получите именно то поведение, которое вам не нужно. С помощью конечного автомата вы можете иметь одно состояние инициализации для холодного запуска, когда система только что была включена, и второе состояние инициализации для горячего перезапуска.
Ох и 6 тем не очень много!:)
В зависимости от того, что вы подразумеваете под "перезапуском" программы, я вижу несколько простых решений.
Одним из них является встраивание всей вашей программы в некоторый класс "Program", который, по сути, обеспечивает некоторый цикл, в котором есть ваша собственная программа. Когда вам нужно перезапустить программу, вы вызываете статический открытый метод "Restart", который снова запускает цикл.
Вы также можете попытаться сделать системный вызов, который снова запустит вашу программу и завершится. Как предлагается в другом ответе, вы можете создать программу-обертку для этой единственной цели (и проверить код возврата, чтобы узнать, выйти или перезапустить).
Другой простой вариант заключается в использовании goto
, Я знаю, что люди будут ненавидеть меня за то, что я даже упомянул это, но давайте посмотрим правде в глаза: мы хотим сделать простую программу, а не использовать красивый шаблон. Переход назад гарантирует уничтожение, так что вы можете создать программу с меткой в начале и некоторой функцией "Перезапуск", которая просто возвращается в начало.
Какой бы вариант вы ни выбрали, хорошо документируйте его, чтобы другие (или вы в будущем) использовали меньше одного WTF.
PS. Как упомянуто Аленом, goto
не будет уничтожать ни глобальные, ни статические объекты, то же самое будет и для окружающего класса. Поэтому любой подход, который не включает запуск новой программы вместо текущей, должен либо воздерживаться от использования глобальных / статических переменных, либо предпринимать надлежащие действия для их переустановки (хотя это может быть утомительным, как при добавлении каждой статической / глобальной)., вам нужно изменить процедуру перезапуска).
Простой и понятный способ сделать это — добавить провод от неиспользуемого вывода данных к выводу RESET и установить на нем низкий уровень для сброса! :-)