我正在编写一个启动运行简单Web服务器的子进程的应用程序。我正在使用NSTask并使用管道与它进行通信,而且一切似乎都或多或少。但是,如果我的程序崩溃,子进程将保持活动状态,下次启动应用程序时,旧的子进程与新进程之间会发生冲突。有没有办法确保当拥有的应用程序死亡时子进程死亡?
答案 0 :(得分:2)
上述所有工作都没有......甚至launchd
甚至都没有记录 - 复杂性有一种处理这种常见情况的方法。我不知道为什么Apple不只是制作“母舰认可”的方式来运行后台进程,但无论如何......我的解决方案是......
通过NSTask启动shell脚本并将所需的任何变量传递给它。 也可以通过int masterPID = [[NSProcessInfo processInfo] processIdentifier];
等传递您的父进程'PID 等。通过$ 1,$ 2等在您的脚本中阅读这些内容。
反过来,从脚本中的启动子进程 ..
在脚本中监控子进程 AND 您的父进程。
这有双重目的......它可以让你“关注孩子们......”,并且在父母(或可怕的车祸)的悲惨事件中 - 杀死僵尸孤儿。然后,你自己扣动扳机(你是shell脚本),你的流程表将是干净的.. 好像你从未存在过。没有阻塞端口,重新启动时没有冲突,没有应用商店拒绝。 Lemme知道这有用吗!
更新:我制作了一个Xcode模板/守护进程/项目/无论做什么。看看.. mralexgray / Infanticide.
答案 1 :(得分:1)
您的申请代表可以实施
- (void)applicationWillTerminate:(NSNotification *)aNotification
消息,并在那里终止NSTask。但是,无法保证在崩溃期间,将调用此委托。
您可以采取以下两个步骤:
答案 2 :(得分:1)
以下代码示例可以为您提供帮助。
它来自here,
#include <CoreFoundation/CoreFoundation.h>
#include <unistd.h>
#include <sys/event.h>
static void noteProcDeath(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info) {
struct kevent kev;
int fd = CFFileDescriptorGetNativeDescriptor(fdref);
kevent(fd, NULL, 0, &kev, 1, NULL);
// take action on death of process here
printf("process with pid '%u' died\n", (unsigned int)kev.ident);
CFFileDescriptorInvalidate(fdref);
CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example
}
// one argument, an integer pid to watch, required
int main(int argc, char *argv[]) {
if (argc < 2) exit(1);
int fd = kqueue();
struct kevent kev;
EV_SET(&kev, atoi(argv[1]), EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
kevent(fd, &kev, 1, NULL, 0, NULL);
CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
CFRelease(source);
// run the run loop for 20 seconds
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 20.0, false);
return 0;
}
答案 3 :(得分:0)
更新:现在我去检查一下它不起作用。尝试设置进程组失败,出现此错误;
EPERM“请求进程的有效用户ID与调用者的有效用户ID不同,并且进程不是调用进程的后代。”
在这个问题上有一个更近期的主题,但据我所知,没有简单的解决方案
http://www.omnigroup.com/mailman/archive/macosx-dev/2009-March/062164.html
我在我的应用程序中尝试过Robert Pointon对Cocoadev的建议。我还没有去测试它。
http://www.cocoadev.com/index.pl?NSTaskTermination
我们的想法是将任务的进程组设置为与启动任务的进程相同(注意:下面的代码基本上是从上面的线程中解除的)。
pid_t group = setsid();
if (group == -1) {
group = getpgrp();
}
[task launch];
if (setpgid([task processIdentifier], group) == -1) {
NSLog(@"unable to put task into same group as self");
[task terminate];
} else {
// handle running task
}
答案 4 :(得分:0)
有-[NSConcreteTask setStartsNewProcessGroup:]
私有API,如果您传递NO
,则创建的NSTask
实例不会将子进程与当前进程的进程组分离,并且它将在不久后死亡父进程死亡。