在可可macOS应用程序中陷阱SIGINT [英] Trap SIGINT in Cocoa macos application

查看:113
本文介绍了在可可macOS应用程序中陷阱SIGINT的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为MacOS制作的UI应用程序捕获SIGINT.在应用程序委托类中,我看到以下方法:

I am attempting to trap a SIGINT for a UI application made for MacOS. In the app delegate class, I see the following method:

func applicationWillTerminate(_ aNotification: Notification) {

}

但是, Ctrl + C SIGINT永远不会陷入这里.在互联网上阅读后发现,不能保证执行此功能,特别是在应用程序在后台运行的情况下.

However, a Ctrl + C, SIGINT, never gets caught in here. Reading around the internet, has shown that this function is not guaranteed to execute, especially if the app goes in the background.

我可以在应用程序委托中做什么以捕获SIGINT?还是我有其他地方可以捕捉到中断,以便我可以适当地关闭资源?

What can I do in the app delegate to catch a SIGINT? Or is there an alternate place I am to catch the interrupt so that I can close the resources appropriately?

推荐答案

查尔斯的答案是正确的,但他的警告(确保仅从处理程序中调用可重入函数")是一个极端的限制.可以使用kqueueCFFileDescriptor将信号处理重定向到更安全的环境.

Charles's answer is correct, but his caveat ("Be sure only to call reentrant functions from within the handler") is an extreme limitation. It's possible to redirect handling of a signal to a safer environment using kqueue and CFFileDescriptor.

技术说明TN2050:无需轮询即可观察过程寿命是在不同的主题上,但说明了该技术.苹果在那里以这种方式描述了查尔斯的警告:

Technical Note TN2050: Observing Process Lifetimes Without Polling is on a different subject but illustrates the technique. There, Apple describes Charles's caveat this way:

侦听信号可能很棘手,因为执行过程古怪 与信号处理程序关联的环境.具体来说,如果您 安装信号处理程序(使用信号

Listening for a signal can be tricky because of the wacky execution environment associated with signal handlers. Specifically, if you install a signal handler (using signal or sigaction), you must be very careful about what you do in that handler. Very few functions are safe to call from a signal handler. For example, it is not safe to allocate memory using malloc!

可从信号处理程序中安全使用的功能(异步信号 安全功能)列在用户手册页上.

The functions that are safe from a signal handler (the async-signal safe functions) are listed on the sigaction man page.

在大多数情况下,您必须采取额外的步骤来重定向传入的信号 到更明智的环境.

In most cases you must take extra steps to redirect incoming signals to a more sensible environment.

我从那里获取了代码说明,并对其进行了修改以处理SIGINT.抱歉,是Objective-C.这是一次性设置代码:

I've taken the code illustration from there and modified it for handling SIGINT. Sorry, it's Objective-C. Here's the one-time setup code:

// Ignore SIGINT so it doesn't terminate the process.

signal(SIGINT, SIG_IGN);

// Create the kqueue and set it up to watch for SIGINT. Use the 
// EV_RECEIPT flag to ensure that we get what we expect.

int kq = kqueue();

struct kevent changes;
EV_SET(&changes, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, NULL);
(void) kevent(kq, &changes, 1, &changes, 1, NULL);

// Wrap the kqueue in a CFFileDescriptor. Then create a run-loop source
// from the CFFileDescriptor and add that to the runloop.

CFFileDescriptorContext context = { 0, self, NULL, NULL, NULL };
CFFileDescriptorRef kqRef = CFFileDescriptorCreate(NULL, kq, true, sigint_handler, &context);
CFRunLoopSourceRef rls = CFFileDescriptorCreateRunLoopSource(NULL, kqRef, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);

CFFileDescriptorEnableCallBacks(kqRef, kCFFileDescriptorReadCallBack);
CFRelease(kqRef);

这是实现上面引用的sigint_handler回调的方法:

And here's how you would implement the sigint_handler callback referenced above:

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

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

    // You've been notified!
}

请注意,此技术要求您在有兴趣处理SIGINT(也许是应用程序的生命周期)并维护/运行其运行循环的线程上运行安装代码.系统为自己的目的而创建的线程(例如服务于Grand Central Dispatch队列的线程)适合于此目的.

Note that this technique requires that you run the setup code on a thread which will live as long as you're interested in handling SIGINT (perhaps the app's lifetime) and service/run its run loop. Threads created by the system for its own purposes, such as those which service Grand Central Dispatch queues, are not suitable for this purpose.

该应用程序的主线程将起作用,您可以使用它. 但是,如果主线程锁定或变得无响应,则它不在为它的运行循环提供服务,并且不会调用SIGINT处理程序.由于SIGINT通常用于完全中断这种卡住的进程,因此主线程可能不合适.

The main thread of the app will work and you can use it. However, if the main thread locks up or become unresponsive, then it's not servicing its run loop and the SIGINT handler won't be called. Since SIGINT is often used to interrupt exactly such a stuck process, the main thread may not be suitable.

因此,您可能想产生一个自己的线程只是为了监视此信号.它不应该做任何其他事情,因为其他任何事情也可能导致它卡住.即使到那里,也存在问题.您的处理程序函数将在您的该后台线程上被调用,并且主线程可能仍被锁定.在系统库中,有很多东西仅是主线程的,您将无能为力.但是,与POSIX样式的信号处理程序相比,您将拥有更大的灵活性.

So, you may want to spawn a thread of your own just to monitor this signal. It should do nothing else, because anything else might cause it to get stuck, too. Even there, though, there are issues. Your handler function will be called on this background thread of yours and the main thread may still be locked up. There's a lot of stuff that is main-thread-only in the system libraries and you won't be able to do any of that. But you'll have much greater flexibility than in a POSIX-style signal handler.

我应该补充一点,GCD的调度源还可以监视UNIX信号,并且更易于使用,尤其是从Swift中.但是,它们不会预先创建运行该处理程序的专用线程.该处理程序将被提交到队列中.现在,您可以指定一个高优先级/高QOS队列,但是我不确定如果该进程具有多个运行中的失控线程,则该处理程序将运行.也就是说,实际上在高优先级队列上运行的任务将优先于低优先级线程或队列,但是启动新任务可能不会.我不确定.

I should add that GCD's dispatch sources can also monitor UNIX signals and are easier to work with, especially from Swift. However, they won't have a dedicated thread pre-created to run the handler. The handler will be submitted to a queue. Now, you may designate a high-priority/high-QOS queue, but I'm not entirely certain that the handler will get run if the process has numerous runaway threads already running. That is, a task that's actually running on a high-priority queue will take precedence over lower-priority threads or queues, but starting a new task might not. I'm not sure.

这篇关于在可可macOS应用程序中陷阱SIGINT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆