雑記
コンパイルの話
最終更新:
匿名ユーザー
-
view
C言語っていうのは、そのままでは実行することはできない。「コンパイル」という作業が最低限必要である。コンピュータにはテキストで書かれたソースファイルの意味はわからないからだ。
「コンパイル」という作業で行われることは、
①ソースファイルの解析
②処理の最適化
③ターゲットのマシン語命令への変換
が行われる。
①ソースファイルの解析
②処理の最適化
③ターゲットのマシン語命令への変換
が行われる。
注意が必要なのは、処理の最適化が行われることによって、C言語の時点でプログラマが思っているのとは違うコードが吐き出される可能性があるということだ。
例えば、staticなどの宣言をつけてスコープを同一ソースファイル内に限定した関数が、一箇所でしかコールされていない場合にはサブルーチンに分けず呼び出し元に展開したりとか、ループ内での定数同士の演算はループの外に出したりとか。
ただし、このあたりの処理はコンパイラに依存するのでそれを期待しすぎるのは良くないから、ほどほどに期待しつつもできるだけプログラマが自前で最適化してあげたほうがいいとは思うけど。。
で。マシン語に変換されるわけですが。
例えば、ifの条件判断で以下のような処理があったとする。
if(A == B) { 処理A } else { 処理B } return;
これがマシン語に展開されると、(個人的に判りやすいと思うのでZ80系アセンブラもどきで書いてます)
SAMPLE: LD A,_A ;Aレジスタに変数_Aの値を格納 LD B,_B ;Bレジスタに変数_Bの値を格納 CP B ;BレジスタとAレジスタの内容を比較 JP NZ,SHORI_B ;AレジとBレジの内容が不一致ならジャンプ SHORI_A: ;処理A JP END SHORI_B: ;処理B END: RET
こんな感じになる。(だいたいのイメージです)
んで、何が言いたいかというと、マルチスレッド/マルチタスクな処理の場合、実際にはとても細かい単位であっちのタスクこっちのタスクと切り替わることで見た目上複数の処理が平行して走っているように見せている。
ちょうど、分身の術みたいな感じ。
でもって他のタスク/スレッドへの処理の交代の切れ目はC言語のステートメント単位や関数単位ではなく、マシン語の命令単位であることがほとんど。
ってことは、さっきの例だと、C言語のソース上は比較の部分は1行なんだけど、その途中で他のタスクに切り替わってまた戻ってきて・・・なんてことが起こりうる。
怖いのは、ジャンプ命令の直前でそれが起こった場合、比較した結果が格納されているフラグレジスタの値が壊されてしまっていては正しく処理できない。
まあ、上のはあくまで例であって、いまどきのマルチタスクOSはたいていレジスタも全て切り替えのタイミングで退避/復元するので問題は起こりにくいが、ファイルなどの入出力や通信などを行っている際には注意が必要となる。
そういったクリティカルな処理を行う場合にはスレッドの切り替えおよびCPU割り込みを禁止したり、排他制御をかける必要がある。
C言語で処理を書いていると、ついついそのまま処理が流れて行ってくれることを期待したソースになってしまいがちなのだけれども、ほんのちょっと、そういったことも意識してプログラムすることでよりセキュアなプログラムになります。というお話でした。