Binary 2.0カンファレンス 2006 で田中さんが発表していた「getcontextの怪」、最初はフーン( ´_ゝ`)と思っていたのだけれど、よくよく考えてみると結構深い話である。
まず、田中さんのサンプルプログラム、よくみると、getcontext()している関数中でローカル変数がvolatileされていない。
んで、さらに困ったことにsetjmp()はC99(JISだとJIS X 3010)の規格で
「ただし対応するsetjmpマクロの呼び出しを含む関数中の、volatile修飾型でない自動記憶域期間をもつオブジェクトの値が、setjmp呼び出しとlongjmp呼び出しの間に変更された場合に不定となる事をのぞく」
と但し書きがあるので、こういうコードは書けない。
※ JISCのサイトで規格が無料で読めるので興味のある人はドゾ
スキャナで取り込んだと思しき、全ページ画像のクソPDFなので、検索できなくて超使いにくいけどね(苦笑)
んで、困ったことにSingle UNIX Spesification(SUS)のgetcontextの定義ではこのvolatile云々の免罪事項がないらしいんだわ。
※2 Linuxの人は man 3p getcontext で規格定義が読めるらしいよ。便利な世の中になったね♪
でも、この volatile 云々ってのはThe rationale for the C99 standard に書かれているように、そもそも変数がレジスタに乗るかどうかコンパイラしか知らない状況ではライブラリに出来る事なんかねーよハゲ。って事なので、書いてないからvolatileなくても動くように作れよ。と言われてもなかなかツライものがあるのである。
たぶん、唯一の解決策は、setjmp, getcontext呼び出しを含む関数はすべてのローカル変数をvolatileであるかのように扱う。ってやつでgccがやっている対策もコレ(のはず、よく知らないけど)
でも、やっぱりオイラは規格を直すべきだとオモタですよ

定義不能! ランキング!
まず、田中さんのサンプルプログラム、よくみると、getcontext()している関数中でローカル変数がvolatileされていない。
んで、さらに困ったことにsetjmp()はC99(JISだとJIS X 3010)の規格で
「ただし対応するsetjmpマクロの呼び出しを含む関数中の、volatile修飾型でない自動記憶域期間をもつオブジェクトの値が、setjmp呼び出しとlongjmp呼び出しの間に変更された場合に不定となる事をのぞく」
と但し書きがあるので、こういうコードは書けない。
※ JISCのサイトで規格が無料で読めるので興味のある人はドゾ
スキャナで取り込んだと思しき、全ページ画像のクソPDFなので、検索できなくて超使いにくいけどね(苦笑)
んで、困ったことにSingle UNIX Spesification(SUS)のgetcontextの定義ではこのvolatile云々の免罪事項がないらしいんだわ。
※2 Linuxの人は man 3p getcontext で規格定義が読めるらしいよ。便利な世の中になったね♪
でも、この volatile 云々ってのはThe rationale for the C99 standard に書かれているように、そもそも変数がレジスタに乗るかどうかコンパイラしか知らない状況ではライブラリに出来る事なんかねーよハゲ。って事なので、書いてないからvolatileなくても動くように作れよ。と言われてもなかなかツライものがあるのである。
たぶん、唯一の解決策は、setjmp, getcontext呼び出しを含む関数はすべてのローカル変数をvolatileであるかのように扱う。ってやつでgccがやっている対策もコレ(のはず、よく知らないけど)
でも、やっぱりオイラは規格を直すべきだとオモタですよ

定義不能! ランキング!
コメント
コメント一覧 (12)
getcontext は遊びでちょっと使ったことあるだけなのですが
NetBSDとLinuxしか見てないのですが両方man は (2) しかなくて
libcにも ヘッダファイルに プロトタイプがあるだけ システムコールかと思っていたのですがそうでもないのですか?
でも、もしシステムコールだとしたら環境はなにもかも全部保存しないとダメで、カーネルのバグのような気もして。。。
結論としては話がよく分かっていないのですが、なにか勘違いしていますか?
getcontextは(3)が正しくて、libcで実装されてます
たぶん、アセンブラでかかれているので、タグジャンプとかだと出てこないと思いますが全ファイルgrepとかすると探せるはず。
ちなみにglibc だと
glibc/sysdep/unix/sysv/linux/i386/getcontext.S
とかになります。
getcontextなんて所詮setjmp に毛がはえたようなモノなんだからカーネル実装はちょっと考えにくい
ありました。
調べたのは、ちょっと前なのでタグジャンプしかしてなかったかは覚えてないのですがそれっぽいことやってました。
でも、僕が見たNetBSDは確かにシステムコールだったと思うんですが、今環境なくてバージョンも分からないので分わかりません。
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/arch/i386/sys/getcontext.S
見ると普通にアセンブラですね。
これでなんで保存できるのがよく分からないですが。あと、シグナルマスク保存してないことも謎です。
でもこっちはシステムコールっぽいです。
http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/i386/sys/getcontext.S
もしかすると歴史的ななにかなのかもしれない気もします。
システムコールにしたい理由で思いつくことは、環境保存とシグナルマスクの保存をアトミックにしたいとかそういう事かなぁ
これが問題になるケースも思いつかないなぁ
何度もすみません。
この実装の差異は気になるなぁ・・・
_SYSCALL マクロは シンボル定義だけかと思ってたのですが、int もしていました。
単に、Linux vs BSD というか流儀の問題なのかもしれません。でも man が (2)だったり(3)だったりするのはひっかかります。
あと、あまり良く見てはないですが前 uClibcも見ていてこっちはプロトタイプだけで実装がないみたいでした。
glibc に無いと思っていたのはこれと話しがごちゃごちゃになってたのかもしれません。
以前に書いたブログ記事、もったいなくありませんか?
これまで書いた膨大な記事を眠らせていませんか?
「レビューda!ドットコム-Reviewda.com-」がオープンしました。
古今東西あらゆるものの評判やクチコミを集めるサイトです。
従来のブログランキングなどとは異なり、
同ブログ内の記事でも異なる記事であれば、
URLを「クチコミ検索」というコーナーに複数登録可能です。
不特定多数の方が投稿された記事を絞り込み検索して読まれますので、
ご自身の記事一つ一つがブログのアクセスアップにつながります。
この機会に是非ご利用ください。
http://www.reviewda.com/
レビューda!ドットコム-Reviewda.com-
uClibcはなぁ・・・
アレは一般人が使わない関数とか、普通の人が使わない使い方は捨ててるんだと思う。
イマドキは組み込みでもglibc使ってるぐらいなのでオイラは見なかったことにしてる
volatile でないローカル変数 ret は setjmp から戻ってきた後に初期化している
(というか、setjmp 以前には使っていない) のですが、
規格上、それでも volatile にしないといけないんでしょうか?
コメントありがとうございます。
規格関係は解釈論争にならないように規格本体をあたられるほうがよいと思うのですがどうでしょうか。
こういうネタ画像メインのblogのコメント欄で終わらせるにはもったいない
しかし、その不定な値を持つ変数を代入で初期化して使うことがいけないようには思えません。
なにか見逃してますか?
論点がだいぶと明確になったように思います。
ありがとうございます。
つまり不定の範囲が
・変更された変数の値だけが不定
・すべてが不定。なにが起きても不思議ではない
のどちらなのか。
ということですかね。
オイラは後者だと思っていたのですが、田中さんのご意見もごもっとも。
規格の英語版もちゃきちゃき調べるべきなのでしょうが、今手元にないのでちょっと苦しいっす。すいません