[Top] [CGI研究室]
ロックについて

ロックの説明とサンプル

  1. ロックの必要性
    ロックとは、複数のCGIが同一のファイルなどに アクセスした場合に、アクセス制限を行なう機能の事です。

    WEBサーバー上ではCGIが常に1つしか動かないわけではありません。
    複数の人が同じCGIを同時にアクセスすれば、同じCGIが同時に複数動きます。
    複数動作したCGIが同時に同一のファイルを読むだけであれば特に問題ありませんが、 同時に同一のファイルに対して書き込みを行なってしまうと、 正常に書き込まれない場合がありえます。
    また、あるCGIが書き込んでいる途中のファイルを別のCGIが読んでしまった場合には、 正常な状態のファイルを読めない可能性もあります。
    ※1枚の紙に対して複数の人が他の人の事も考えずに同時に字や絵を描くのと一緒です。

    ロックを行ない、1つのCGIがファイルにアクセスしている間は 他のCGIがファイルにアクセスできない様にすれば、 ファイルに対して正常な書き込みが行なえる様になります。

  2. ロックの手順
    どのようなロックを行なうかは、作成するCGIより異なります。
    以下に一般的なロックの手順を示します。

    概要:ロック時は一定時間待機する
    1. ロック時
      1. ロック&ロック判断
        ・ロック処理を行い、ロックが成功した否かを判断する。
      2. ロックが失敗した場合
        ・特定の間隔(1秒以下)ごとに再度「ロック&ロック判断」を行い、ロックが成功するまで繰り返す。
        ・一定時間(5〜10秒程度)が経過してもロックが解除されない場合はロックエラーとする。  (エラーを表示してプログラム終了等)
    2. ロック解除時
      1. ロック解除
        ・ロックの解除を行なう。
        (注)ロックを行なった場合は必ずロック解除を行なう必要があります。

  3. ロックの種類
    ロックの種類を以下に示します。

    1. flock(対象ファイルを直接ロック)
      ファイルロック関数flockを使用して対象のファイルを直接ロックする。
      • 特徴
        ・速い。
        ・アンロックし忘れが無い。
        ・ファイル毎のロックに便利。
        ・処理全体をロックする場合には不向き。
        ・flockが使えない環境もある。

    2. flock(ロック用ファイルをロック)
      ファイルロック関数flockを使用して別途用意したロック用ファイルをロックする。
      • 特徴
        ・速い。
        ・アンロックし忘れが無い。
        ・処理全体をロックする場合に便利。
        ・ロック用ファイルが常に残る。
        ・サーバーによってflockが使えない場合がある。

    3. symlink
      シンボリックリンク関数symlinkを使用してロック用のシンボリックリンクを作成し、 シンボリックリンクの有無によりロック状態を判断する。
      • 特徴
        ・遅い。
        ・ロックしたままの状態(ロック用シンボリックリンク)が残る可能性がある。
        ・処理全体をロックする場合に便利。
        ・サーバーによってsymlinkが使えない場合がある。

    4. mkdir
      ディレクトリ作成関数mkdirを使用してロック用のディレクトリを作成し、 ディレクトリの有無によりロック状態を判断する。
      • 特徴
        ・遅い。
        ・ロックしたままの状態(ロック用ディレクトリ)が残る可能性がある。
        ・処理全体をロックする場合に便利。
        ・どの環境でも使用できる。

    どのロックを使用するかは自由ですが、個人的には「flock(ロック用ファイルをロック)」がお勧めです。
    但し、サーバーによってflockが使えない場合があるので、flockが使えない場合は「mkdir」のロックを使うようにするのがよいでしょう。
    flockが使えるか否かはevalで判断できます。
    例)
    eval { flock( LOCKCHK, 8 ) ; } ;
    if ( ! $@ )
    {
    	flockが使える
    }
    else
    {
    	flockが使えない
    }
    

  4. ロックのサンプル
    ロックのサンプルを以下に示します。
    ※flockが使用可能な場合は、ロックが解除された後にもlock1.datが残ります。

    
    $LockFile0	= './lock0.dat';	# ロックファイル名(mkdir、symlink用)
    $LockFile1	= './lock1.dat';	# ロックファイル名(flock用)
    
    $LockMode = 0 ;				# ロックモード(0:mkdir、1:flock、2:symlink)
    eval { flock( LOCKCHK, 8 ) ; } ;
    if ( ! $@ )
    {
    	$LockMode = 1 ;
    }
    else
    {
    	eval { symlink("",""); } ;
    	if ( ! $@ )	{ $LockMode = 2 ; }
    }
    
    $LockFlg = 0 ;			# ロックフラグ(0:ロックしていない,1:ロックしている)
      ・
      ・
      ・
    &Lock ;
    if ( $LockFlg )		# ロック成功?
    {
    	#ファイルアクセス処理
    }
    else
    {
    	#エラー処理
    }
    &Unlock ;
      ・
      ・
      ・
    
    #-----------------------------------------------------------------------#
    #	ロック
    #	 入力用変数:	$LockMode	…ロックモード
    #			$LockFile0	…ロックファイル名(mkdir、symlink用)
    #			$LockFile1	…ロックファイル名(flock用)
    #	 出力用変数:	$LockFlg	…ロックフラグ(0:ロックしていない,1:ロックしている)
    #			LOCK1		…ロックファイルハンドル(flock用)
    #-----------------------------------------------------------------------#
    sub Lock
    {
    	local($i) ;
    
    	$LockFlg = 0 ;
    
    	for( $i = 0; $i < 5; $i++ )
    	{
    		if ( $LockMode == 1 )
    		{
    			if ( open( LOCK1, ">$LockFile1" ) )
    			{
    				if ( flock( LOCK1, 6 ) )
    				{
    					$LockFlg = 1 ;
    					last ;
    				}
    				close( LOCK1 ) ;
    			}
    			else
    			{
    				last ;
    			}
    		}
    		elsif ( $LockMode == 2 )
    		{
    			if ( symlink(".", $LockFile0) )
    			{
    				$LockFlg = 1 ;
    				last ;
    			}
    		}
    		else
    		{
    			if ( mkdir( $LockFile0, 0755 ) )
    			{
    				$LockFlg = 1 ;
    				last ;
    			}
    		}
    		sleep(1) ;	# 1秒Sleep
    		# sleep時間を短くする場合は下記のコードに変更
    		# select(undef, undef, undef, 0.5) ;	# 0.5秒Sleep
    	}
    }
    
    #-----------------------------------------------------------------------#
    #	アンロック
    #	 入力用変数:	$LockFlg	…ロックフラグ(0:ロックしていない,1:ロックしている)
    #			$LockMode	…ロックモード
    #			$LockFile0	…ロックファイル名(mkdir、symlink用)
    #			LOCK1		…ロックファイルハンドル(flock用)
    #	 出力用変数:	$LockFlg	…ロックフラグ(0:ロックしていない)
    #-----------------------------------------------------------------------#
    sub Unlock
    {
    	if ( $LockFlg )
    	{
    		if ( $LockMode == 1 )
    		{
    			flock( LOCK1, 8 ) ;
    			close( LOCK1 ) ;
    		}
    		elsif ( $LockMode == 2 )
    		{
    			unlink( $LockFile0 ) ;
    		}
    		else
    		{
    			rmdir( $LockFile0 ) ;
    		}
    		$LockFlg = 0 ;
    	}
    }
    


CGI Saloon