进程退出时收到通知
Get notified when a process quits
我已经开发了一个用Qt(C++)和Objective-C编写的OSX守护进程应用程序。我使用内核扩展来监视其他应用程序和进程何时启动,但需要知道它们何时终止。
是否有任何方法可以接收其他进程终止的通知,而不必不断轮询目标进程的pid或mach任务?
您可以使用kqueue/kevent来实现这一点。我黑掉了一个控制台应用程序,然后对它进行了一点重构,这样就可以清楚地看到发生了什么,并添加了一个助手函数来更容易地调用它。只是几乎没有经过测试,但希望它能给你一条前进的路。。。
哦,是的,请注意,这段代码假设主运行循环正在应用程序中运行。。。它将从运行循环中调用块。。。简单到可以用另一个运行循环替换它。。。或者,如果您没有使用任何CF运行循环,则必须将kq文件描述符添加到您正在使用的任何通知机制中。
编辑
修复了重新启用回调的错误,因为每次触发后都必须重新启用文件描述符回调。此外,make args采用多个PID来演示监视多个PID。
当然,您可以很容易地使用invoke委托方法,而不是使用块,但这并不是真正的重点。。。
啊。。。。修复资源泄漏。。。我可能无法修复更多。。。因为这是一个被黑客入侵的例子,但每次我回去读它时,我都会发现一些错误。。。也许我就不读了:-)
// main.c
#include <CoreFoundation/CoreFoundation.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
static void
kqueueCallbackOnExit(CFFileDescriptorRef fileDescriptor,
CFOptionFlags flags,
void *info)
{
int fd = CFFileDescriptorGetNativeDescriptor(fileDescriptor);
struct kevent event;
if (kevent(fd, NULL, 0, &event, 1, NULL) == 1 && event.udata) {
void (^cb)(pid_t) = event.udata;
cb((pid_t)event.ident);
Block_release(cb);
}
CFFileDescriptorEnableCallBacks(
fileDescriptor, kCFFileDescriptorReadCallBack);
}
static int
createOnProcessExitQueue()
{
int kq = kqueue();
if (kq < 0) return -1;
CFFileDescriptorContext context = {
.version = 0,
.info = NULL,
.retain = NULL,
.release = NULL,
.copyDescription = NULL
};
CFFileDescriptorRef kqFileDescriptor = CFFileDescriptorCreate(
NULL, kq, true, kqueueCallbackOnExit, &context);
if (kqFileDescriptor == NULL) {
close(kq);
kq = -1;
return -1;
}
CFRunLoopSourceRef runLoopSource = CFFileDescriptorCreateRunLoopSource(
NULL, kqFileDescriptor, 0);
CFRunLoopAddSource(CFRunLoopGetMain(),
runLoopSource, kCFRunLoopDefaultMode);
CFRelease(runLoopSource);
CFFileDescriptorEnableCallBacks(
kqFileDescriptor, kCFFileDescriptorReadCallBack);
CFRelease(kqFileDescriptor);
return kq;
}
static int
onProcessExit(pid_t pid, void (^callback)(pid_t pid))
{
static int kq;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
kq = createOnProcessExitQueue();
});
void (^cb)(pid_t) = Block_copy(callback);
struct kevent event = {
.ident = pid,
.filter = EVFILT_PROC,
.flags = EV_ADD | EV_ONESHOT,
.fflags = NOTE_EXIT,
.data = 0,
.udata = (void*)cb
};
if (kevent(kq, &event, 1, NULL, 0, NULL) != 1) {
Block_release(cb);
return -1;
}
return 0;
}
int main(int argc, const char * argv[])
{
for (int i = 0; i < argc; ++i) {
pid_t pid = atoi(argv[i]);
printf("watching pid: %dn", pid);
fflush(stdout);
onProcessExit(pid, ^(pid_t pid) {
printf("process %d just diedn", (int)pid);
fflush(stdout);
});
}
CFRunLoopRun();
return 0;
}
感谢@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);
}
以下示例以UNIX进程ID为参数,最多监视20秒,并报告进程是否在该时间终止
// 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' diedn", (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;
}
对于那些几乎不知道C的人:
内部版本:cc test.c -framework CoreFoundation -O
运行:./a.out 57168
57168是被监控进程的pid。杀死它来测试
当然,你可以增加20秒,让它持续多久就持续多久。
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- 创建LinkedList退出,返回代码为-11(SIGSEGV)
- 当我在main中声明了我的2d数组时,为什么我的程序会退出
- 如何让LLDB在成功时退出,在失败时等待
- C++控制台应用程序阻止退出
- 程序在执行程序的其余部分之前退出
- 构造函数在退出函数时无法初始化一个参数
- 为什么异常不退出程序?
- 我不断收到 [错误] ID 返回 1 退出状态错误,但看不到问题所在
- 退出简单while循环时出现问题
- 使用vscode调试时,GDB意外退出
- 函数如何通知用户它基于函数原型抛出异常?
- 如何在ECS框架中更新组件数据和通知系统
- 当 I2C 值在C++中发生变化时收到通知
- 如何设计具有不同类型的通知和观察器的观察者模式?
- 如何运行进程,然后在它退出时收到通知
- 当子线程退出时,如何通知父线程
- 线程退出时通知Waiters
- 通知线程退出
- 进程退出时收到通知