[Ruby]プログラムの2重起動を防ぐ方法を教わったら勉強になった件

ruby_icon_314

rubyで特定のプログラムの2重起動を防ぐ方法を先輩から教わったのでエントリーです。

てゆーか、プログラミング言語によっては2重起動を防ぐようなクラスが標準で実装されてたりするんですね。
あんまりよく知らないですが。。

いずれにせよ、一般的に他のプロセスから既にプログラムが呼ばれて実行中かどうかは、ロックファイルを作って判定するのだそう。
ロックファイル? はぁ。。 と思ったのですが、単純なファイル有る無しではワナがひそんでいるようです。
ある意味学生がする勉強ノートなエントリーです。

ソース全体

こんな感じ

LOCKFILE = "lock_file_name"
def file_check
   # ファイルチェック
   if File.exist?(LOCKFILE)
      # pidのチェック
      pid = 0
      File.open(LOCKFILE, "r"){|f|
         pid = f.read.chomp!.to_i
      }
      if exist_process(pid)
         $logger.info("既に起動中のヤツがいるです")
         exit
      else
         $logger.error("プロセス途中で死んでファイル残ったままっぽいっす")
         exit
      end
   else
   # なければLOCKファイル作成
      File.open(LOCKFILE, "w"){|f|
         # LOCK_NBのフラグもつける。もしぶつかったとしてもすぐにやめさせる。
         locked = f.flock(File::LOCK_EX | File::LOCK_NB)
         if locked
            f.puts $$
         else
            $logger.error("lock failed -> pid: #{$$}")
         end
      }
   end
end

# プロセスの生き死に確認
def exist_process(pid)
   begin
      gid = Process.getpgid(pid)
      return true
   rescue => ex
      puts ex
      return false
   end
end

# 終了時のLOCKFILE削除
File.delete(LOCKFILE)

説明

LOCKFILEの有無を確認

 File.exist?(LOCKFILE)

まず、プログラム開始時に誰かが作ったLOCKFILEがないか確認します
なければ誰もいませんね? っつーことでLOCKFILEで痕跡を残して処理に突っ走る。
あれば、本当にいるんですか?の確認
なお、プロセスも見るようにした(後述)ので、LOCKFILEを作成する際にpidを中に記入してます。

ロックファイルある場合のプロセスチェック

pidの有無もみます

pid = 0
File.open(LOCKFILE, "r"){|f|
   pid = f.read.chomp!.to_i
}

そのpidは今実行中か確認する
def exist_process(pid)
   begin
      gid = Process.getpgid(pid)
      return true
   rescue => ex
      puts ex
      return false
   end
end

前のヤツが残したpidを見てみる⇒あれば、本当に実行中。なければ、死んでるけどロックファイルが残ったままになってる。

      if exist_process(pid)
         $logger.info("既に起動中のヤツがいるです")
         exit
      else
         $logger.error("プロセス途中で死んでファイル残ったままっぽいっす")
         # 必要に応じてkill
      end

注意
普通に考えればそうなんですが、今回私もエラーハンドリングがイケてなくてて処理の途中でコケたけどLOCKFILEが残ったままになってた事がありました。
アプリによっては、そこでkillして作業に突き進むケースもあるのかな。

LOCKFILE作成して処理を開始する場合

LOCKFILEを作成
File.open(LOCKFILE, "w"){|f|
   locked = f.flock(File::LOCK_EX | File::LOCK_NB)
    if locked
      f.puts $$
    else
      $logger.error("lock failed -> pid: #{$$}")
    end
}

注意
ここで、File.openでpidを書いたファイルを作るだけでなくflockも必要との事。
なんか、そうしないとやっぱりカブる時があるらしい。
けど、見ればわかるように、Fileの{}を抜けるとLOCK_EXはなくなります。

処理の間もLOCKしておけばいいんじゃねーの?って最初思いましたが、どうも違うらしい。
途中でFileをdeleteせずにコケたりすると、排他制御かかったLOCKFILEは誰にも削除できなくなるから、
最悪OSのリブート(!)とかになるらしい。
怖い怖い。

つーわけで、LOCKしたらf.putsでpidだけ記入してすぐに抜けてるってゆー。

処理終了時は

File.delete(LOCKFILE)

で、ファイルを削除しておさらば。

次に来た人はLOCKFILEがないので再びプログラムを実行するでしょう。

以上、こんな感じ。
勉強になりました。

2013-06-13 | Posted in RubyNo Comments » 


関連記事

Comment





Comment



*