Как программа на C / C++ может переключиться на задний план?

Как лучше всего запустить работающую программу C или C++, запущенную из командной строки, в фоновый режим, что эквивалентно тому, как если бы пользователь запускал из оболочки unix с '&' в конце команды? (Но пользователь этого не сделал.) Это приложение с графическим интерфейсом и не требует ввода-вывода оболочки, поэтому нет причин связывать оболочку после запуска. Но я хочу, чтобы запуск команды оболочки выполнялся автоматически без символа «&» (или в Windows).

В идеале мне нужно решение, которое будет работать на любой ОС Linux, OS X и Windows. (Или отдельные решения, которые я могу выбрать с помощью #ifdef.) Можно предположить, что это нужно делать прямо в начале выполнения, а не где-то посередине.

Одно из решений - сделать так, чтобы основная программа была скриптом, который запускает настоящий двоичный файл, аккуратно помещая его в фоновый режим. Но необходимость в этих связанных парах оболочка / двоичная оболочка кажется неудовлетворительной.

Другое решение - немедленно запустить исполняемую версию еще один (с 'system' или CreateProcess) с теми же аргументами командной строки, но поместить дочерний элемент в фоновый режим и затем иметь родительский выход. Но это кажется неуклюжим по сравнению с процессом, переводящим сам в фоновый режим.

Отредактировано после нескольких ответов: Да, fork () (или system (), или CreateProcess в Windows) - это один из способов сделать это, на что я намекнул в своем первоначальном вопросе. Но все эти решения создают ВТОРОЙ процесс, который является фоновым, а затем завершают исходный процесс. Мне было интересно, есть ли способ поместить СУЩЕСТВУЮЩИЙ процесс в фоновый режим. Одно отличие состоит в том, что если приложение было запущено из сценария, который записал его идентификатор процесса (возможно, для более позднего уничтожения или для другой цели), вновь созданный или созданный процесс будет иметь другой идентификатор и поэтому не будет управляться никаким запускающим сценарием, если вы видите, к чему я клоню.

Редактировать # 2:

fork () не является хорошим решением для OS X, где на странице руководства для «fork» говорится, что это небезопасно при использовании определенных фреймворков или библиотек. Я попробовал это, и мое приложение громко жалуется во время выполнения: «Процесс разветвился, и вы не можете безопасно использовать эту функциональность CoreFoundation. Вы ДОЛЖНЫ exec ()».

Меня заинтриговал daemon (), но когда я попробовал его на OS X, он выдал то же сообщение об ошибке, поэтому я предполагаю, что это просто причудливая оболочка для fork () и имеет те же ограничения.

Простите за центризм OS X, просто в данный момент передо мной стоит система. Но я действительно ищу решение для всех трех платформ.

Почему бы не задать еще один вопрос о проблеме с OS X? fork () / exec () - это способ C / C++ сделать то, что задал ваш исходный вопрос.

Jon Ericson 23.09.2008 02:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
14
1
18 812
20

Ответы 20

Обычно в Unix-подобных ОС это делается с помощью fork () в начале и выхода из родительского. Это не будет работать в Windows, но намного элегантнее, чем запуск другого процесса, в котором существует разветвление.

Наиболее распространенный способ сделать это в Linux - через разветвление. То же самое должно работать на Mac, что касается Windows, я не уверен на 100%, но я считаю, что у них есть что-то похожее.

В основном происходит то, что процесс разделяется на два процесса, а затем исходный завершается (возвращая управление оболочке или чему-то еще), а второй процесс продолжает работать в фоновом режиме.

Как уже упоминалось, fork () - это то, как это сделать на * nix. Вы можете получить fork () в Windows с помощью библиотек MingW или Cygwin. Но для этого вам потребуется переключиться на использование GCC в качестве компилятора.

В чистом мире Windows вы должны использовать CreateProcess (или одну из его производных CreateProcessAsUser, CreateProcessWithLogonW).

В UNIX вам нужно дважды подряд выполнить форк и позволить родителю умереть.

Это законный способ "перейти в фоновый режим", хотя и не обычный (т.е. нет необходимости в двойном форковании при выполнении setsid и т. д.); так что кто бы ни проголосовал против, я считаю, что они не должны. Это не «канонично», но полезно.

tzot 23.09.2008 04:11

Для двойной fork () есть веская причина, но вы должны сделать хотя бы setid () между ними. Это гарантирует, что демонизированный процесс не является лидером группы процессов, а также не имеет управляющего терминала.

Jonathan Leffler 18.10.2008 22:50

Я не уверен насчет Windows, но в UNIX-подобных системах вы можете fork(), а затем setsid() - разветвленный процесс, чтобы переместить его в новую группу процессов, не подключенную к терминалу.

Три вещи нужно сделать,

fork
setsid
redirect STDIN, STDOUT and STDERR to /dev/null

Это относится к системам POSIX (все те, которые вы упомянули, утверждают, что они POSIX (но Windows останавливается на бите требования))

Я ожидал, что это работает и для приложений POSIX в Windows. Но вы должны понимать, что никто не пишет приложения POSIX в Windows (т.е. это не проблема ОС).

MSalters 25.09.2008 15:25

Если вам нужен скрипт с PID программы, вы все равно можете получить его после форка.

При форке сохраните PID дочернего процесса в родительском процессе. Когда вы выходите из родительского процесса, либо выведите PID в STD{OUT,ERR}, либо просто добавьте оператор return pid; в конец main(). Затем вызывающий скрипт может получить идентификатор программы, хотя для этого требуются определенные знания о том, как работает программа.

Самый простой вид фона:

if (fork() != 0) exit(0);

В Unix, если вы хотите полностью отделиться от tty в фоновом режиме, вы должны:

  1. Закройте все дескрипторы, которые могут обращаться к tty (обычно 0, 1 и 2).
  2. if (fork() != 0) exit(0);
  3. setpgroup(0,getpid()); /* Might be necessary to prevent a SIGHUP on shell exit. */
  4. signal(SIGHUP,SIG_IGN); /* just in case, same as using nohup to launch program. */
  5. fd=open("/dev/tty",O_RDWR);
  6. ioctl(fd,TIOCNOTTY,0); /* Disassociates from the terminal */
  7. close(fd);
  8. if (fork() != 0) exit(0); /* just for good measure */

Это должно полностью демонизировать вашу программу.

Чтобы ответить на ваш отредактированный вопрос:

I was wondering if there was a way to put the EXISTING process into the background.

В Unix-подобных ОС я действительно не знаю способа сделать это. Оболочка заблокирована, потому что она выполняет один из вариантов вызова wait (), ожидая завершения дочернего процесса. Дочерний процесс не может оставаться запущенным, но каким-то образом заставляет оболочку wait () возвращаться со статусом «пожалуйста, перестань следить за мной». Причина, по которой у вас есть дочерняя вилка и выход из оригинала, заключается в том, что оболочка вернется из wait ().

Вы можете поместить слушателя для SIGHUP, но не в интерактивном режиме

dsm 23.09.2008 00:18

Ctrl-Z и команда "bg"? Я никогда не пытался понять, как это работает, но у меня это всегда срабатывало.

MSalters 25.09.2008 15:24

Вот какой-то псевдокод для Linux / UNIX:

initialization_code()
if (failure) exit(1)
if ( fork() > 0 ) exit(0)
setsid()
setup_signal_handlers()
for(fd=0; fd<NOFILE; fd++) close(fd)
open("/dev/null", O_RDONLY)
open("/dev/null", O_WRONLY)
open("/dev/null", o_WRONLY)
chdir("/")

И поздравляю, ваша программа продолжает работать как независимый «демонизированный» процесс без управляющего TTY и без какого-либо стандартного ввода или вывода.

Теперь в Windows вы просто создаете свою программу как приложение Win32 с WinMain () вместо main (), и она автоматически запускается без консоли. Если вы хотите работать в качестве службы, вам придется поискать это, потому что я никогда не писал ни одной и не знаю, как они работают.

Действительно плохой код, извините. Вы предполагаете, что а) вам разрешено закрывать stdin / stdout / stderr, b) что 0 = stdin, 1 = stdout | stderr, 2 = stdout | stderr и c) что open () снова заполнит дескрипторы файлов с 0 на .

Thorsten79 23.09.2008 00:00

Нет, это очень стандартный код демонизации. Вам всегда разрешено закрывать 0,1,2 в Unix'ish. Функция open всегда возвращает первый доступный fd, поэтому, если 0,1,2 закрыты, следующее открытие будет 0,1,2. Если вы настаиваете, вы можете использовать dup2 (), чтобы установить для fds значение 0,1,2, но в этом нет необходимости.

Zan Lynx 23.09.2008 02:08

@ Thorsten79: вы, очевидно, не занимались программированием POSIX, извините. Возможно, вы захотите найти способ получить доступ к оболочке в системе POSIXish и попробовать «man 2 open». Как правило, первый абзац раздела ОПИСАНИЕ вводит вас в заблуждение.

tzot 23.09.2008 03:47

POSIX гарантирует (a) и (b), но не (c). Однако из-за такого кода почти все системы на практике подчиняются (c).

wnoise 23.09.2008 04:31

@wnoise: «Дескриптор файла, возвращенный успешным вызовом, будет дескриптором файла с наименьшим номером, который в данный момент не открыт для процесса». в соответствии с POSIX.1-2001. Дескрипторы файлов 0, 1 и 2 только что были закрыты, никакой другой поток не запущен, следовательно…

tzot 23.09.2008 12:34

Процесс не может перейти в фоновый режим, потому что он не отвечает за задний план или передний план. Это будет оболочка, ожидающая выхода из процесса. Если вы запускаете процесс с амперсандом «&» в конце, то оболочка нет ожидает завершения процесса.

Но единственный способ, которым процесс может выйти из оболочки, - это отключить другого потомка, а затем позволить своему исходному «я» вернуться в ожидающую оболочку.

Из оболочки вы можете фоновый процесс с помощью Control-Z, затем введите «bg».

Да, я знал об этом. Суть вопроса заключалась в том, могла ли сама программа делать что-то, что было бы эквивалентно нажатию пользователем Ctrl-Z и последующему запуску «bg».

Larry Gritz 22.09.2008 23:27

Ответили в первом абзаце, и ответ - «Нет».

Zan Lynx 23.09.2008 02:09

Процесс всегда может послать себе сигнал SUSP, но это только половина того, что спросил Ларри;)

tzot 23.09.2008 04:08

Фон процесса - это функция оболочки, а не функция ОС.

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

#! /bin/sh
/path/to/myGuiApplication &

Это не отвечает на исходный вопрос, а именно: как фонить ваш собственный процесс, если он НЕ БЫЛ запущен с '&', и вам не нужен отдельный сценарий оболочки для этого.

Larry Gritz 22.09.2008 23:26

Я думаю, что под Windows последнее, что вы получите от fork (), - это загрузка вашей программы как службы Windows.

Вот ссылка на вводную статью о службах Windows ... CodeProject: простой пример службы Windows

В Linux демон () - это то, что вы ищете, если я правильно вас понял.

Так что очень близко! Выглядит отлично для Linux, но в OS X кажется, что он просто оборачивает fork () и выдает то же ужасное сообщение об ошибке во время выполнения о том, что вилка небезопасна при использовании определенных библиотек фреймворка.

Larry Gritz 23.09.2008 02:10

Итак, как вы говорите, просто fork () ing не поможет. Что вы должны сделать, это fork (), а затем повторно выполнить exec (), как это делает этот пример кода:

#include stdio.h>
#include <unistd.h>
#include <string.h>

#include <CoreFoundation/CoreFoundation.h>

int main(int argc, char **argv)
{
    int i, j;

    for (i=1; i<argc; i++)
        if (strcmp(argv[i], "--daemon") == 0)
        {
            for (j = i+1; j<argc; j++)
                argv[j-1] = argv[j];

            argv[argc - 1] = NULL;

            if (fork()) return 0;

            execv(argv[0], argv);

            return 0;
        }


    sleep(1);

    CFRunLoopRun();

    CFStringRef hello = CFSTR("Hello, world!");

    printf("str: %s\n", CFStringGetCStringPtr(hello, CFStringGetFastestEncoding(hello)));

    return 0;
}

Цикл предназначен для проверки аргумента --daemon, и, если он присутствует, удалите его перед повторным запуском, чтобы избежать бесконечного цикла.

Я не думаю, что это сработает, если двоичный файл будет помещен в путь, потому что argv [0] не обязательно является полным путем, поэтому его нужно будет изменить.

Мой совет: не делай этого, по крайней мере, не под Linux / UNIX.

Программы с графическим пользовательским интерфейсом под Linux / UNIX традиционно сами выполняют авто-фон нет. Хотя иногда это может раздражать новичков, у этого есть ряд преимуществ:

  • Упрощает регистрацию стандартной ошибки в случае дампов ядра / других проблем, требующих отладки.

  • Упрощает сценарий оболочки для запуска программы и ожидания ее завершения.

  • Упрощает сценарий оболочки для запуска программы в фоновом режиме и получения идентификатора процесса:

    gui-program &
    pid=$!
    # do something with $pid later, such as check if the program is still running
    

    Если ваша программа разветвляется, это поведение нарушится.

«Возможность сценариев» полезна во многих неожиданных обстоятельствах, даже с программами с графическим интерфейсом пользователя, что я не решился бы явно нарушить такое поведение.

Окна - это отдельная история. AFAIK, программы Windows автоматически запускаются в фоновом режиме - даже при вызове из командной оболочки - если они явно не запрашивают доступ к командному окну.

Программы Windows консоль не работают в фоновом режиме. Программы Windows Графический интерфейс не имеют представления о том, что они находятся в фоновом или фоновом режиме относительно консоли. Это просто окна в Z-порядке.

MSalters 25.09.2008 15:23

В большинстве случаев, когда вы запускаете что-то, вывод вас не интересует, и необходимость отсоединять его самостоятельно раздражает, когда программа может просто делать это сама и иметь флаг --foreground, если вам это нужно.

remmy 19.04.2014 14:07

Вы отредактировали свой вопрос, но вам все еще может не хватать того факта, что ваш вопрос является своего рода синтаксической ошибкой - если процесс не был переведен в фоновый режим для начала, и вы хотите, чтобы PID остался прежним, вы можете ' Игнорировать тот факт, что программа, запустившая процесс, ожидает этого PID, и это в значительной степени определение нахождения на переднем плане.

Я думаю, вам нужно подумать, почему вы хотите поместить что-то в фоновый режим и сохранить PID одинаковым. Я предполагаю, что вам, вероятно, не нужны оба этих ограничения.

/**Deamonize*/

pid_t pid;
pid = fork(); /**father makes a little deamon(son)*/
if (pid>0)
exit(0); /**father dies*/
while(1){
printf("Hello I'm your little deamon %d\n",pid); /**The child deamon goes on*/
sleep(1)
}

/** try 'nohup' in linux(usage: nohup <command> &) */

В Unix я научился делать это с помощью fork(). Если вы хотите перевести запущенный процесс в фоновый режим, fork дважды.

Я пробовал решение.

От родительского процесса требуется только одна вилка.

Самым важным моментом является то, что после fork родительский процесс должен умереть, вызвав _exit(0);, а НЕ вызвав exit(0);.

При использовании _exit(0); командная строка немедленно возвращается в оболочку.

Это уловка.

Другие вопросы по теме