telnet でログインすると Control-O が使えないことがあります? [1998.5]

1982年頃アメリカで流布した文書 Real Programmers Don't Write Pascal (邦訳,本物のプログラマは PASCAL を使わない,bit 1985年4月号) でも UNIX の tty 回りは難解で複雑なので本物のプログラマ好みだ,と紹介されているように,昔から悩みの種なのである.「Non blocking モードで open した tty の output queue 内の文字数がいっぱいで書けないのが原因で write が return した後,queue の文字数が low water mark を下回ったことを知らせる SIGIO をプロセスにあげてくれるか?」なんて問題がすぐ発生するのが tty である.FAQ に質問されたらどうしよう.

UNIX は端末からの入力に対してカーネルが直接処理をする部分が多い.Control-C でプログラムを中断するのがカーネルなのはわかるだろうが,入力途中の文字を Delete (や Control-H) で1文字消すのもカーネルがやってくれる.端末に関する機能や設定は全体として大きく次の 5つに分けられるだろう.

である.設定は stty -a コマンドで表示される.5つの分類ではないかもしれない.さて表示のなかに ^O が見当たるだろうか.あれば,discard とか flush が ^O だと表示されているだろう.その付近には intr が ^C とか stop が ^S と出ていないか? そう,それが端末割り込み文字の現在の設定である.discard は,端末への出力を捨てろとの指令を意味する.Contol-S で出力を一時停止するのとは違って,プログラムは走り続けている.この設定が有効のままなら,プログラムに Control-O は渡らない

最近の telnet を使ってログインすると手前のホストの端末設定を通信先のホストと合わせるように telnet が調整をする.手前の discard 設定を無効にしてくれたりする.相手の telnet のバージョンによって調整ができないと,有効のままになることがある.それでホストによって Control-O が動かなかったりするのだ.stty discard '^-' を両方で実行すれば確実に無効になる.stty -a で確かめてみよう.


† Control-O は Emacs の open-line コマンドに割り当てられている.あまり使われないようで,Canna ではデフォルトでかな漢字変換の起動文字に割り当てられている.だが,使うとやめられないコマンドだ.
打ってみればわかるが,カーソルの位置を変えずに空行をその位置に追加していく.段落の途中に文章を追加するときは,まず Control-O を5〜6回打ってすき間をあける.そこに文章を打っていく.リターンで改行しても,空行がある程度残っているときには改行は空行を増やさない.よって画面が大きく動くことなく文章を追加することができる.追加が終ったら,C-x C-o でもって余計な空行を取り除く.もちろんプログラムを書くときにも便利に使える.[2003.7.19]

FreeBSD の場合 [2003.6.20]


プログラムを速くしたいのですが…? [1998.7]

プログラムの速度を考える前にまずそのプログラムが正しく動作することをもう一度確認しましょう.誤った入力で破綻しませんか? プログラムソースはわかりやすく書かれていますか? プログラムは,作成,実行,保守,更新と,そのライフタイムのどの場面でも性能維持のコストが小さいことが求められます.実行時の速度はその一部分にしかすぎません …

と言われたり,書いてあったりするが,実行が速いのが悪いことはない.一度は実行速度だけを追求したプログラムを書いてみるべきだ.納期や〆切に追われる身になる前に,こだわって,究めてみよう.さて,C で書くとして,何をやってもいいのだが基本ははずせない.問題をよく研究して適切なアルゴリズムを適用しよう.問題をうまく分割し,適切なデータ構造を採用しよう. 入出力の高速化ならバッファリングは必須だ.プロファイラも活用しよう.

次は,実行する CPU の研究だ.bit 1998年6月号のように新命令を使いこなすのも面白いが,遅い命令,速い命令,同時に実行できる命令,パイプラインの構成といった CPU の特徴を把握しよう.cc -S でコンパイルするとコンパイル結果のアセンブラ命令が見える.最適化オプションをいろいろ変えて出力を吟味しよう.CPU 内に性能測定用のカウンタが備わっていたらぜひ値を見てみよう.Intel の CPU ならカウンタの説明も入手できる.命令フェッチ,メモリアクセス,キャッシュヒット,分岐命令のフェッチ,分岐数といった統計情報が得られる.つぼを見つけて asm 文に挑戦しよう.

命令の実行回数が減っても速度が上がらないのが最近の計算機だ.キャッシュである.パソコン UNIX なら,BIOS 設定で L1, L2 のキャッシュをオフにしてみれば,その働きが体感できる.少なくとも100倍は遅くなるだろう.いかにキャッシュに当てるかが決め手だ.組で使う大域変数のアドレスを連続させるのがいい.ソースで隣に書いても,初期化しない大域変数はばらばらに配置されることが多いので,無駄に見えても宣言部分で 1 に初期化してみよう†3.struct でまとめてもいい.ページで泣き別れていないかは nm で確認する.

ここまでやって速くなったかな.手を入れたら遅くなった? よくあることだが,やってみないとわからない.そして次の言葉も理解できるだろう.プログラムの速度を … (先頭に戻る).


IA-32 Intel Architecture Developers Manual にある.特に IA-32 Intel Architecture Optimization Reference Manual は CPU の能力を発揮させるためのマニュアルである.
AMD Athlon XP なら Athlon XP Processor Technical Documentations に揃っている.
DEC Alpha なら和書 64bit RISC Alpha APX アーキテクチャ概要 共立出版 が面白い [2003.8.3]
FreeBSD 4 なら man 4 perfmon を参照のこと./dev/perfmon に対する ioctl で Intel CPU の内部カウンタを読み出すことができる.[2003.8.3]
†3 gcc 2.7 (とそれに組になっている ld) では,0 以外の値で大域変数を初期化すると,data 領域にプログラムソースと同じ順序で配置されるようだ.bss に置かれた変数の配置はソースでの登場順とは変わってしまう.nm -n で確認してほしい.[2003.8.3]


C プログラムから計算機を停止するには? [1998.8]

計算機を止めるには,スーパーユーザで shutdown -h now とか halt と打てばいい,のは知っているだろうか.知っているなら,プログラムから shutdown か halt プログラムを起動してしまえばいいのだ.コマンドだってプログラムなのだから,コマンドで行なえる操作はもちろん自分のプログラムでもできるはずだ.でも,そのためのライブラリを調べたり,引数を正しく用意したりするのはかなり面倒だ.shutdown を呼び出せば,すぐ止めるのも,20分後に止めるのもプログラムは簡単だ.実は shutdown プログラムも -h のときには halt を呼んでいるのだ.システム停止にはマウスを使ってメニュー選択,だとそう簡単ではない.スタートボタンが左下にあるとは限らないからね. (ところでシステム停止にいくためにスタートボタンを押すというのは,もう誰もが慣れているのだろうな.教えてもらわないと決してわからない入口だけど.)

さて,プログラムファイルを起動するにはライブラリ関数 system が簡単だ.shutdown はそうは呼ばないだろうが,stty を呼んでいるプログラムはよく見掛ける.パスワードを入力する前後で stty -echo と stty echo を system で呼び出すのは常套手段だ.端末回りは UNIX の中でも特に扱いの面倒なもののひとつだが,stty さえ知っていれば簡単だ.起動したコマンドの出力を読み込むには,関数 popen が使える.コマンドを対話的に呼ぶのは少し難しいが,最近の UNIX には双方向パイプを使った popen もある.これはプロセス間通信そのものだ.socket もなんにもいらない.昔 popen で Lisp 処理系を起動して分散処理の実験をしたこともあるが,当然相手のプログラムの変更は全く必要ない.

コマンドではできるのに,system 関数では動かないものもある.cd コマンドだ.system や popen は内部で sh を立ち上げてコマンドの起動を任せる.関数 system で cd を実行すると,この仲立ちの sh の作業ディレクトリが移るだけで,呼び出した側には効果がない.いよいよシステムコールプログラミングをしなければならないのだ.そんなに恐いものではないから,少しづつやってみよう.まずは man 2 chdir からかな.


system や popen は大変便利な関数だが,ユーザから得た文字列を system 関数に渡すのは避けよう.system 関数でユーザに自由にコマンドを実行される可能性がある.popen も同様.[2003.6.20]


プログラムソースの整理をしたいのですが. [1998.10]

一人が書けるソースの量はそんなに多くない.書き下ろしで,1週間にデバッグ済みプログラムを 1,000行づつ積み重ねていくあたりがいいところではないか.それでもファイルはいくつもできるし,修正をキャンセルして直前の動いていた版に戻すのも大変だ.プロジェクトで複数の人間でプログラムを書いていたら,その人達がネットワークで繋がっているだけで地理的に離れていたら,ソースの管理に計算機の助けが欠かせない.

種々のソース管理システムがあるが,ここでは CVS (Concurrent Version System) を紹介する.入手先等は,リンク先を見てほしい.

CVS ではファイルの原本はリポジトリと呼ぶひとつのディレクトリに格納されている.プログラマは,リポジトリから取出して手元に置いた原本のコピーに対して修正を加える.新たに書いたファイルもリポジトリに登録することで他の人に見えるようになる.ロックをかけて原本を修正していくタイプではない.よってひとつのファイルを2人以上で同時に修正している可能性がある.これは,修正をリポジトリに再登録する際にシステムに通知される.修正のマージはかなり自動的にできるが人の介入が必要ならそう通知される.

ソース管理の機能は十分に整っている.一人でプログラムを書いているときにも非常に便利だ.リポジトリには最初の版からの修正が差分の形で保持されていて,ひとつのファイルについて,過去のバージョン (修正の登録ごとにバージョンが上がる) がいつでも取り出せる.保存スペースも小さい.区切りがついたときに,ファイルすべてに指定したタグ名をつけることができる.このタグのついたバージョンひとまとまりのファイルをいつでも取り出すことができる.修正の登録時には,メッセージをいれることを強要される.(これは大きな利点だ).

リリース後に修正が必要なったときなど,開発最先端からではなくリリース版から枝分かれしたバージョンを設定することができる.ここで修正してリリース版からのパッチを作成したり,本流の最先端版に修正個所をマージする手助けをしてくれる.

プログラムコードだけでなく,論文書きや,WEB ページなど,手で書いたファイルすべての管理に有効だろう.


この FAQ のページも cvs で管理しています [2003.6.20]
subversion


グループの設定のしかたは? [1998.11]

1台の UNIX マシンで複数の人が共同作業をするときの要求は,共同作業用のファイルは自由に読み書きしたい,作業用ファイルを作成したら,自動的に共同者には読み書き自由の設定にしたい.作業用ディレクトリを作るのも ok. 自分のホームディレクトリやそこにあるファイルは共同者だからといって書き込んでほしくない,などとすべてを満たすのが難しものである.作ったファイルをいちいちモードを変えたり,ひとつのユーザに su してしまったりでは長続きしないだろう.しかし,これはグループ設定でほとんど解決できる.

設定は次のようにする.作業用のグループを新たに作って,共同者を登録する.各自の umask は002に設定する.共同作業用のディレクトリのトップは作成時にグループ id を作業用グループにして chmod g+ws しておく.umask 002 は,ファイルやディレクトリの作成時に8進数で2のビットのモード,つまり 他人の書き込みだけを禁止する意味する.これで共同者には書き込み可能となる.ディレクトリが g+s になっていると,その元に作られたファイル,ディレクトリのグループは親ディレクトリと同じになる.さらにグループの s ビットはディレクトリに引き継がれる.

アカウントを新たに作るとき,グループは,職場や研究室の所属によって既存のグループに登録することがよく行なわれる.この場合,共同作業用に umask を 002 に設定すると,自分のホームディレクトリに作成したファイルがグループの他の人から書き込み可能となってしまう.これを防ぐ簡単な方法は,アカウントごとにグループをひとつ作って (アカウントと同じ名前にすれば簡単) そこに所属することである.そのグループには本人しか登録しない.共同作業には上に書いたように別にグループを作る.これで要求はすべて満たすことができた.

グループを個別に作るのは FreeBSD のユーザ登録スクリプトで行なわれている.また FreeBSD ではディレクトリのグループの s ビットにかかわらず,そこに作成されるファイル,ディレクトリのグループはそのグループに所属する.

この仕組みを理解していると,共同作業はより快適なものとなるだろう.


ガーベジコレクションとはなんですか? [1999.1]

最近,Java にこのガーベジコレクション (Garbage Collection 略して GC, ゴミ集めなどと訳される) 機能がある,というので表舞台でも聞くようになったが,裏方の代表のような機能である.

読込み終るまでいくつ来るか分からないデータ,計算してみるまでどこまで増えるか分からない配列,文字列を切ったり張ったりの処理,と実行時にその場で記憶域の大きさを決めたい例は多い.C で malloc などすれば,使えるメモリをもらえる.だが free 関数でこれを返すのがけっこう面倒だ.駅の傘でもメモリでも,使用済みは回収しないと破綻がくるが,回収を引き受けるのが GC である.

GC が回収してくれるのは,実行時に動的に確保した領域である.未使用だからといって,大域変数を再利用に回しはしない.Lisp のような記号処理言語では,記憶域は動的に確保するのが基本なので,このときにはすべての領域が GC の守備範囲といってよい.回収できる記憶域とは,要求して得た領域のうち利用できなくなった部分である.例えば,malloc を続けて 2回して,結果を同じ変数に代入したら,1回目で得た領域を使用することは決してできない.GC はこのような領域を見つけて,次の領域要求時にここを再利用できるように備えるのが役目だ.

シンプルな GC は,例えば大域変数や局所変数からたどれる記憶域にすべて印を付け,次に印のついていない領域を回収するものだ.この間は,新たなメモリ要求などは受け付けない.GC の間は他の仕事も停止する.マークアンドスイープ方式と呼ばれる.1960年に Lisp の作者,マッカーシー が発案してから脈々と使われている.Java の GC もこの方式だ.

とはいえ,GC 中に処理が止まってしまっては,実時間の仕事には使えない.ロボットがバットを振る瞬間に GC が動いてバットが止まったら空振りにもならない.停止を避けるためのアルゴリズムは多数考案されているし,現在もされ続けている.他にも,「確保した領域を差すポインタらしい」ビットパターンを追いかけて印付けをする conservative GC といった面白いアルゴリズムもある. 裏方とはいえ,活発な議論が続いている分野なのである.


Boehm-Demers-Weiser conservative GC は C 言語用の GC で,組み込むのには単に元のソースプログラムから free() の呼出しを取り除けばよい.FreeBSD では ports devel/boehm-gc になっているので簡単に利用できる.[2003.6.22]


   タイトル一覧  ホーム
webmaster