如何在内核扩展Mac OS X中检测应用程序终止

时间:2014-12-01 18:40:49

标签: macos kernel signals exit

我正在寻找一种方法来检测内核空间中的应用程序退出(例如cmd-q),以便在网络内核扩展中进行处理。

更确切地说: 当一个进程(例如终端ping)保存在IOLockSleep(... THREAD_ABORTSAFE)中时,ctrl-c能够释放锁。 询问proc_issignal(),它会响应sigmask(SIGINT)。

现在我正在寻找一种方法来检测另一个进程退出,例如firefox(菜单栏:应用程序退出(cmd-q))。

以下是我的尝试:

#define FLAG(X) ((dispatch_source_get_data(src) & DISPATCH_PROC_##X) ? #X" " : "")

struct ProcessInfo {
    int pid;
    dispatch_source_t source;
};

// function called back on event
void process_termination_event(struct ProcessInfo* procinfo) {
    dispatch_source_t src = procinfo->source;
    printf("process_termination_event: %d \n", procinfo->pid);
    printf("flags: %s%s\n", FLAG(EXIT), FLAG(SIGNAL));
    dispatch_source_cancel(procinfo->source);
}

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

// Monitor a process by pid, for termination
void MonitorTermination(int pid) {
    struct ProcessInfo* procinfo = (struct ProcessInfo*)malloc(sizeof(struct ProcessInfo));

    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|DISPATCH_PROC_SIGNAL, queue);

    procinfo->pid = pid;
    procinfo->source = dsp;

    dispatch_source_set_event_handler_f(procinfo->source, (dispatch_function_t)process_termination_event);
    dispatch_source_set_cancel_handler_f(procinfo->source, (dispatch_function_t)process_termination_finalize);
    dispatch_set_context(procinfo->source, procinfo);
    dispatch_resume(procinfo->source);
}

int main(int argc, const char * argv[])
{
    for (int i = 0; i < argc; ++i) {
        pid_t pid = atoi(argv[i]);
        printf("MonitorTermination: %d\n", pid);
        fflush(stdout);

        MonitorTermination(pid);
    }

    CFRunLoopRun();
    return 0;
}

如上所述,在cmd-q之后不会调用process_termination_event。即使在强行退出之后。

进程本身保存在网络内核扩展函数中的循环中:

errno_t KEXT::data_out(void *cookie, socket_t so, const struct sockaddr *to, mbuf_t *data, mbuf_t *control, sflt_data_flag_t flags)
{
    // at this point I would like to detect the app quit/termination signal.
    while(PROCESS_IS_NOT_TEMINATING); // <-- pseudo code, actually held with IOLockSleep...
    return 0;
}

我真的很感激任何帮助!提前致谢。

1 个答案:

答案 0 :(得分:1)

这可能不是你一直在思考的方式,但如果你在内核空间,那么我假设你正在编写内核扩展(kext)。通过内核扩展,您可以monitor Vnodes来执行应用程序。您也许可以使用文件范围。

与用户级应用程序(守护程序)一起,kext通知守护程序进程已开始执行,然后使用Grand Central Dispatch函数监视从用户级守护程序终止已启动的应用程序。如果需要,用户应用程序可以通知已终止应用程序的kext。

要监控用户级应用程序的终止,当您收到正在执行的应用程序的通知时,您可以执行以下操作: -

// pid and path provided from vNode scope kext...
void UserLevelApp::MonitorProcessTermination(int pid, const QString &path)
{    
    ProcessInfo* procinfo = new ProcessInfo;

    procinfo->pid = pid;
    procinfo->path = path;
    procinfo->context = this;

    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)process_termination_event);
    dispatch_source_set_cancel_handler_f(dsp,  (dispatch_function_t)process_termination_finalize);

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

    dispatch_resume(dsp);
}

// app terminated call-back function
void UserLevelApp::process_termination_event(struct ProcessInfo* procinfo)
{
    dispatch_source_cancel(procinfo->source);

    // example of how to use the context to call a class function
    procinfo->context->SomeClassFunction(procinfo->pid, procinfo->path);

    qDebug("App Terminated: %d, %s\n", procinfo->pid, procinfo->path.toUtf8().data());
}

// finalize callback function
void UserLevelApp::process_termination_finalize(struct ProcessInfo* procinfo)
{
   dispatch_release(procinfo->source);
   delete procinfo;
}

因此,每个启动的应用程序(由kext通知)都有一个与之关联的事件处理程序,当应用程序终止时,您将在已注册的函数中回调 process_termination_event process_termination_finalize

虽然此方法需要使用带有kext的关联用户级守护程序应用程序,但从安全性和稳定性的角度来看,这并不是一件坏事。