检测OSX上的进程退出

时间:2014-07-11 03:39:28

标签: c macos

我希望在进程退出OSX之前得到通知,以便我可以在进程终止之前收集有关进程的统计信息。 (示例细节:我想聚合具有许多子进程的进程的CPU使用率,这些进程快速生成并死掉,但是使用大量的CPU。当通过诸如proc_pidinfo之类的东西对CPU使用率进行采样时,生成并死亡的进程在我的汇总统计数据中没有充分捕获与我的采样率相似的费率。我希望在进程死亡时收到通知,这样我就可以总结用户和系统的总时间。

到目前为止,看起来效果最好的方法是使用libdtrace,但我无法弄清楚如何使用{{1}设置流程退出的探测器}命令,更不用说从C程序设置dtrace了。任何有关设置此类dtrace探针的提示以及有关如何使用libdtrace的教程都将非常感激。

修改

好的,按照一些评论者的建议,我设法创建了一个使用libdtrace并可靠地触发进程的程序。出口。这很好,但不幸的是我似乎无法获得正在退出的过程的PID。似乎在我的D程序中调用libdtrace并尝试格式化整数被严重破坏。具体来说,如果我运行this program,它应该在它退出时打印出进程的名称和PID,它就会失败。它打印一些其他整数,实际上无论我尝试输出什么,都会打印整数。如果我从

更改D程序
printf()

只是

syscall::*exit*:entry {
    printf(\"%s %d\\n\", execname, curpsinfo->pr_pid);
};

它只打印出神秘整数的前三位数字。请注意,流程名称是正确的,它只是整数 - >我相信,失败的字符串转换。使用上面的D程序运行主syscall::*exit*:entry { printf(\"%d\\n\", 100); }; 程序可以正常工作,但是我想将它集成到我已经编写过很多的C程序中,并且将子命令输出汇总到该程序中并不是真的我希望前进的方式。

有关如何使dtrace的缓冲输出正常工作,或者将PID作为整数而不是字符串获取的帮助会很棒。

2 个答案:

答案 0 :(得分:3)

有关此主题的Apple技术说明。

TN2050 Observing Process Lifetimes Without Polling

为了监控任意流程,本指南建议kqueues

我们可以通过[unable to detect application running with another user (via switch user)或[Programmatically check if a process is running on Mac这种方式获取正在运行的流程的PID。

清单8使用kqueues监控特定流程

static pid_t gTargetPID = -1;
    // We assume that some other code sets up gTargetPID.

- (IBAction)testNoteExit:(id)sender
{
    FILE *                  f;
    int                     kq;
    struct kevent           changes;
    CFFileDescriptorContext context = { 0, self, NULL, NULL, NULL };
    CFRunLoopSourceRef      rls;

    // Create the kqueue and set it up to watch for SIGCHLD. Use the 
    // new-in-10.5 EV_RECEIPT flag to ensure that we get what we expect.

    kq = kqueue();

    EV_SET(&changes, gTargetPID, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, NULL);
    (void) kevent(kq, &changes, 1, &changes, 1, NULL);

    // Wrap the kqueue in a CFFileDescriptor (new in Mac OS X 10.5!). Then 
    // create a run-loop source from the CFFileDescriptor and add that to the 
    // runloop.

    noteExitKQueueRef = CFFileDescriptorCreate(NULL, kq, true, NoteExitKQueueCallback, &context);
    rls = CFFileDescriptorCreateRunLoopSource(NULL, noteExitKQueueRef, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
    CFRelease(rls);

    CFFileDescriptorEnableCallBacks(noteExitKQueueRef, kCFFileDescriptorReadCallBack);

    // Execution continues in NoteExitKQueueCallback, below.
}

static void NoteExitKQueueCallback(
    CFFileDescriptorRef f, 
    CFOptionFlags       callBackTypes, 
    void *              info
)
{
    struct kevent   event;

    (void) kevent( CFFileDescriptorGetNativeDescriptor(f), NULL, 0, &event, 1, NULL);

    NSLog(@"terminated %d", (int) (pid_t) event.ident);

    // You've been notified!
}

答案 1 :(得分:0)

The following example takes a UNIX process ID as argument, and watches up to 20 seconds, and reports if the process terminates in that time

// 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;
}

对于那些几乎不了解C的人:
版本:cc test.c -framework CoreFoundation -O
运行:./a.out 57168
57168是正在监视的进程的pid。杀死它进行测试!

当然,您可以延长20秒以使其持续尽可能长的时间。