我覺得你的問題的關鍵是,你的plist中有這樣的:
<key>ExitTimeOut</key>
<integer>0</integer>
爲launchd.plist手冊頁說:
ExitTimeOut <整數>
啓動時間等待發送SIGTERM信號和發送SIGKILL信號之間,當作業停止時 。默認值是系統定義的。零值爲 解釋爲無窮大,不應使用,因爲它可以永久停止系統 關閉 。
實驗一下,看起來這個文本是不準確。根據經驗,我觀察到,如果該值設置爲0
,我會得到您所描述的行爲(在收到TERM
之後立即編輯該過程的地址爲KILL
,而不管任何未完成的已聲明事務)。如果將此值更改爲某個任意更大例如60的數字,我觀察到我的TERM
處理程序被調用,並有機會在退出之前進行清理。
由於您發佈的鏈接描述了您是否使用了經典的信號處理或GCD,這並不完全清楚,但是如果您使用的是經典的UNIX信號處理,那麼我還應該提及您已經調用了函數您的信號處理程序不在可以在信號處理程序中調用的函數列表中(dprintf
和usleep
不在列表中)。但是您似乎更可能使用GCD。
發生到我的另一件事是,如果你使用vproc_transaction_begin/end
到支架你在你的處理器等力所能及的工作項目,那麼你會得到這種行爲「免費」,而無需在信號處理程序在所有。完全可以想象,無論正常工作項目如何,都需要進行一些集中式清理工作,但如果這只是等待其他異步任務完成,則可能更簡單。
不管怎樣,萬一有幫助,這是我用來測試這種情況下的代碼:
#import <Foundation/Foundation.h>
#import <vproc.h>
static void SignalHandler(int sigraised);
static void FakeWork();
static void Log(NSString* str);
int64_t outstandingTransactions;
dispatch_source_t fakeWorkGeneratorTimer;
int main(int argc, const char * argv[])
{
@autoreleasepool
{
// Set up GCD handler for SIGTERM
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(source, ^{
SignalHandler(SIGTERM);
});
dispatch_resume(source);
// Tell the standard signal handling mechanism to ignore SIGTERM
struct sigaction action = { 0 };
action.sa_handler = SIG_IGN;
sigaction(SIGTERM, &action, NULL);
// Set up a 10Hz timer to generate "fake work" events
fakeWorkGeneratorTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(fakeWorkGeneratorTimer, DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC, 0.05 * NSEC_PER_SEC);
dispatch_source_set_event_handler(fakeWorkGeneratorTimer, ^{
// Dont add an event *every* time...
if (arc4random_uniform(10) >= 5) dispatch_async(dispatch_get_global_queue(0, 0), ^{ FakeWork(); });
});
dispatch_resume(fakeWorkGeneratorTimer);
// Start the run loop
while (1)
{
// The runloop also listens for SIGTERM and will return from here, so I'm just sending it right back in.
[[NSRunLoop currentRunLoop] run];
}
}
return 0;
}
static void SignalHandler(int sigraised)
{
// Open a transaction so that we dont get killed before getting to the end of this handler
vproc_transaction_t transaction = vproc_transaction_begin(NULL);
// Turn off the fake work generator
dispatch_suspend(fakeWorkGeneratorTimer);
Log([NSString stringWithFormat: @"%s(): signal received = %d\n", __func__, sigraised]);
int64_t transCount = outstandingTransactions;
while (transCount > 0)
{
Log([NSString stringWithFormat: @"%s(): %lld transactions outstanding. Waiting...\n", __func__, transCount]);
usleep(USEC_PER_SEC/4);
transCount = outstandingTransactions;
}
Log([NSString stringWithFormat: @"%s(): EXITING\n", __func__]);
// Close the transaction
vproc_transaction_end(NULL, transaction);
exit(0);
}
static void FakeWork()
{
static int64_t workUnitNumber;
const NSTimeInterval minWorkDuration = 1.0/100.0; // 10ms
const NSTimeInterval maxWorkDuration = 4.0; // 4s
OSAtomicIncrement64Barrier(&outstandingTransactions);
int64_t serialNum = OSAtomicIncrement64Barrier(&workUnitNumber);
vproc_transaction_t transaction = vproc_transaction_begin(NULL);
Log([NSString stringWithFormat: @"Starting work unit: %@", @(serialNum)]);
// Set up a callback some random time later.
int64_t taskDuration = arc4random_uniform(NSEC_PER_SEC * (maxWorkDuration - minWorkDuration)) + (minWorkDuration * NSEC_PER_SEC);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, taskDuration), dispatch_get_global_queue(0, 0), ^{
Log([NSString stringWithFormat: @"Finishing work unit: %@", @(serialNum)]);
vproc_transaction_end(NULL, transaction);
OSAtomicDecrement64Barrier(&outstandingTransactions);
});
}
static void Log(NSString* str)
{
static NSObject* lockObj = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lockObj = [NSObject new];
});
@synchronized(lockObj)
{
int fd = open("/tmp/myTestD.log", O_WRONLY | O_CREAT | O_APPEND, 0777);
if (fd <= 0) return;
dprintf(fd, "%s\n", str.UTF8String);
close(fd);
}
}
而且plist中:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>DaemonDeathTest</string>
<key>ProgramArguments</key>
<array>
<string>/tmp/bin/DaemonDeathTest</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ExitTimeOut</key>
<integer>60</integer>
<key>EnableTransactions</key>
<true/>
</dict>
</plist>
感謝您的快速響應ipmcc: – 2014-09-23 11:56:29
對不起,我以前不必要的評論,但我被拖走了會議,而寫了更多的行... 無論如何: *我不使用標準的POSIX信號處理,因爲我知道一個是限制在一小組可重入的libc函數。 *關於ExitTimeOut設置爲0:至少launchd似乎認識到無限ExitTimeOut,因爲它在system.log中表示(「process has infinite exit time ...」) - >但是,我會盡快檢查您的建議我回來工作了。 – 2014-09-24 05:20:54
是的。很顯然,ExitTimeOut設置爲0意味着無限,但實際上,至少在退出由'launchctl unload'觸發時(這是我觸發launchd來終止進程的方式),看起來行爲是不同的。來源在這裏:http://www.opensource.apple.com/source/launchd/launchd-442.26.2/src/core.c那裏沒有一個**吸菸槍,但它看起來像那裏有些情況下,零的'exit_timeout'沒有被特別防範,所以這不是不可想象的。如果是我,我只是不會使用'ExitTimeOut = 0'並繼續生活。 – ipmcc 2014-09-24 14:36:36