从命令行启动的正在运行的C或C ++程序的最佳方法是将自身置于后台,相当于用户使用'&'从unix shell启动时在命令的最后? (但用户没有。)这是一个GUI应用程序,不需要任何shell I / O,因此没有理由在启动后绑定shell。但是我想要一个shell命令启动,在没有'&'的情况下自动后台运行(或在Windows上)。
理想情况下,我想要一个适用于Linux,OS X和Windows的解决方案。 (或者我可以用#ifdef选择单独的解决方案。)可以假设这应该在执行开始时完成,而不是在中间的某个地方。
一种解决方案是让主程序成为启动真实二进制文件的脚本,并将其小心地放入后台。但是需要这些耦合的shell /二进制对似乎并不令人满意。
另一个解决方案是立即启动另一个执行版本(带有'system'或CreateProcess),使用相同的命令行参数,但将子项放在后台然后让父出口。但与将本身置于背景中的过程相比,这似乎很笨拙。
在几个答案后编辑:是的,一个fork()(或系统()或Windows上的CreateProcess)是一种解决这个问题的方法,我在原始问题中暗示过。但是所有这些解决方案都使得SECOND流程成为背景,然后终止原始流程。我想知道是否有办法将现有流程放入后台。一个区别是,如果应用程序是从记录其进程ID的脚本启动的(可能是为了以后的查杀或其他目的),新分叉或创建的进程将具有不同的ID,因此任何启动脚本都无法控制,如果你看到我得到了什么。
编辑#2 :
fork()不是OS X的好解决方案,其中'fork'的手册页说如果使用某些框架或库则不安全。我试过了,我的应用程序在运行时大声抱怨:“进程已分叉,你不能安全地使用这个CoreFoundation功能。你必须执行()。”
我对daemon()感兴趣,但是当我在OS X上尝试它时,它给出了相同的错误消息,所以我认为它只是fork()的一个花哨的包装器,并且具有相同的限制。
请原谅OS X中心主义,它恰好是我面前的系统。但我确实在寻找所有三个平台的解决方案。
答案 0 :(得分:10)
我的建议:不要这样做,至少不在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调用 - 除非它们明确请求访问命令窗口。
答案 1 :(得分:9)
在Linux上,daemon()是您正在寻找的,如果我理解正确的话。
答案 2 :(得分:7)
它通常在类Unix操作系统上完成的方式是在开始时fork()并从父节点退出。这在Windows上不起作用,但比启动另一个存在分叉的过程要优雅得多。
答案 3 :(得分:5)
需要做三件事,
fork
setsid
redirect STDIN, STDOUT and STDERR to /dev/null
这适用于POSIX系统(你提到的所有声称都是POSIX(但Windows在声明位停止))
答案 4 :(得分:4)
在UNIX上,你需要连续两次分叉并让父亲死掉。
答案 5 :(得分:4)
进程无法将自己置于后台,因为它不是负责背景与前景的人。那将是shell,它正在等待进程退出。如果您启动带有&符“&”的流程最后,shell会不等待进程退出。
但进程可以逃脱shell的唯一方法是分叉另一个子进程,然后让它自己的原始自退出到等待的shell。
在shell中,您可以使用Control-Z对进程进行后台处理,然后键入“bg”。
答案 6 :(得分:3)
后台进程是shell函数,而不是OS函数。
如果您希望应用程序在后台启动,典型的技巧是编写一个shell脚本来启动它,并在后台启动它。
#! /bin/sh
/path/to/myGuiApplication &
答案 7 :(得分:2)
你编辑了你的问题,但是你可能仍然错过了你的问题是一种语法错误的观点 - 如果这个过程没有放在后台开始,你希望PID保持不变,你不能忽视这样一个事实,即启动进程的程序正在等待那个PID,这几乎就是在前台的定义。
我认为你需要考虑为什么你想要在背景中放置一些东西并保持PID相同。我建议你可能不需要这两种限制。
答案 8 :(得分:2)
跟进您编辑过的问题:
我想知道是否有办法将现有流程放到后台。
在类Unix操作系统中,我知道没有办法做到这一点。 shell被阻止,因为它正在执行wait()调用的一个变体,等待子进程退出。子进程没有办法继续运行,但不知何故导致shell的wait()以“请停止关注我”状态返回。你有子fork并退出原来的原因是shell将从wait()返回。
答案 9 :(得分:2)
以下是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应用程序,并且它在没有控制台的情况下自动运行。如果你想作为一项服务运行,你必须要查看它,因为我从来没有写过它,我真的不知道它们是如何工作的。
答案 10 :(得分:1)
正如其他人提到的,fork()是如何在* nix上完成的。您可以使用MingW或Cygwin库在Windows上获取fork()。但那些将要求您切换到使用GCC作为编译器。
在纯Windows世界中,您使用CreateProcess(或其衍生产品之一CreateProcessAsUser,CreateProcessWithLogonW)。
答案 11 :(得分:0)
我正在尝试解决方案。
父进程只需要一个fork。
最重要的一点是,在fork之后,父进程必须通过调用_exit(0);
而不是通过调用exit(0);
来消亡
使用_exit(0);
时,命令提示符会立即在shell上返回。
这就是诀窍。
答案 12 :(得分:0)
在Unix中,我学会了使用fork()
来做到这一点。
如果您想将正在运行的流程放入后台,fork
两次。
答案 13 :(得分: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> &) */
答案 14 :(得分:0)
所以,正如你所说的,只是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]不一定是完整路径,因此需要修改它。
答案 15 :(得分:0)
在Windows下,我认为你将获得fork()的关闭是将程序作为Windows服务加载。我想。
以下是有关Windows服务的简介文章的链接... CodeProject: Simple Windows Service Sample
答案 16 :(得分:0)
最简单的背景形式是:
if (fork() != 0) exit(0);
在Unix中,如果你想要完全解除与tty的关联,你会这样做:
0
,1
和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 */
这应该完全符合您的计划。
答案 17 :(得分:0)
如果你需要一个脚本来获得程序的PID,你仍然可以在fork之后得到它。
fork时,将子进程的PID保存在父进程中。退出父进程时,将PID输出到STD{OUT,ERR}
或在return pid;
末尾只有main()
语句。然后,调用脚本可以获取程序的pid,尽管它需要了解程序的工作方式。
答案 18 :(得分:0)
我不确定Windows,但在类UNIX系统上,您可以fork()
然后setsid()
分叉进程将其移动到未连接到终端的新进程组。
答案 19 :(得分:0)
在Linux下执行此操作的最常见方法是通过forking。同样适用于Mac,对于Windows我不是100%肯定,但我相信他们有类似的东西。
基本上发生的是进程将自身分成两个进程,然后原始进程退出(将控制权返回给shell或其他任何进程),第二个进程继续在后台运行。