确保子进程在Cocoa中已停止
我正在编写一个应用程序,启动一个运行简单web服务器的子进程。我正在使用NSTask,并通过管道与之通信,一切似乎都很好。但是,如果我的程序崩溃,子进程将保持活动状态,下次启动应用程序时,旧子进程和新子进程之间会发生冲突。是否有任何方法可以确保当所属应用程序死亡时子进程死亡?您的应用程序委托可以实现确保子进程在Cocoa中已停止,cocoa,nstask,Cocoa,Nstask,我正在编写一个应用程序,启动一个运行简单web服务器的子进程。我正在使用NSTask,并通过管道与之通信,一切似乎都很好。但是,如果我的程序崩溃,子进程将保持活动状态,下次启动应用程序时,旧子进程和新子进程之间会发生冲突。是否有任何方法可以确保当所属应用程序死亡时子进程死亡?您的应用程序委托可以实现 - (void)applicationWillTerminate:(NSNotification *)aNotification 消息,并在此处终止NSTask。但是,不能保证在崩溃期间调用此委托
- (void)applicationWillTerminate:(NSNotification *)aNotification
消息,并在此处终止NSTask。但是,不能保证在崩溃期间调用此委托
您还可以采取两个额外步骤:
- 在启动新父进程期间关闭现有孤立子进程,方法是在创建时将子进程的PID写入磁盘,并在正常关闭期间将其删除(有时不是最安全的行为)
- 如果NSPipe的端点在特定时间段内(类似心跳)未发送数据,则关闭子流程
我在我的应用程序中尝试了Robert Pointon关于Cocoadev的建议。不过我还没来得及测试它 其思想是将任务的进程组设置为与启动任务的进程的进程组相同(注意:下面的代码基本上是从上面的线程中提取出来的)
以上这些都不起作用……即使是
launchd
,在所有这些糟糕的文档中,复杂性都有办法处理这种常见的场景。我不知道为什么苹果不只是用“母舰批准”的方式来运行后台进程,不管怎样。。我的解决办法是
int masterPID=[[NSProcessInfo processInfo]processIdentifier]传入父进程的PID
代码>等。通过$1、$2等在脚本中阅读这些内容更新:我制作了一个Xcode模板/daemon/project/无论什么都可以。去看看 下面的代码示例应该对您有所帮助 是从,
#包括
#包括
#包括
静态void noteProcDeath(CFFileDescriptorRef fdref、CFOptionFlags回调类型、void*信息){
结构kevent-kev;
int fd=cfFileDescriptorTargetNativeDescriptor(fdref);
kevent(fd,NULL,0,&kev,1,NULL);
//在此处对进程死亡采取行动
printf(“pid为“%u”的进程已死亡\n”,(无符号int)kev.ident);
CFFileDescriptorInvalidate(fdref);
CFRelease(fdref);//CFFileDescriptorRef在本例中不再有用
}
//需要一个参数,一个要监视的整数pid
int main(int argc,char*argv[]){
如果(argc<2)退出(1);
int fd=kqueue();
结构kevent-kev;
EV_集(&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(来源);
//运行运行循环20秒
CFRunLoopRunInMode(kCFRunLoopDefaultMode,20.0,false);
返回0;
}
有一个-[NSConcreteTask setStartsNewProcessGroup:
私有API,如果您传递否
,则创建的NSTask
实例将不会将子进程从当前进程的进程组中分离,并且它将在父进程死亡后立即死亡
xctool的荣誉:您是否尝试过使您的程序不崩溃我在考虑Atlas这里^^你可以尝试在Launchd中运行web服务器。至少这样,当你的应用程序崩溃并重新启动时,Launchd会告诉你服务器已经在运行,这样你就可以关闭它并在需要时重新启动它。
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
}
#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;
}