Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

RubyのUNIXデーモンの理論的紹介

Unixデーモンは、バックグラウンドで実行されるプログラムです。 Nginx、Postgres、OpenSSHはいくつかの例です。彼らはいくつかの特別なトリックを使用してプロセスを「切り離し」、どの端末からも独立して実行できるようにします。

私はいつもデーモンに魅了されてきました-おそらくそれは名前です-そしてそれらがどのように機能するかを説明する投稿をするのは楽しいだろうと思いました。具体的には、Rubyでそれらを作成する方法。

...しかし最初に。

自宅でこれを試さないでください!

おそらくデーモンを作りたくないでしょう。仕事を成し遂げるもっと簡単な方法があります。

バックグラウンドで実行されるプログラムを作成することをお勧めします。問題ない。 OSには、通常のプログラムをバックグラウンドで実行できるシステムが用意されています。

ubuntuでは、これはsystemdのUpstartを介して実行されます。 OSXでは起動されます。他にもあります。しかし、それらはすべて同じ概念に従って機能します。長時間実行プログラムを開始および停止する方法をシステムに指示する構成ファイルを提供します。それなら...まあ、それはほとんどそれです。 service my_app startなどのシステムコマンドを使用してプログラムを開始できます バックグラウンドで実行されます。

つまり、起動はシンプルで信頼性が高く、昔ながらのデーモンは難解で、正しく理解するのが非常に困難です。

...しかし、その場合、なぜデーモンについて学ぶ必要があるのでしょうか。まあ、楽しいから!そして、その過程でUnixプロセスに関するいくつかの興味深い事実を学びます。

最も単純なデーモン

デーモンを作成しないように言われたので、デーモンを作成しましょう。 Ruby 1.9の時点では、これは非常に簡単です。 Process.daemonメソッドを使用するだけです。

# Optional: set the process name to something easy to type<br>$PROGRAM_NAME = "rubydaemon"<br>
# Make the current process into a daemon
Process.daemon()

# Once per second, log the current time to a file
loop do
  File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) }
  sleep(1)
end

このスクリプトを実行すると、制御がコンソールに戻ります。ログを調整すると、予想どおりにタイムスタンプが毎秒追加されていることがわかります。

RubyのUNIXデーモンの理論的紹介

とても簡単でした。しかし、それでもデーモンがどのように機能するかについては説明していません。 本当に デーモン化を手動で行う必要があることを理解してください。

親プロセスの変更

bashを使用して通常のプログラムを実行する場合、そのプログラムのプロセスはbashの子です。ただし、デーモンの場合、デーモンをどのように起動するかは問題ではありません。それらの親プロセスは、常にOSによって提供される「ルート」プロセスです。

これは、デーモンの親IDを確認することでわかります。デーモンの親IDは常に1です。以下の例では、pstreeを使用してこれを示しています。

$ pstree
-+= 00001 root /sbin/launchd
 |--- 72314 snhorne rubydaemon

興味深いことに、これは「孤立したプロセス」の外観でもあります。孤立したプロセスは、親が終了した子プロセスです。

したがって、デーモンを作成するには、プロセスを意図的に孤立させる必要があります。以下のコードはこれを行います。

# Optional: set the process name to something easy to type
$PROGRAM_NAME = "rubydaemon"

# Create a new child process and exit the parent. This "orphans"
# our process and creates a daemon. 
exit if fork()

# Once per second, log the current time to a file
loop do
  File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) }
  sleep(1)
end

forkを呼び出すと、2つのプロセスが同じコードを実行します。元のプロセスは、新しいプロセスの親です。 Forkは、親には真の値を返し、子には偽の値を返します。したがって、exit if fork() 親を出るだけです。

現在のセッションからの切り離し

「デーモン化」コードにはいくつかの問題があります。プロセスを正常に孤立させますが、それでも端末のセッションの一部です。つまり、ターミナルを強制終了すると、デーモンも強制終了します。これを修正するには、新しいセッションを作成して再フォークする必要があります。 UNIXセッショングループに精通していませんか?これが優れたStackOverflowの投稿です。

# Optional: set the process name to something easy to type
$PROGRAM_NAME = "rubydaemon"

# Create a new child process and exit the parent. This "orphans"
# our process and creates a daemon. 
exit if fork

# Create a new session, create a new child process in it and 
# exit the current process. 
Process.setsid
exit if fork  

# Once per second, log the current time to a file
loop do
  File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) }
  sleep(1)
end

STDIN、STDOUT、およびSTDERRの再ルーティング

上記のコードにあるもう1つの問題は、既存のSTDOUTなどがそのまま残ることです。つまり、端末からデーモンを起動すると、デーモンがSTDOUTに書き込んだものはすべて端末に送信されます。良くない。

ただし、実際には、STDIN、STDOUT、およびSTDERRを任意のパスに再ルーティングできます。ここで、/ dev/nullに再ルーティングします。

# Optional: set the process name to something easy to type
$PROGRAM_NAME = "rubydaemon"

# Create a new child process and exit the parent. This "orphans"
# our process and creates a daemon. 
exit if fork

# Create a new session, create a new child process in it and 
# exit the current process. 
Process.setsid
exit if fork 

STDIN.reopen "/dev/null"       
STDOUT.reopen "/dev/null", "a"
STDERR.reopen '/dev/null', 'a' 


# Once per second, log the current time to a file
loop do
  File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) }
  sleep(1)
end
作業ディレクトリの変更

最後に、デーモンの作業ディレクトリは、デーモンを実行したときにたまたまあったディレクトリです。後でディレクトリを削除することを決定する可能性があるため、これはおそらく最善のアイデアではありません。それでは、ディレクトリを/に変更しましょう。

# Optional: set the process name to something easy to type
$PROGRAM_NAME = "rubydaemon"

# Create a new child process and exit the parent. This "orphans"
# our process and creates a daemon. 
exit if fork

# Create a new session, create a new child process in it and 
# exit the current process. 
Process.setsid
exit if fork 

STDIN.reopen "/dev/null"       
STDOUT.reopen "/dev/null", "a"
STDERR.reopen '/dev/null', 'a' 

Dir.chdir("/")

# Once per second, log the current time to a file
loop do
  File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) }
  sleep(1)
end

これは前に見たことがありませんか?

この一連の手順は、基本的に、Process.daemonメソッドがRubyコアに追加される前にすべてのRubyデーモンが実行しなければならなかったことです。 ActiveSupport拡張機能からRails4.xで削除されたProcessモジュールに1行ずつコピーしました。その方法はここで確認できます。


  1. Rubyのニューラルネットワーク:それほど怖くない紹介

    この投稿では、ニューラルネットワークの基本と、Rubyを利用してそれらを実装する方法を学びます。人工知能とディープラーニングに興味があるが、開始方法がわからない場合は、この投稿が最適です。重要な概念を強調するために、簡単な例を見ていきます。 Rubyを使用して多層ニューラルネットワークを作成することはほとんどありませんが、単純さと読みやすさのために、何が起こっているのかを理解するための優れた方法です。まず、一歩下がって、どうやってここにたどり着いたかを見てみましょう。 映画ExMachniaの静止画。写真クレジット Ex Machinaは2014年に公開された映画です。Googleでタ

  2. Ruby2.6の9つの新機能

    Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,