从命令行启动的运行C或C ++程序的最佳方式是如何将自己置于后台,等同于用户是否在命令末尾使用'&'从unix shell启动? (但用户没有。)这是一个GUI应用程序,不需要任何shellI / O,所以没有理由在发布后绑定shell。 但是我想要一个shell命令启动时自动后台没有'&'(或在Windows上)。
理想的情况下,我想要一个适用于任何Linux,OS X和Windows的解决scheme。 (或者我可以用#ifdefselect单独的解决scheme。)可以假定这应该在执行开始时正确地执行,而不是在中间的某个地方执行。
一个解决scheme是让主程序成为一个启动真实二进制文件的脚本,仔细地把它放到后台。 但是,需要这些耦合的shell/二进制对似乎并不令人满意。
另一个解决scheme是立即启动另一个执行的版本(使用“system”或CreateProcess),使用相同的命令行参数,但将subprocess放在后台,然后让父进程退出。 但是这个过程与背景相比似乎很笨重。
在几个答案后编辑 :是的,在Windows上的fork()(或system()或CreateProcess)是这样做的一种方法,我暗示在我原来的问题。 但是,所有这些解决scheme都会创build一个后台进程,然后终止原始进程。 我想知道是否有办法把EXISTING进程放到后台。 一个区别是,如果应用程序是从logging其进程ID的脚本启动的(可能是为了以后的查杀或其他目的),则新分叉或创build的进程将具有不同的id,因此不会由任何启动脚本控制,如果你看到我在做什么。
编辑#2 :
fork()对于OS X来说并不是一个好的解决scheme,其中'fork'的手册页说如果使用某些框架或库,它是不安全的。 我试了一下,我的应用程序在运行时大声抱怨:“进程已经分叉了,你不能安全地使用这个CoreFoundationfunction,你必须执行exec()。
我被daemon()所吸引,但是当我在OS X上尝试它时,它给出了相同的错误信息,所以我认为它只是fork()的一个奇妙的包装,并且具有相同的限制。
请原谅OS X的中心主义,这恰好是我眼前的系统。 但是我的确在为这三个平台寻找解决scheme。
在Linux上,如果我正确理解你, daemon()就是你要找的东西。
我的建议是: 不要这样做 ,至少不要在Linux / UNIX下。
Linux / UNIX下的GUI程序通常不会自动进行自我背景化。 虽然这可能偶尔会对新手感到烦恼,但它有许多优点:
在核心转储/需要调试的其他问题的情况下,可以轻松捕获标准错误。
使shell脚本可以轻松运行程序并等待完成。
使shell脚本能够轻松地在后台运行程序并获取其进程ID:
gui-program & pid=$! # do something with $pid later, such as check if the program is still running
如果你的程序自行分叉,这个行为就会中断。
“Scriptability”在很多意想不到的情况下都是有用的,即使是GUI程序,我也会毫不犹豫地明确地打破这些行为。
Windows是另一回事。 AFAIK,Windows程序在后台自动运行 – 即使从命令shell调用 – 除非它们明确请求访问命令窗口。
通常在类Unix操作系统上完成的方式是在开始处fork()并从父节点退出。 这在Windows上不起作用,但比启动另一个分叉存在的程序要优雅得多。
有三件事情需要做,
fork setsid redirect STDIN, STDOUT and STDERR to /dev/null
这适用于POSIX系统(所有你提到的声称是POSIX的(但是Windows停止在声明位))
在UNIX上,您需要连续分叉两次并让父母死亡。
一个过程不能把自己置身于背景之中,因为它不是背景与前景的负责人。 这将是等待流程退出的shell。 如果在最后启动带“&”符号的进程,则shell不会等待进程退出。
但是这个过程逃脱外壳的唯一方法就是把另一个孩子分叉,然后让原来的自己退回到等待的外壳。
从shell中,可以使用Control-Z背景进程,然后键入“bg”。
后台进程是一个shell函数,而不是一个OS函数。
如果你想要一个应用程序在后台启动,那么典型的技巧就是编写一个shell脚本来启动它,并在后台启动它。
#! /bin/sh /path/to/myGuiApplication &
要对你编辑的问题进行跟进:
我想知道是否有办法把EXISTING进程放到后台。
在类似Unix的操作系统中,我确实没有办法做到这一点。 由于正在执行wait()调用的变体之一,所以外壳被阻塞,等待子进程退出。 孩子进程没有办法继续运行,但不知何故导致shell的wait()返回一个“请不要看我”的状态。 你有孩子分叉和退出原来的原因是这样外壳将从wait()返回。
这里是一些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中,您只需使用WinMain()而不是main()来将您的程序构建为Win32应用程序,并且无需控制台即可自动运行。 如果你想作为一项服务来运行,那么你就不得不去看那个,因为我从来没有写过一个,我真的不知道它们是如何工作的。
你编辑了你的问题,但是你可能仍然错过了你的问题是一个类型的语法错误 – 如果这个过程没有放在后台开始,并且你希望PID保持不变,你可以“不要忽视这样一个事实:开始这个过程的程序正在等待这个PID,这几乎就是在前台的定义 。
我认为你需要考虑为什么你要把东西放在后台,并保持PID相同。 我建议你可能不需要这两个约束。
正如其他人提到的,fork()是如何在* nix上完成的。 您可以使用MingW或Cygwin库在Windows上获取fork()。 但是那些将需要你切换到使用GCC作为你的编译器。
在纯粹的Windows世界中,您将使用CreateProcess (或其衍生物之一CreateProcessAsUser,CreateProcessWithLogonW)。
在Linux下最常见的做法是通过分叉 。 在Mac上也是如此,对于Windows我不是100%确定,但我相信他们有类似的东西。
基本上会发生什么是进程分裂成两个进程,然后原来的退出(返回控制到壳或任何),第二个进程继续在后台运行。
我不确定Windows,但是在类UNIX系统上,您可以fork()
然后setsid()
分叉进程将其移动到未连接到终端的新进程组中。
在Windows下,你将要到达fork()的最后一件事就是将你的程序加载到Windows服务中,我想。
这里是一个链接到Windows服务的介绍文章… CodeProject:简单的Windows服务示例
所以,正如你所说,只是叉()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; }
循环是检查一个 – 守护进程的参数,如果它存在,请在重新执行之前将其删除,以避免无限循环。
我不认为这将工作,如果二进制放入路径,因为argv [0]不一定是一个完整的路径,所以它将需要修改。
/**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);
当_exit(0);
被使用时,命令提示立即返回到shell。
这是诀窍。
最简单的背景形式是:
if (fork() != 0) exit(0);
在Unix中,如果你想从tty完全解除关联,你可以这样做:
0
和2
)。 if (fork() != 0) exit(0);
setpgroup(0,getpid()); /* Might be necessary to prevent a SIGHUP on shell exit. */
signal(SIGHUP,SIG_IGN); /* just in case, same as using nohup to launch program. */
fd=open("/dev/tty",O_RDWR);
ioctl(fd,TIOCNOTTY,0); /* Disassociates from the terminal */
close(fd);
if (fork() != 0) exit(0); /* just for good measure */
这应该完全守护你的程序。
如果你需要一个脚本来获得程序的PID,你仍然可以在fork之后得到它。
当您分叉时,将子进程的PID保存在父进程中。 当你退出父进程时,要么输出PID到STD{OUT,ERR}
要么只是return pid;
一个return pid;
语句在main()
的结尾。 然后调用脚本可以获得程序的pid,尽管它需要对程序的工作有一定的了解。