C++ 当进程退出时收到通知

C++ 当进程退出时收到通知,c++,objective-c,macos,qt,process,C++,Objective C,Macos,Qt,Process,我开发了一个OSX守护程序应用程序,用Qt(C++)和Objective-C编写。我使用内核扩展来监视其他应用程序和进程何时启动,但需要知道它们何时终止 是否有任何方法可以在不必不断轮询目标进程的pid或mach任务的情况下接收其他进程终止的通知?您可以使用kqueue/kevent执行此操作。我对一个控制台应用程序进行了黑客攻击,然后对其进行了一点重构,使其有点明显,并添加了一个帮助函数以更轻松地调用它。只是勉强测试过,但希望它能给你一条前进的道路 哦,是的,请注意,此代码假定主运行循环正在应

我开发了一个OSX守护程序应用程序,用Qt(C++)和Objective-C编写。我使用内核扩展来监视其他应用程序和进程何时启动,但需要知道它们何时终止


是否有任何方法可以在不必不断轮询目标进程的pid或mach任务的情况下接收其他进程终止的通知?

您可以使用kqueue/kevent执行此操作。我对一个控制台应用程序进行了黑客攻击,然后对其进行了一点重构,使其有点明显,并添加了一个帮助函数以更轻松地调用它。只是勉强测试过,但希望它能给你一条前进的道路

哦,是的,请注意,此代码假定主运行循环正在应用程序中运行。。。它将从运行循环中调用块。。。简单到可以用另一个运行循环替换它。。。或者,如果没有使用任何CF运行循环,则必须将kq文件描述符添加到所使用的任何通知机制中

编辑

修复了重新启用回调的错误,因为每次触发后必须重新启用文件描述符回调。此外,使参数采用多个PID来演示监视多个PID

当然,您可以轻松地使用invoke a delegate方法而不是使用块,但这并不是真正的重点

啊。。。。修复资源泄漏。。。我可能再也修不好了。。。因为这是一个黑客的例子,但每次我回去读它,我发现一些错误。。。也许我会停止读它:-)

//main.c
#包括
#包括
#包括
#包括
静态空隙
kqueueCallbackOnExit(cfFileDescriptorRefFileDescriptor,
CFOptionFlags标志,
作废*信息)
{
int fd=cfFileDescriptorTargetNativeDescriptor(fileDescriptor);
结构事件;
if(kevent(fd,NULL,0,&event,1,NULL)==1&&event.udata){
无效(^cb)(pid_t)=event.udata;
cb((pid_t)事件标识);
块释放(cb);
}
CFFileDescriptorEnableCallBacks(
fileDescriptor,kCFFileDescriptorReadCallBack);
}
静态整数
createOnProcessExitQueue()
{
int kq=kqueue();
如果(kq<0)返回-1;
CFFileDescriptorContext上下文={
.version=0,
.info=NULL,
.retain=NULL,
.release=NULL,
.copyDescription=NULL
};
CFFileDescriptorRef kqFileDescriptor=CFFileDescriptorCreate(
NULL、kq、true、kqueueCallbackOnExit和context);
if(kqFileDescriptor==NULL){
关闭(kq);
kq=-1;
返回-1;
}
CFRunLoopSourceRef runLoopSource=CFFileDescriptorCreateRunLoopSource(
NULL,kqFileDescriptor,0);
CFRunLoopAddSource(CFRunLoopGetMain(),
runLoopSource,kCFRunLoopDefaultMode);
CFRelease(runLoopSource);
CFFileDescriptorEnableCallBacks(
kqFileDescriptor、kCFFileDescriptorReadCallBack);
CFRelease(kqFileDescriptor);
返回kq;
}
静态整数
onProcessExit(pid\u t pid,void(^callback)(pid\u t pid))
{
静态int-kq;
静态调度一次;
一次发送(一次发送)^{
kq=createOnProcessExitQueue();
});
无效(^cb)(pid t)=块拷贝(回调);
结构kevent事件={
.ident=pid,
.filter=EVFILT_PROC,
.flags=EV|u ADD | EV|u ONESHOT,
.fflags=注释\u退出,
.数据=0,
.udata=(void*)cb
};
if(kevent(kq,&event,1,NULL,0,NULL)!=1){
块释放(cb);
返回-1;
}
返回0;
}
int main(int argc,const char*argv[]
{
对于(int i=0;i
多亏了@JodyHagins,我对kqueue和kevent所做的研究让我了解了如何使用GCD监控文件和数据。以此为模板,我得出了以下结论:-

struct ProcessInfo
{
    int pid;
    dispatch_source_t source;
};

// function called back on event
void pid_event(struct ProcessInfo* procinfo)
{
    printf("****** Application exited: %d ******\n", procinfo->pid);
    dispatch_source_cancel(procinfo->source);
}

// function called back when the dispatch source is cancelled
void pid_finalize(struct ProcessInfo* procinfo)
{
    dispatch_release(procinfo->source);
    printf(">>>> Finished with %d <<<<\n", procinfo->pid);
    delete procinfo;
}

// Monitor a process by pid, for termination
void DispatchMonitorProcess(int pid, ProcessInfo* procinfo)
{
    procinfo->pid = pid;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t dsp = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, queue);

    dispatch_source_set_event_handler_f(dsp, (dispatch_function_t)pid_event);
    dispatch_source_set_cancel_handler_f(dsp,  (dispatch_function_t)pid_finalize);

    procinfo->source = dsp;
    dispatch_set_context(dsp, procinfo);

    dispatch_resume(dsp);
}

// Monitors the termination of a process with the given pid
void MonitorTermination(int pid)
{           
   DispatchMonitorProcess(pid, new ProcessInfo);
}
struct ProcessInfo
{
int-pid;
调度来源;
};
//事件时回调函数
无效pid_事件(结构ProcessInfo*procinfo)
{
printf(“******应用程序退出:%d******\n”,procinfo->pid);
调度\来源\取消(procinfo->source);
}
//取消分派源时回调函数
无效pid_finalize(结构进程信息*进程信息)
{
调度发布(procinfo->source);
printf(“>>>>以%d结束

//cc test.c-framework CoreFoundation-O
#包括
#包括
#包括
静态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;
}
对于那些几乎不懂C的人:
构建:
cc test.c-framework CoreFoundation-O

运行:
/a.out 57168

57168是正在监视的进程的pid。终止它以进行测试!

当然,你可以增加20秒,使其持续时间与y一样长
struct ProcessInfo
{
    int pid;
    dispatch_source_t source;
};

// function called back on event
void pid_event(struct ProcessInfo* procinfo)
{
    printf("****** Application exited: %d ******\n", procinfo->pid);
    dispatch_source_cancel(procinfo->source);
}

// function called back when the dispatch source is cancelled
void pid_finalize(struct ProcessInfo* procinfo)
{
    dispatch_release(procinfo->source);
    printf(">>>> Finished with %d <<<<\n", procinfo->pid);
    delete procinfo;
}

// Monitor a process by pid, for termination
void DispatchMonitorProcess(int pid, ProcessInfo* procinfo)
{
    procinfo->pid = pid;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t dsp = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, queue);

    dispatch_source_set_event_handler_f(dsp, (dispatch_function_t)pid_event);
    dispatch_source_set_cancel_handler_f(dsp,  (dispatch_function_t)pid_finalize);

    procinfo->source = dsp;
    dispatch_set_context(dsp, procinfo);

    dispatch_resume(dsp);
}

// Monitors the termination of a process with the given pid
void MonitorTermination(int pid)
{           
   DispatchMonitorProcess(pid, new ProcessInfo);
}
// cc test.c -framework CoreFoundation -O
#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;
}