Day: May 9, 2007

コマンドライン引数の取得 その2

関数getopt_longは長い形式のオプションの取得に便利な関数である.これはGNU拡張らしい.長いオプションは大抵–で始まる.getopt_long外部変数オプションの設定サンプルgetopt_long_only getopt_long getopt_longはgetoptを拡張して–で始まる長い形式のオプションを解釈できるようにした関数である.POSIX準拠ではなく,GNU独自のものらしい.長い形式のオプションは一意に決まる限り短く省略できる.例えば,–foo –foovarの二つのみをオプションに持つコマンドがあった場合,–foovarは一意に決まる省略形–foovでも解釈される.int getopt_long(int argc, char * const argv[],const char *optstring,const struct option *longopts, int longindex); 各引数について以下に示す. argc,argv[]main関数の引数をそのまま渡す.*optstringgetoptと同様,短い形式のオプションを示す.*longopts長い形式のオプションを示す構造体optionの配列.longindex マッチした長い形式のオプションに対するlongoptsのインデクス.getopt_longのさらなる説明はオプションを示す構造体optionの説明が中心になる.struct … More コマンドライン引数の取得 その2

forkとwaitとゾンビプロセス

forkはプロセスのコピーを作成する.そしてwaitで待つ.処理の流れが一本から複数に分かれる様がフォークみたいだからforkなのだろうか.forkで何がコピーされるのかforkのサンプルwaitwaitのサンプルゾンビプロセスについて forkで何がコピーされるのか forkはそれを実行したプロセスの子プロセスを作成する.子プロセスは親プロセスのコピーである.プロセスをどこまで実行したか(プログラムカウンタ?)もコピーされるので,子プロセスはforkの返り値が返るところから実行される(と,思う).あと,ファイルディスクリプタなどもオープンされていればそのままコピーされる.コピーされないのはpidやppidとファイルロックやサスペンド中のシグナルぐらいだろうか.そして,そのforkの返り値によって,プロセスは自分が親プロセスか子プロセスかを知ることができる.成功すれば子プロセスには0が返り,親プロセスには子プロセスのPIDが返される.エラーの時は親プロセスに-1が返り,子プロセスは生成されない.-1が返ったときには,errnoに値がセットされているはず.EAGAIN親プロセスのページ・テーブルのコピーと子プロセスのタスク構造に生成に必要なメモリをfork が割り当てることができなかった.ENOMEMメモリが足りないために,forkは必要なカーネル構造体を割り当てることができなかった. 子プロセスはPIDが固有で,PPIDを親プロセスのPIDに設定されている以外は親プロセスのものがコピーされる. forkのサンプル 子プロセスを生成して子プロセスの親プロセスがそれぞれ自分と相手のPIDを表示するサンプルを作成してみた. 実行すると,以下のようになる.fork donefork donechild process. pid = 1261. my ppid = 1260parent process. pid … More forkとwaitとゾンビプロセス

sigaction

sigaction関数はsignal関数よりもより細かい制御ができる.sigactionはX/Open仕様ではsignalよりも信頼できるとして推奨しているらしい.sigaction構造体サンプルsigset_tsigactionの仲間達サンプル2sigactionのメモ sigaction構造体 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);sigactionの引数について以下に示す.signumsigaction構造体を設定するシグナル.act設定するsigaction構造体.oldact設定前(変更前)のsigaction構造体.sigaction関数は成功すれば0,失敗すると-1を返す.sigaction関数ではsigaction構造体を引数にとる.この構造体がsignal関数との違いの鍵を握っている.以下,/usr/include/bits/sigaction.hより sa_sigactionは3つの引数を取る関数で,そのうちの一つsiginfo_tは構造体になっていて,またたくさんのフィールドを持つ.面倒なのでここでは書かないが,こいつを使うとさらに細かい制御ができそうな雰囲気を感じた. サンプル sigactionを使ったサンプルを以下に示す. sigset_t sigactionの仲間達 仲間かどうかは知らないが,とにかくman sigactionとすると一緒に出て来る関数が三つある.int sigprocmask(int … More sigaction

libsafeを使ってみる

libsafeはCの標準関数のうち,よくバッファオーバーフローを起こすものを差し替えてバッファサイズをチェックする処理をしてくれるらしいので試してみた.libsafeって何だ?インストール試してみる libsafeって何だ? libsafeはベル研がLinux向けに開発した,バッファオーバフロー対策用のライブラリらしい. 詳細はこの辺に書いてあるが,とにかくCの標準ライブラリがバッファのオーバーフローチェックをしてないからダメなんじゃないか,だったらそういうライブラリを作って差し替えようとか,そういう感じらしい. インストール 試してみる 適当にlibsafeがチェックする関数を呼び出すサンプルを作成してみる.

マウス

ncursesではマウスの操作を検知できるらしい.ただしSVr4以前とか4.4BSD以前ではできないようだ.マウスイベントバグマウスイベントの検知座標クリックの検知サンプル マウスイベント バグ man mouseのBUGSによるとxtermでマウスイベントを検知するためには,以下の条件が必要らしい.cbreakモードになっている(wmousemaskでマウスイベント検知をONにする場合).キーパッドが有効(なんかマウスイベントをファンクションキーとして扱ってしまうらしい).terminfoでkmousの値が”\E[M”になっている必要がある.環境変数$TERMが”xterm”を含むかterminfoでkmousが定義されている.なんか移植性が心配な雰囲気だ. マウスイベントの検知 座標 特に指定しなければ,マウスイベントはmousemaskで指定したボタンが押されたとか離されたということしか検知しない.マウスイベントを検知したとき,マウスの座標をしりたければmousemaskでREPORT_MOUSE_POSITIONを渡す.例えばボタン1の押下を検知し,その座標を知りたいときはmousemask(BUTTON1_PRESSED | REPORT_MOUSE_POSITION, NULL);とする.また,stdscrでの座標からスクリーンでの座標に変換するにはbool mouse_trafo(int* pY, int* pX, bool to_screen);を使用する.このへんはslk_…関数に関係があるらしい(man slk参照). クリックの検知 マウスのクリックやダブルクリックはmousemask関数の第一引数にBUTTON1_PRESSEDやBUTTON1_RELEASEDを指定して実装出来そうだが,BUTTON1_CLICKEDやBUTTON1_DOUBLE_CLICKED等を指定するだけで検知することができる.で,ボタン押下とボタン離しでボタンクリックやダブルクリックを実装するときをイメージすると,ボタンが押されてから何秒以内にボタンを離せばクリックとするのかを指定する必要がある気がして来る.これを指定するのが,int … More マウス

リソース情報の取得と設定

プログラムを実行する際のリソース情報(とくにリソースの制限)を取得する必要がある場合がある.limits.hプライオリティの取得と設定リソース使用量リソースの制限とその設定 limits.h プライオリティの取得と設定 リソース使用量 リソースの制限とその設定 リソースの制限にはソフト制限とハード制限がある. ソフト制限 超えるべきでない値.超えるとライブラリ関数がエラーを返すことがある. ハード制限 この制限を超えるとシステムがプロセスにシグナルを送りそのプロセスを殺そうとする ソフト制限はハード制限を超えない範囲で設定できる,ということである.さらに,ハード制限は引き下げることが可能である.また,root権限がある場合だけはハード制限を引きあげることができる.リソースの制限はrlimit構造体で取得できる.以下,/usr/include/bits/resource.hより

termios

termiosは「非同期通信ポートを制御するための汎用ターミナルインタフェース」らしい.端末をclearしたりechoを抑制したり非カノニカルモードにしたりできる.termios構造体非カノニカルモード termios構造体 termiosには,いろんな関数がある.これらの多くは,termios構造体を引数にとり,この構造体のフィールド操作によって端末を制御する.termios構造体は少なくとも以下のフィールドがある.tcflags_t c_iflag端末への入力をどうプログラムに渡すかの設定.tcflags_t c_oflagプログラムからの出力をどう処理するかの設定.tcflags_t c_cflag端末のハードウェア的制御の設定.tcflags_t c_lflag端末のいろんな制御の設定.cc_t c_cc[NCCS]特殊制御文字(EOFとか)の設定.各フィールドの設定変更にはマクロを使用するが,沢山あるので随時manを見ることになりそうだ.端末制御のコーディングのパターンは以下のようになる.tcgetattrで端末の現在の設定を取得.端末の現在の設定のコピーを作成し,フィールドをいろいろ書き換える.tcsetattrにあたらしい端末の設定を渡して反映.何かする.tcsetattrに最初に取得した端末の設定を渡して端末をもとの状態に戻す.tcsetattrの第二引数には,端末設定の変更方法として以下の三つの選択肢がある.TCSANOW変更を即反映TCSADRAIN出力がすべて転送された後に反映.出力に影響するパラメータを変更したときに使用.TCSAFLUSH出力を全て転送し,受信したが読み込んでいない入力を全て破棄して反映. 非カノニカルモード termiosについて詳細に書くのは大変なので例を書いてみる.通常は入力をいろいろ編集して納得したらリターンを押してプログラムに入力を渡す(カノニカルモード).が,キーを押した瞬間にプログラムに入力を渡したいこともある(非カノニカルモード).以下は非カノニカルモードで入力を受け取るサンプルである. 実行結果を以下に示す.$ ./nocanoninput = ainput = binput = cinput = qキーを押したら即input … More termios

terminfo

terminfoを使用すると出力場所とかをイイ感じにできるかもしれない.terminfoとはterminfoのデータclearカーソル移動 terminfoとは かつてのUNIXシステムではいろんな端末があって,それらの制御方法は標準化されていなかった.そこで,統一されたインタフェースでプログラムからの要求を受け取り,保持している各端末の制御方法に関するデータを参照して処理するパッケージとしてterminfoが登場した.terminfoはcursesと組み合わせて使用するが,両者の境界はなんだかあいまいらしい. terminfoのデータ 私のシステムではterminfoのデータが入っているterminfoファイルは/usr/share/terminfo/に沢山あった.これらはterminfoファイル名の頭文字ごとにディレクトリに分けられ,それぞれのterminfoファイルはticというコマンドでコンパイルされたもの(バイナリファイル)らしい.terminfoファイル内の各データは以下の3種類に分けられる.ブール型該当する機能をサポートするか否か.数値型サイズを表す.文字列型端末機能にアクセスするためのキーとか,キーボード入力をどんな文字列に変換してプログラムに渡すかを定義.端末を制御するコーディングの流れは大体以下のようになる.setuptermで現在の端末に対応するTERMINAL構造体を取得tigetflag,tigetnum,tigetstrで現在の端末の状態を取得エスケープシーケンスを生成putp,tputsでエスケープシーケンスを端末に送るエスケープシーケンスの生成についてちょっと書いておく.まず,どんな文字列型のデータがあるかは,man 5 terminfoに一覧がある(やたら多い).ここにある「Capname」をキーにしてtigetstrを呼び出すとその機能に対応したエスケープシーケンスが得られる.で,そこで得られるエスケープシーケンスの中にはそのままでは使用できないものがある.「カーソルを何行目の何列目に移動しろ」などという命令に対応するエスケープシーケンスは,数が膨大になるのを防ぐために,「何行目」とか「何列目」というのをパラメータ化している.このような場合,tparmを使用して取得したエスケープシーケンスのパラメータに具体的な値をいれるというステップが入る. clear サンプルとしてclearコマンドを作成する.man clearによるとclearコマンド自体,tputをclear引数とともに呼んでいるらしい. terminfoを使用するときは,コンパイル時にコンパイルオプションが必要である.私のシステムでは以下でコンパイルできた.gcc -Wall -o my_clear my_clear.c -lncurses-lncursesがないと怒られる.システムによっては-Iでncurses.hの場所を教える必要があるかもしれない(私のシステムでは/usr/include/ncurses.hがあった).コンパイル後実行するとclearコマンドと同様の結果が得られた. カーソル移動 もう一つの例としてカーソル移動のサンプルを作成してみた. このサンプルはviみたくhjklでカーソルを移動する.文字は入力できない.あとqを入力すると終了する.

socket関数とプロトコル

まずsocket関数とプロトコルとアドレスについて記述しておく.socket関数ドメインとタイプとプロトコルアドレスの表現よく使うSocket socket関数 同一マシンのプロセス間だけでなく,別マシンのプロセスとも通信したりするには,Socketを使用する.で,そのためにsocketシステムコールを使用する.int socket(int domain, int type, int protocol);この関数の呼び出しが成功するとソケットへのディスクリプタを返す.これはファイルディスクリプタと同様に扱える.引数にprotocolというのがある.これを見ると,TCP/IPだのUDPだのを指定するのかと思うのは私だけだろうか.実際はちょっと違う.例えばTCP/IPで通信したければ,socketを以下のように呼び出す.socket_df = socket(AF_INET, SOCK_STREAM, 0);雰囲気を掴めたところで(こんなんで掴めるのか?)パラメータを一つずつ見ていく. ドメインとタイプとプロトコル アドレスの表現 socket関数でプロトコルを指定できた.では,アドレスはどうだろうか.アドレスはドメインによって独自のアドレス形式があるらしい.例えば,PF_INETではアドレスを以下の構造体で指定する(man ipより).struct sockaddr_in {sa_family_t sin_family; … More socket関数とプロトコル

ロックとmutex

スレッドの同期には,pthread_joinによる合流の他に,ロックという方法がある.mutexって何だ?mutexの属性と初期化mutexのロックとアンロックサンプル mutexって何だ? mutexはmutual exclusionの略らしい. ということは,mutexは「ミューテクス」とかそんな感じの読みのような気がするが,正しい読みはよくわからない(※). とにかく,pthreadでは,mutexをロックすることによって,スレッド間の同期を行うことができる.mutex = バイナリセマフォということで,よいと思う. つまり,あるスレッドでロックしたmutexに対し,別のスレッドがロックしようとしたら,最初のスレッドがそのmutexを開放するまで後のスレッドは待たされる. 上図で,破線で囲まれた部分の処理が,同期が保証される領域で,クリティカルセクションという. 具体的にはクリティカルセクションでは,複数のスレッドで参照,更新するデータに対する処理を行う. クリティカルセクションで参照するデータは,別のスレッドによって更新途中になっている不完全なデータを参照したり更新したりしてしまう心配がない. 性能の観点からすると,クリティカルセクションは短い程よい.※ 私はどういうわけか,「ムテフ」とか「マテフ」と読んでしまうが,これは,まぁ,間違ってるんだろうな. mutexの属性と初期化 mutexはまず初期化する必要がある.int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex-attr_t *mutexattr);pthread_mutex_initはmutexattarで指定した属性をもつmutexを作成する. … More ロックとmutex