原因を知りたい場合は、次のようなシグナル ハンドラを登録できます。
void handler(int signum, siginfo_t *info, void *context)
{
struct sigaction action = {
.sa_handler = SIG_DFL,
.sa_sigaction = NULL,
.sa_mask = 0,
.sa_flags = 0,
.sa_restorer = NULL
};
fprintf(stderr, "Fault address: %p\n", info->si_addr);
switch (info->si_code) {
case SEGV_MAPERR:
fprintf(stderr, "Address not mapped.\n");
break;
case SEGV_ACCERR:
fprintf(stderr, "Access to this address is not allowed.\n");
break;
default:
fprintf(stderr, "Unknown reason.\n");
break;
}
/* unregister and let the default action occur */
sigaction(SIGSEGV, &action, NULL);
}
そして、それを登録する必要がある場所:
struct sigaction action = {
.sa_handler = NULL,
.sa_sigaction = handler,
.sa_mask = 0,
.sa_flags = SA_SIGINFO,
.sa_restorer = NULL
};
if (sigaction(SIGSEGV, &action, NULL) < 0) {
perror("sigaction");
}
基本的に、SIGSEGV が配信されたときに発火するシグナルを登録し、追加情報を取得して、man ページを引用します。
The following values can be placed in si_code for a SIGSEGV signal:
SEGV_MAPERR address not mapped to object
SEGV_ACCERR invalid permissions for mapped object
これらは、セグ フォールトが発生する 2 つの基本的な理由に対応しています。つまり、アクセスしたページがまったくマップされていないか、そのページに対して試行した操作を実行できませんでした。
ここでは、シグナル ハンドラーが起動した後、それ自体を登録解除し、既定のアクションを置き換えます。これにより、失敗した操作が再度実行されるため、通常のルートでキャッチできます。これは、ページ フォールト (セグ フォールトを取得する前兆) の通常の動作であるため、デマンド ページングなどが機能します。