All articles by Fujiko

セマフォ

共有メモリの話題に入る前の準備としてセマフォについて記述する.同期の問題セマフォとはセマフォを使ってみるセマフォと共有メモリ 同期の問題 二つのプロセス(もしくはスレッド)A,Bが共有する変数xがあったとする.そしてA,B両者がxに1を足すとする.xが0なら処理後のxの状態が2になっていることを期待してみる.Aがx(=0)の値を読み込む.Aがx(=0)に1を足してそれをxに書き込む.Bがx(=1)の値を読み込む.Bがx(=1)に1を足してそれをxに書き込む.この時x=2.上は「たまたま」正常なタイミングで動作した場合である.もしかしたら以下の状況なるかもしれない.Aがx(=0)の値を読み込む.Bがx(=0)の値を読み込む.Aがx(=0)に1を足してそれをxに書き込む.Bがx(=0)に1を足してそれをxに書き込む.この時x=1.期待に反してx=1となってしまった.これは「たまたま」プロセスの切替えタイミングが悪く,同期をとって処理すべき変数xに対してA,Bが非同期で処理してしまったから起こった現象である.このような処理をクリティカルセクションという.誰かがxをクリティカルセクションを処理しているときに,別の誰かはxを処理できない様にする必要がある. セマフォとは 上の問題を解決するために,ロック変数yを考えてみる.変数yが0の時は別のプロセスはxに対して処理をせず,1なら処理をする.したがってA,Bはxに対して処理する前にyをチェック,変更する.先のタイミングが悪かったパターンは以下のようになる.Aはy(=1)を読み込む.Aはyが1なのでこれを0にする.Aがx(=0)の値を読み込む.Bはy(=0)を読み込む.Bはyが0なので待つ.Aがx(=0)に1を足してそれをxに書き込む.Aはyを1に戻す.Bはy(=1)を読み込む.Bはyが1なのでこれを0にする.Bがx(=1)の値を読み込む.Bがx(=1)に1を足してそれをxに書き込む.Bはyを1に戻す.この時x=2.これでうまくいった.が,よく考えるとyに対してもまた最初のxと同じ状況が発生し得る.これではきりがないので,それを処理する間プロセスの切替えがおこらない何かが必要になる.これがセマフォ(semaphore)である.セマフォはDijkstraが発表したらしい.先のyがセマフォだったとすると,あるプロセスがyを読み込んで値を変更するまでの間,他のプロセスはyに対して処理しないことが保証されている.すなわちセマフォに対する処理はアトミックであることが保証されている.セマフォについてまとめてみた.0か正の整数をとる.waitとsignalという二つの操作だけが可能.waitをしたとき,値が1より大きければ値をデクリメントする.0ならば待つ.signalをしたとき,値が0より大きくなるのを待っているプロセスがあれば,そのプロセスを再開する.なければ値をインクリメントする.バイナリセマフォは0と1の二値をとる.汎用セマフォは0と複数の正の値をとる(例えば0,1,2).こうすると,クリティカルセクションの処理は以下のようになる.セマフォにwait操作をする.クリティカルセクションの実行.セマフォにsignal操作をする.これで問題は解決する. セマフォを使ってみる セマフォを使用するための関数には以下の3つがある.int semget(key_t key, int nsems, int semflg)keyで指定したnsems個からなるセマフォ集合の識別子を取得する.semflgはオプションでパーミッションなどもここで設定する.semflgでIPC_EXCLを指定するとセマフォが一意となることが保証される(keyがセマフォのために既に使用されていればsemgetは失敗する).int semop(int semid, struct sembuf *sops, unsigned nsops);semidで指定したセマフォ集合の操作のメンバを操作する.sopsはセマフォに設定したい値を持つsembuf構造体のnsops個の配列である.int … More セマフォ

bzlibでbz2ファイルを読み書きしてみる

bz2はメモリを喰うがzlibより圧縮効率がよいらしいので,そのライブラリbzlibを試してみた.bzlib.h圧縮エラーメッセージの取得解凍コンパイルと実行 bzlib.h 圧縮 データをbz2形式圧縮してファイル出力するサンプルを以下に示す. include include include define BZ2_MODE “wb” intmain(int argc, char *argv[]){if (argc != 4){fprintf(stderr, “Usage : %s … More bzlibでbz2ファイルを読み書きしてみる

for文

BASHスクリプトにおけるfor文の書き方for文seq for文 for文の構文を以下に示す.for 変数 in 値(複数可)do文(複数可)donefor文では変数名に値を代入しdoとdoneに囲まれた文を実行する.doneまで来ると,次の値を変数に代入してまたdoとdoneに囲まれた文を実行する.以下に例を示す.for x in a b c d e f gdoecho $x seq

BIOとBase64

BIOはOpenSSLにおけるフレームワークにおける抽象データ型で,暗号化や符号化,ファイルやネットワークといった入出力の詳細をアプリケーションから隠蔽する.で,このフレームワークのもとで実装されているBase64コーデックを試してみた.BIOって何だ?base64エンコードするサンプルbase64デコードするサンプルサンプルの実行 BIOって何だ? BIOはOpenSSLにおける入出力関連のフレームワークにおける抽象データ型である. BIOを使用することにより,アプリケーションでは変更不要なある「お決まりの形式」でプログラミングしつつ,容易にファイル/ネットワークとか,暗号化形式や符号化形式を切替えることができる. さらに,BIOはチェインを組むことができ,例えば暗号化して符号化してネットワークに出力するというBIOのチェイン(このチェイン自信もBIO型となる)を作成して書き出すのも,データをそのままファイルに書き出すのも同様の記述によって表現できるようになる. 上の図はBIOのクラス図である. BIO_f_md,BIO_f_base64,BIO_s_file,BIO_s_connectは実際には関数だが,イメージを掴みやすくするために,上のように記述した. man bioによると,BIOには二種類ある.ソース/シンクBIOソケットBIOとか,ファイルBIOといった,データのソース(源),シンク(宛先)を実装したBIO.フィルターBIO暗号化など,他のBIOからデータを受け取り,別のBIOにデータを渡すフィルターの役割を果たす. フィルターBIOに格納されているデータが変更されるかどうかは,BIOの実装やオペレーションによる. base64エンコードするサンプル では,実際にBIOを使用したプログラミングを試してみる. 今回はbase64でエンコードするサンプルとデコードするサンプルを作成してみる. まずはエンコード. base64デコードするサンプル 次にデコードのサンプルを示す. サンプルの実行

OpenSSLでRSAを試してみる

OpenSSLにはRSAのキーペアを作成し,これを使用して暗号化,復号する機能や,キーをPEM形式で入出力する機能があるのでためしてみた.概要キーペアの作成キーのダンプ出力キーのPEM形式出力エラーメッセージの出力PEM形式キーファイルの読み込み暗号化と復号サンプルの実行 概要 今回は,OpenSSLで以下の作業をする.RSAキーペアの作成RSAは公開鍵方式なので,その公開鍵と秘密鍵を生成する.RSAキーをPEM形式で入出力生成したRSAの公開鍵と秘密鍵をPEM形式でファイルに出力したり,PEM形式のキーデータを読み込んだりする.RSA暗号化と復号RSAキーペアで暗号化と復号を行う.具体的には,キーペアを作成してPEM形式出力するサンプルと,PEM形式キーファイルを読み込んでRSA暗号化,復号を行うサンプルを作成してみた. キーペアの作成 では,キーペアを作成してPEM形式出力するサンプルを以下に示す. キーのダンプ出力 キーのPEM形式出力 エラーメッセージの出力 PEM形式キーファイルの読み込み では,PEM形式キーファイルを読み込んでRSA暗号化,復号を行うサンプルを以下に示す. 暗号化と復号 サンプルの実行

CLOS

CLOSとはCommon Lisp Object Systemの略らしい.OOというのはメソッドというしばしば破壊的な関数がバンバンでてきて,Lispとは相容れない世界かと思っていたのだが,そうではないようだ.クラス定義とインスタンンス化とスロットの参照メソッドスロット優先度総称関数スロットの継承補助メソッドオペレータメソッドコンビネーション総称関数の削除 クラス定義とインスタンンス化とスロットの参照 メソッド スロット クラスを定義する際に,スロットにいろいろな属性を付加することができる.:accessorslot-valueとは別にスロットを参照できる関数を暗黙に定義する.:reader:accessorは読み書きのためのアクセサ関数を定義するが,:readerは読み込み専用関数を暗黙に定義する.定義された関数の引数はインスタンスである.:write:accessorは読み書きのためのアクセサ関数を定義するが,:readerは書き込み専用関数を暗黙に定義する.定義された関数の第一引数は設定値,第二引数がインスタンスである.:initform該当スロットのデフォルト値を定義.:initargmake-instance時にスロットに初期値設定を行えるようにする.:initargの後には:で始まる識別子を指定して,make-instance時にはその識別子を介して値を設定する. (defclass clazz ()((slot :initarg :slotIni))) (slot-value (make-instance ‘clazz :slotIni 5) ‘slot)5:allocation引数に:classを指定すると,該当スロットはインスタンス間で共有される.つまり該当フスロットがいわゆるクラスフィールドとなる.:instanceはデフォルトで,つまりインスタンスフィールドである.:documentationスロットのドキュメント.:typeスロットの型を指定.以下にこれらのいくつかを使用する例を示す.(defclass hogefuga … More CLOS

多値

Common Lispでは関数は値を0個以上返す.これまでは主に1つの値を返す場合の記述をしてきた.本頁では0個の値を返す,つまり値を返さない場合や2個以上の値を返す場合について記述する.多値を返す多値を受け取る多値を他の関数に渡すmultiple-value-list 多値を返す 多値を返す関数としてvaluesがある.(values 値 値 値…)valuesは引数の値をすべて返す.以下に例を示す. (values 1 2 3)1 ;2 ;3このvaluesを関数の最後に実行すれば,多値を返す関数を作成することができる.以下は三つの引数をそのまま返す関数である.(defun hoge (a b c)(values a b c))HOGE … More 多値

トランザクション

JDBCのデフォルト動作はautocommitである.Connectionオブジェクトのメソッド呼び出しにより,autocommitのon/offやCOMMIT,ROLLBACKができる.また,きれいな方法ではないがJDBCによるDBアクセスプログラムは,基本的にプログラムでSQL文を生成して発行するだけなので,BEGINとかCOMMITというSQL文を発行すればトランザクション処理ができる.概要サンプルConnectionオブジェクトによるトランザクション操作BEGIN,COMMIT等のSQL文を発行する方法トランザクションブロックSELECTにおけるターゲットリスト 概要 JDBCではデフォルトではautocommitとなっている.すなわち1SQL文で1トランザクションとなり自動でCOMMITがかかる.これはautocommitのon/offはConnectionオブジェクトで設定する.すなわち接続単位に設定することになる.さらにConnectionオブジェクトのcommit,rollbackメソッドによりCOMMITとROLLBACKを行う. サンプル 以下にautocommitをoffとしてトランザクション処理を行うサンプルを示す. このサンプルでは以下を行う.DB接続テーブル作成.COMMIT.作成したテーブルのレコード数を問い合わせる1トランザクションブロック内でインサート.COMMIT.作成したテーブルのレコード数を問い合わせるテーブル削除.COMMIT.DB切断これを実行してみる. Connectionオブジェクトによるトランザクション操作 BEGIN,COMMIT等のSQL文を発行する方法 Connectionオブジェクトのメソッド呼び出しではなく,JDBCの上位プログラムでBEGIN,COMMITなどのSQL文を発行することができた.その方法を以下に示すが推奨されない方法である.さて,JDBCのデフォルトではautocommitなので,トランザクションブロックはSQL文BEGIN;で開始しCOMMIT;あるいはROLLBACK;で終了する必要がある.いずれのSQL文もレコードが選択されることはないので,executeUpdateによって発行する.JDBCを利用してトランザクション処理を行うサンプルを以下に示す. トランザクションブロック SELECTにおけるターゲットリスト トランザクションから話題が離れるが,本頁の二つのサンプルでは以下のようなSELECT文を発行した.SELECT count(*) AS record_count from jdbcsample ;このSQL文では集約関数countの列にあえてrecord_countという名前をつけている.そして以下のようにその名前を使用して値を取得している.System.out.println (“Got record … More トランザクション

接続

まずlibpqを使用するための環境を整えた.で,接続しないと何にもできないので,libpqを使用してPostgreSQLへ接続する方法を調査してみた.環境構築サンプル接続関数PQconnectdb接続情報の取得切断環境構築Debian GNU/Linuxを使用している私は以下をした.apt-get install libpgsql2 postgresql-dev他の環境ではどうしたらよいのかは知らない.とりあえず以下があればよい(たぶん).libpq-fe.h私のマシンでは以下にあった./usr/include/postgresql/libpq-fe.hlibpq.so私のマシンでは以下にあった./usr/lib/libpq.so サンプル libpqでPostgreSQLに接続するプログラムでの処理概要は以下となる.PostgreSQLに接続PostgreSQLに要求切断本頁では,もっともPostgreSQLへの接続方法のうち,もっとも基本的なものと切断方法について記述する.また,DB接続プログラムでのもっとも基本的な注意点として,接続状態や接続情報の取得,チェックがある.これらの方法についても記述する.以下はPostgreSQLに接続して接続情報を取得,表示し,切断するサンプルである. 接続関数PQconnectdb libpqには,いくつかのDB接続関数がある.前節のサンプルでは以下を使用した.PGconn *PQconnectdb(const char *conninfo)引数は一つのcharへのポインタであり,先述の実行例のようにスペース区切りでキーワード=値を繋げたものである.主なキーワードは以下である.hostDBサーバ名hostaddrDBサーバのIPアドレスuserDB接続時のユーザ名passworduserに対するパスワードdbnameDB名PQconnectDBは内部でPGconnの領域を確保し,それへのポインタを返す.NULLを返すのは,PGconnへの領域確保に失敗した場合のみで,接続に失敗しても返す.PGconnはサーバへの接続情報であり,これによりサーバに対して複数接続し管理することができるようになっている.PGconnは直接操作,参照してはいけない(普通にはできないようになっている).接続状態は以下で取得する.ConnStatusType PQstatus(const PGconn *conn)返り値には多くのパターンがあるが,同期接続を行なうPQconnectdbの場合(※)は以下の二種類がある.CONNECTION_OK正常CONNECTION_BAD異常PQconnectDBを実行したあと,さまざまな要求をサーバに投げる前には,PQstatusで接続情報を確認する必要がある.※ サーバへの接続要求が(成功,失敗を問わず)完了するまでブロックする. 接続情報の取得 PGconnには接続状況だけでなく,サーバやユーザ名などの接続情報が格納されている.PGconnの内部は直接参照できないので,取り出したい情報に応じた関数を使用する.以下に上記のサンプルで使用した接続情報取得関数の一覧を示す.サーバからのエラーメッセージ以下の関数は,引数で渡す接続においてサーバから最後に取得したDB操作のエラーメッセージを返す.char PQerrorMessage(const PGconn … More 接続

Debian GNU/Linux(Sarge)でqmailをインストールしてみる

恥ずかしながら私はメールサーバを構築したことがない.これではいろんな実験,検証をする際に不便な場合があるので,構築してみた.構築概要競合するパッケージのアンインストールucspi-tcpのインストールucspi-tcpのビルドqmail-srcパッケージのインストールqmailのビルド競合するパッケージに連座したパッケージをインストールqmailの起動確認 構築概要 qmailのバイナリパッケージが見当たらないので(※),作業概要は以下のようになった.競合するパッケージのアンインストールソースパッケージucspi-tcp-srcのインストールucspi-tcpのビルドソースパッケージqmail-srcのインストールqmailのビルド競合するパッケージに連座したパッケージをインストールqmail-srcインストール時,同時にインストールを推奨されるのがucspi-tcp-src. 推奨ならいらないかと,無視していたが,ビルドされるqmailは,ucspi-tcpに依存するので,結果的にucspi-tcp-srcは推奨ではなく依存である.※ qmailのライセンス(変更した場合は,ソースとして配布しなければならない)とDebianのポリシー(qmailのディレクトリ構造などがDebianとしてはよろしくない)とが,うまくいかず,折衷案としたこうなったらしい. 競合するパッケージのアンインストール eximはqmailと競合するので,あらかじめ削除しておく. apt-get remove exim 私の環境では同時にat mailx postgresqlが消された. あとで入れ直す. ucspi-tcpのインストール ucspi-tcpのビルド qmailのビルド 競合するパッケージに連座したパッケージをインストール qmailの起動確認