シグナルハンドラーのキャンセルポイント?
-
26-09-2019 - |
質問
プログラムがシグナル ハンドラーからのキャンセル ポイントである関数を呼び出した場合はどうなりますか?POSIX では、非同期シグナル セーフとキャンセル ポイントの両方として指定されている関数が多数あります。シグナル ハンドラーがそのような関数を呼び出し、キャンセルが行われた場合、結果は、スレッドが非同期キャンセルを有効にしている場合に起こることと非常によく似ています。実際には、すべてのキャンセル クリーンアップ ハンドラー (おそらく非同期シグナルではないため) が使用されるため、実際にはさらに悪いことになります。安全な場合は、シグナルハンドラーコンテキストから呼び出されます。
この場合、POSIX では実際に何を指定しているのでしょうか?また、実装は実際に何を行うのでしょうか?POSIX には、シグナル ハンドラー内のキャンセル ポイントの動作を禁止する言語は見つかりません。また、glibc/nptl ソースにはそのような保護も見つかりません。
解決
POSIX があえてこのトピックについて言及していることさえ知りませんが、徹底的な検索は行っていません。
gcc/nptl システムで簡単に実験してみると、私が疑っていたように、そしてあなたもそうだったと思いますが、NPTL にはそのような保護はなく、シグナル ハンドラー コンテキスト内からキャンセル ハンドラーが実際に呼び出されていることがわかります。
以下のプログラム (ハッキングなどをお詫びします) は次の出力を表示します。
Signal handler called
Sent cancellation
Cleanup called
In sighandler
...次のことを示します:
- シグナルハンドラーが呼び出されました
- 他のスレッドが呼び出されました
pthread_cancel()
- シグナル ハンドラーが完了することなく、キャンセル ハンドラーが呼び出されました。
プログラムは次のとおりです。
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
pthread_t mainthread;
int in_sighandler = 0;
void
cleanup (void *arg)
{
write(1, "Cleanup called\n", strlen("Cleanup called\n"));
if (in_sighandler) {
write(1, "In sighandler\n", strlen("In sighandler\n"));
} else {
write(1, "Not in sighandler\n", strlen("In sighandler\n"));
}
}
void
sighandler (int sig, siginfo_t *siginfo, void *arg)
{
in_sighandler = 1;
write(1,"Signal handler called\n", strlen("Signal handler called\n")); // write() is a CP
usleep(3000000); // usleep() is a CP; not strictly async-signal-safe but happens to be so in Linux
write(1, "Signal handler exit\n", strlen("Signal handler exit\n"));
in_sighandler = 0;
}
void *
thread (void *arg)
{
sleep(1);
pthread_kill(mainthread, SIGUSR1);
usleep(500000);
pthread_cancel(mainthread);
printf("Sent cancellation\n");
return (NULL);
}
int
main (int argc, char **argv)
{
int rc;
struct sigaction sa;
pthread_t threadid;
mainthread = pthread_self();
// Set up a signal handler to test its cancellation properties
sa.sa_sigaction = &sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
rc = sigaction(SIGUSR1, &sa, NULL);
assert(rc == 0);
// Set up a thread to send us signals and cancel us
rc = pthread_create(&threadid, NULL, &thread, NULL);
assert(rc == 0);
// Set up cleanup handlers and loop forever
pthread_cleanup_push(&cleanup, NULL);
while (1) {
sleep(60);
}
pthread_cleanup_pop(0);
return (0);
}
所属していません StackOverflow