[gcc]gccでC99とPOSIXを同時に使いたかったのだ

2016年12月19日月曜日

C gcc linux シグナル マルチスレッド

[Java][C]JavaのvolatileとCのvolatileでシグナルハンドラを作るときの注意を書いたけど、今どきのマルチスレッドプログラムとはシグナルハンドラを定義するやり方では相性が悪い。
というわけで、そんなときはsigwaitを使うのがいいみたい。

ところが、以下のようなサンプルプログラムを作って、コンパイルしようとすると、エラーメッセージがいっぱい出てきてしまった。

sigtest.c
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    struct timespec timeout = {
        .tv_sec = 0,
        .tv_nsec = 4 * 1000 * 1000,
    };

    sigset_t sigset;
    sigemptyset(&sigset); 
    sigaddset(&sigset, SIGUSR1);

    errno = 0;
    if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) {
        perror("sigprocmask");
        return 1;
    }

    int signum;
    errno = 0;
    while ((signum = sigtimedwait(&sigset, NULL, &timeout)) <= 0) {
        if (errno != EAGAIN) {
            perror("sigprocmask");
            break;
        }
        errno = 0;
    }

    printf("%d\n", signum);

    return 0;
}
$ gcc -Wall -std=c99 -o sigtest sigtest.c
sigtest.c: In function ‘main’:
sigtest.c:7:12: error: variable ‘timeout’ has initializer but incomplete type
     struct timespec timeout = {
            ^
sigtest.c:8:9: error: unknown field ‘tv_sec’ specified in initializer
         .tv_sec = 0,
         ^
sigtest.c:8:9: warning: excess elements in struct initializer [enabled by default]
sigtest.c:8:9: warning: (near initialization for ‘timeout’) [enabled by default]
sigtest.c:9:9: error: unknown field ‘tv_nsec’ specified in initializer
         .tv_nsec = 4 * 1000 * 1000,
         ^
sigtest.c:9:9: warning: excess elements in struct initializer [enabled by default]
sigtest.c:9:9: warning: (near initialization for ‘timeout’) [enabled by default]
sigtest.c:7:21: error: storage size of ‘timeout’ isn’t known
     struct timespec timeout = {
                     ^
sigtest.c:12:5: error: unknown type name ‘sigset_t’
     sigset_t sigset;
     ^
sigtest.c:13:5: warning: implicit declaration of function ‘sigemptyset’ [-Wimplicit-function-declaration]
     sigemptyset(&sigset); 
     ^
sigtest.c:14:5: warning: implicit declaration of function ‘sigaddset’ [-Wimplicit-function-declaration]
     sigaddset(&sigset, SIGUSR1);
     ^
sigtest.c:17:5: warning: implicit declaration of function ‘sigprocmask’ [-Wimplicit-function-declaration]
     if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) {
     ^
sigtest.c:17:21: error: ‘SIG_BLOCK’ undeclared (first use in this function)
     if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) {
                     ^
sigtest.c:17:21: note: each undeclared identifier is reported only once for each function it appears in
sigtest.c:24:5: warning: implicit declaration of function ‘sigtimedwait’ [-Wimplicit-function-declaration]
     while ((signum = sigtimedwait(&sigset, NULL, &timeout)) <= 0) {
     ^
sigtest.c:7:21: warning: unused variable ‘timeout’ [-Wunused-variable]
     struct timespec timeout = {

うーむ、シグナル関係の関数や構造体定義が見えていないようだ。
ちょっと調べてわかったのは -std=c99 の指定が関わっているということ。

-stdオプションを指定していない時のgcc 4.xデフォルトは gnu89 になるようだ。

確かに -std=gnu99 だと、コンパイルがうまく行く。

$ gcc -Wall -std=gnu99 -o sigtest sigtest.c

つまり、 -std=c99 を指定するとC99規格以外のPOSIX準拠の定義は見えなくなってしまう。
でも、 -std=gnu99だと、gnu拡張も許してしまうんじゃないかなぁ。

結局、gnu99を使わずにC99とPOSIXを両方使うには_POSIX_C_SOURCEマクロを定義してあげる必要があった。
sigtimedwaitとかを使うには_POSIX_C_SOURCEの値を199309L以上にする必要がある。
sigwaitinfo(2)

$ gcc -Wall -D_POSIX_C_SOURCE=199309L -std=c99 -o sigtest sigtest.c