Last active
          November 2, 2024 00:52 
        
      - 
            
      
        
      
    Star
      
          
          (105)
      
  
You must be signed in to star a gist  - 
              
      
        
      
    Fork
      
          
          (1)
      
  
You must be signed in to fork a gist  
- 
      
 - 
        
Save Shinpeim/4736099 to your computer and use it in GitHub Desktop.  
Revisions
- 
        
Shinpeim revised this gist
Mar 22, 2013 . 1 changed file with 0 additions and 17 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -10,20 +10,3 @@ URL 変わっちゃうの申し訳ないんだけど、一覧性が高くなる この文書は\*nix系のシステムにおけるプロセスやシグナルなどについて説明することを目的に書かれています。「プロセスとかよくわかってないからちゃんと知りたいな」みたいなひとたちが想定読者です。あと、まとめることによって自分の知ってることの棚卸しをするという目的もあります。  - 
        
Shinpeim revised this gist
Mar 22, 2013 . No changes.There are no files selected for viewing
 - 
        
Shinpeim revised this gist
Mar 22, 2013 . 1 changed file with 8 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,11 @@ # 重要 サイズがあまりに大きくなってしまったので、gist ではなくて github 上で管理するようにしました。 https://github.com/Shinpeim/process-book URL 変わっちゃうの申し訳ないんだけど、一覧性が高くなるのと pull req が受け取れるメリットのほうを取ります。せっかく読みにきてくれたのにひと手間かかっちゃってすみません。 # この文書の目的 この文書は\*nix系のシステムにおけるプロセスやシグナルなどについて説明することを目的に書かれています。「プロセスとかよくわかってないからちゃんと知りたいな」みたいなひとたちが想定読者です。あと、まとめることによって自分の知ってることの棚卸しをするという目的もあります。  - 
        
Shinpeim revised this gist
Mar 20, 2013 . 1 changed file with 1 addition and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,6 @@ # この文書の目的 この文書は\*nix系のシステムにおけるプロセスやシグナルなどについて説明することを目的に書かれています。「プロセスとかよくわかってないからちゃんと知りたいな」みたいなひとたちが想定読者です。あと、まとめることによって自分の知ってることの棚卸しをするという目的もあります。 # 目次(予定)  - 
        
Shinpeim revised this gist
Mar 20, 2013 . 1 changed file with 8 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,18 +6,18 @@ # 目次(予定) [第一回:導入](https://gist.github.com/Shinpeim/4994708) [第二回:プロセスの生成](https://gist.github.com/Shinpeim/4994709) [第三回:プロセスとファイル入出力](https://gist.github.com/Shinpeim/4994710) [第四回:ファイルディスクリプタ](https://gist.github.com/Shinpeim/4994711) [第五回:preforkサーバーを書く](https://gist.github.com/Shinpeim/4994713) [第六回:ゾンビプロセスと孤児プロセス](https://gist.github.com/Shinpeim/5056638) [第七回:シグナル と kill](https://gist.github.com/Shinpeim/5194454) [第八回:プロセスグループ と フォアグランドプロセス](https://gist.github.com/Shinpeim/5205353)  - 
        
Shinpeim revised this gist
Mar 20, 2013 . 1 changed file with 3 additions and 17 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,22 +6,6 @@ # 目次(予定) [第一回](https://gist.github.com/Shinpeim/4994708) [第二回](https://gist.github.com/Shinpeim/4994709) @@ -34,4 +18,6 @@ [第六回](https://gist.github.com/Shinpeim/5056638) [第七回](https://gist.github.com/Shinpeim/5194454) [第八回](https://gist.github.com/Shinpeim/5205353)  - 
        
Shinpeim revised this gist
Mar 19, 2013 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -32,4 +32,6 @@ [第五回](https://gist.github.com/Shinpeim/4994713) [第六回](https://gist.github.com/Shinpeim/5056638) [第七回](https://gist.github.com/Shinpeim/5194454)  - 
        
Shinpeim revised this gist
Feb 28, 2013 . 1 changed file with 7 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,7 +23,13 @@ ファイルが増えてきてgist上で表示されなくなったので、分けました。 [第一回](https://gist.github.com/Shinpeim/4994708) [第二回](https://gist.github.com/Shinpeim/4994709) [第三回](https://gist.github.com/Shinpeim/4994710) [第四回](https://gist.github.com/Shinpeim/4994711) [第五回](https://gist.github.com/Shinpeim/4994713) [第六回](https://gist.github.com/Shinpeim/5056638)  - 
        
Shinpeim revised this gist
Feb 20, 2013 . 10 changed files with 11 additions and 784 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -16,4 +16,14 @@ * シグナル * スレッドについて * プロセスとの違い * リソースの共有について # 本文 ファイルが増えてきてgist上で表示されなくなったので、分けました。 [第一回](https://gist.github.com/Shinpeim/4994708) [第二回](https://gist.github.com/Shinpeim/4994709) [第三回](https://gist.github.com/Shinpeim/4994710) [第四回](https://gist.github.com/Shinpeim/4994711) [第五回](https://gist.github.com/Shinpeim/4994713) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,36 +0,0 @@ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,166 +0,0 @@ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,199 +0,0 @@ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,170 +0,0 @@ Binary file not shown.Binary file not shown.Binary file not shown.Binary file not shown.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,212 +0,0 @@  - 
        
Shinpeim revised this gist
Feb 20, 2013 . 1 changed file with 212 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,212 @@ # 今日のお題:preforkサーバーを作ってみよう さて、前回は、fork するとファイルディスクリプタ(以下fdと表記)は複製されるけどオープンファイル記述は共有されるというのを見ました。これを利用して、preforkモデルのサーバーを実際に書いてみましょう。 # tcp socketはファイルである 以前見たとおり、Linuxではプロセスの入出力はファイルを通じて行います。とうぜん、ネットワークごしに入出力する tcp socket もファイルです。ここで「ファイルです」が意味するのは、プロセスがソケットを通じて入出力をしようと思えば、socket の fd を通じて write や read を行うということですね。では、実際に socket がファイルであるところを見てみましょう # -*- coding: utf-8 -*- require "socket" # 12345 portで待ち受けるソケットを開く listening_socket = TCPServer.open(12345) # ソケットもファイルなので fd がある p listening_socket.fileno # ひとまずなにもせず閉じる listening_socket.close 上記のような Ruby スクリプトを実行してみると、openしたソケットがfdを持つことが確認できるかと思います。 # クライアントの接続を受け入れる 今は socket を開いてなにもせずにすぐ閉じてしまいましたが、今度はクライアントの接続を受け入れてしてみましょう。 # -*- coding: utf-8 -*- require "socket" # 12345 portで待ち受けるソケットを開く listening_socket = TCPServer.open(12345) p listening_socket.fileno # acceptでクライアントからの接続を待つ # 接続されるまでブロックする puts "accepting..." socket = listening_socket.accept puts "accepted!" # 接続されると新しいsocketが作られる # このsocketを通じてクライアントと通信する # あたらしいsocketなのでfdの番号がlistening_socketと違う p socket.fileno # なにもせずクライアントとのコネクションを切る socket.close # 待ち受けソケットも閉じる listening_socket.close 上記のような Rubyスクリプトを適当な名前で作って、実行してみましょう。listening_socket の fd が出力されたあとに、accepting…と出力されて、そこで止まってしまいプロンプトが帰ってこないかと思います。なぜこういう動きをするか、いままでこのドキュメントを読み進めてきたみなさんはもう理解できますね。listen している socket で accept を呼び出すと、プロセスはそこでブロックして、クライアントからの接続を待ちます。そこでブロック中になっているため、プロセスがそれ以上進まないわけですね。 では、今度はそのままターミナルをもうひとつ開いて、ここにコネクションを貼ってみましょう。 # べつのターミナルで $ telnet localhost 12345 上記のように、 telnet コマンドで、さっきのプロセスが待ち受けてる 12345 ポートに接続してみましょう。一瞬で接続が切られてしまうかと思います。 一方、今度はさっきプロンプトが返ってこないままになっていたターミナルを再度見てみてください。 accepted! のあとに、listening_socket の fd とはまた違う数字の fd が出力されて、プロンプトが返ってきたかと思います。これは、telnetでクライアントから接続されたことにより、accept の行でブロック中になっていたプロセスが動き出したためです。accept はクライアントから接続されるとブロック中から抜け出し、新しい socket を作り出して返します。サーバーのプロセスは、この新しい socket を通じてクライアントと通信をします。この socket にたいして write をすればクライアントへデータを送ることになるし、この socket から read をすれば、クライアントからの入力を受け取るという感じですね。とうぜん、この socket を close するとクライアントとのコネクションは切断されます。 今回はなにもせずに socket を close したので、クライアント側(telnetコマンドを打った側)ではすぐにサーバーからコネクションが切られてしまったわけですね。 # クライアントから送られてくるデータを読み込む さっきはなにもせず socket を close してしまいましたが、今度はクライアントからデータが送られてきたらそれを読む、という動きにしてみましょう。 # -*- coding: utf-8 -*- require "socket" listening_socket = TCPServer.open(12345) # クライアント受け入れ無限地獄 loop do puts "accepting..." socket = listening_socket.accept puts "accepted a client!" # クライアントからの入力受け取り無限地獄 loop do # クライアントからの入力を1行読む # 入力されるまでブロックする line = socket.gets line.gsub!(/[\r\n]/,"") #改行コード捨てる # exitと入力されてたらソケット閉じてループを抜ける if line == "exit" socket.close puts "closed a connection!" break end # そうでなければ標準出力に出力 puts line end end はい、ちょっと書き換えてみました。 ターミナルを立ち上げて、これを実行してみましょう。このターミナル上で動いてるのがサーバープロセスになります。今は accepting… が出力されたところでプロセスがブロックしてると思います。ここまではさっきとおなじですね。では、またべつのターミナルを開いて、telnetコマンドでサーバープロセスに接続してみましょう。 $ telnet localhost 12345 今度は切断されないと思います。 ではまたサーバープロセスが走ってるほうのターミナルを見てみましょう。"accepted a client"と出力されて、そこでプロセスがブロックしていると思います。line = socket.gets のところで、クライアントからのデータを読み込もうとしていますが、クライアントがまだなにもデータを送っていないのでここでブロックしているわけですね。 では今度は telnet のほうのターミナルで、なんかを入力して、改行してみましょう。 再度サーバープロセスのほうを見てみると、今 telnet で入力した一行が、標準出力に書き出されているのが見て取れると思います。 では telnet のほうに戻って(何度も往復してたいへんだ!)、今度は exit と入力して改行してみましょう。すると、サーバープロセスが socket を close したことにより、接続が切れるかと思います。 サーバープロセスのほうを見てみると、"closed a connection!" が出力されたあと、また "accepting…" が出力されて、ブロックしてると思います。これは、break でクライアントからの入力受け取り無限地獄を抜けたはいいけれど、今度はクライアント受け入れ無限地獄loopによりまた listening_socket.accept しているところでブロックしてるわけですね。 動きを確認したら、サーバープロセスのほうで Ctrl + C を入力して、プロセスを終了してあげましょう。 いまは puts line で標準出力にクライアントからの入力を出力していますが、この行を socket に対する書き込みにすれば、いわゆるエコーサーバーとして動くプロセスになります。そのあたりは宿題とするので、自分で書き換えて動きを見てみてください。 # このサーバーは出来損ないだ、たべられないよ さて、これでクライアントから接続を待って、クライアントに接続されたらそのクライアントとネットワーク越しに入出力することができました。しかし、このサーバーには欠陥があります。わかりますか? そう、このままでは、同時にひとつのクライアントしか処理できないのです。クライアントからの接続を accept したあとは、このプロセスは「クライアントの入力受け取り無限地獄」にいます。その無限地獄にいる限り、このプロセスは次の listening_socket.accept に到達することはありません。なので、「クライアントの入力受け取り無限地獄」を抜けるまでは新しく接続してこようとするクライアントを受け入れることができないのです。これは困りましたね。 じっさい、このサーバープロセスを立ち上げた状態で、さらにターミナルをふたつ立ち上げて、両方で $ telnet localhost 12345 をしてみると、先に telnet したほうは普通に動くのだけれど、もういっこのほうはいくら入力してもサーバープロセスがうんともすんとも言わないのが見て取れると思います。 # 明日の同じ時間にここに来てください。本当のサーバーってやつを見せてあげますよ べつに用意する食材もないので、明日の同じ時間を待つ必要はありません。すぐにコードを書き換えてしまいましょう。 # -*- coding: utf-8 -*- require "socket" number_of_workers = 3 listening_socket = TCPServer.open(12345) number_of_workers.times do pid = fork if pid # 親プロセスは次々に fork で子プロセスを作る next else # 子プロセス # クライアント受け入れ無限地獄 loop do puts "accepting..." # 子プロセスは全部ここでブロックする。 socket = listening_socket.accept puts "accepted a client!" # クライアントの入力受け取り無限地獄 loop do line = socket.gets line.gsub!(/[\r\n]/,"") if line == "exit" socket.close puts "closed a connection!" break end puts line end end end end # 子プロセスは無限ループしてるからここには届かない # 親プロセスでは子プロセスの終了を待ち続ける Process.waitall listening_socket を作ったあとに、3回 fork するようにしてみました。親プロセスでは fork したあとに何もしないで子プロセスの終了を待ちます。一方、子プロセスでは、 accept を呼んでブロックしていますね。 さて、ここで前回の内容が役に立ちますよ。 listening_socket はファイルでした。そのため、fd を持ちます。そして、forkした場合、fd は複製されるけど、複製された fd は複製もとと同じオープンファイル記述を参照しているのでしたね。 というわけで、今、listening_socket を作ったあとに fork したことで、12345 portで待ち受ける同じソケットを全てのプロセスで共有している状態になっているわけです。ここまではいいですか? そして、親プロセスではその listening_socket に対して何もせず、子プロセスで accept していますね。この3つの子プロセスは、クライアントからの接続を獲得して、新しい socket を作ろうと身構えてブロックしている状態なわけです。ここで、あるクライアントが 12345 ポートに対して接続してきたとしましょう。なにが起こりますか? 3つの子プロセスは、それぞれがクライアントからの接続を受け入れて新しい socket を作ろうとしますが、オープンファイル記述が共有されているため、クライアントからの接続を受け入れられるのはたったひとつの子プロセスだけです。前回の内容を思い出して下さい。file を open したあと fork して、両方のプロセスでその file を読み込んだ場合、片方のプロセスでしか読み込むことができていなかったと思います。これと同じことが accept でも起こっているわけですね。 さて、首尾よく accept できて新しいソケットを獲得した子プロセスは、クライアントからの入力受け取り無限地獄へと突入します。というわけで、今接続してきたクライアントとのやり取りは、この子プロセスにまかせることができました。一方、残念ながら accept できなかった他の子プロセスは、さっきとおなじところでブロックしたままです。 さて、ここに、さらに新しいクライアントが接続してきた場合はどうなるでしょうか?accept でブロック中になっている、他の子プロセスがそのクライアントを accept してくれるわけですね。 こんなふうに、あらかじめ子プロセスをいくらか fork しておいて、その子プロセスでクライアントからの接続を受け入れて処理するような仕組みを、「prefork」といいます。先に(pre)forkしておくサーバーってことですね。 さて、これで無事、同時に複数のクライアントからの接続を受け入れることが可能になりました。今回は 3 回forkしたので、同時接続数は 3 つまでですね。サーバープロセスの他にもターミナルをたくさん立ち上げて、それぞれで telnet localhost 12345 してみてください。3つまでは同時に処理できるけど、4つを超えると同時に処理できてないことが見て取れるかと思います。 今までの話で、preforkサーバーが書けて、さらにどうしてそんなことが可能なのかも理解できましたね! # preforkの利点、欠点 さて、上に見たように、prefork サーバーはひとつのプロセスがひとつのクライアントを受け持つようなアーキテクチャになっています。このことは、いくつかの利点と欠点をもたらします。 まず、上に見たように、worker(子プロセス)の数が少ないと、同時に処理できるクライアントの数が少なくなってしまいます。同時処理の数を増やしたければ、その数だけプロセスを生成する必要があるわけです。プロセスが生成されればそのぶんだけメモリーは消費されるので、この方法は意外と効率がよくないんですね。 一方で、アーキテクチャが単純なので、コードが追いやすいです。シンプルであることは、とても大切なことです。さらに、ひとつのクライアントがひとつのプロセスで処理されるためたとえばプロセスが暴走したとかそういうときの影響範囲がせまくなります。ひとつのプロセス内でたくさんのクライアントを受け持つと、そのプロセスがなんかおかしなことになったときに影響範囲が大きくなって困りますね。 # 次回予告 次回はちょっと話を戻して、forkした際に親が先に死んだり終わったりしたらどうなるのとかそういう話をしたいなって思います。  - 
        
Shinpeim revised this gist
Feb 13, 2013 . 1 changed file with 4 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -69,6 +69,8 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら これで、たとえばpidが100番のプロセスから「5番のファイルの、次の行読み込んでよ」と言われても、「ふぇぇ」ってならずに、「100番のプロセスさんの5番の番号札に紐づいたメモはこれだな」「メモには/path/to/fileの3行目まで読み込んだって書いてあるな」「じゃあこのファイルの4行目を読み込めばいいね!」「はいできた!」と言ってデータを返すことができるわけですね! このあたりのイメージは、このファイルの下に attach してある 004_01.png と 004_02.pngを見てみてください。 # ファイルディスクリプタ/オープンファイル記述とfork さて、では、forkしたとき、ファイルディスクリプタやオープンファイル記述はどうなるのでしょうか? @@ -156,6 +158,8 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら 実行してみると、親プロセスがすでに番号札をOSに返してしまっても、子プロセスは複製された番号札を持っているので問題なくファイル操作ができているのが見て取れると思います。 このあたりのイメージは、このファイルの下にattachしてある 004_03.png と 004_04.png を見てみてください。 ## どうするのがベストプラクティスなの? すでにfileがopenされている状態でforkすると、以上に見たように予期せぬ動作で混乱することがあります。そのため、forkした場合、親プロセスで使わないファイルは親プロセスですぐ閉じる、子プロセスで使わないファイルは子プロセスですぐ閉じるとすると、最も問題が起きにくいと思います。子プロセスでファイルを閉じたとしても、親プロセスでファイル使いたい場合に問題なく扱える(またはその逆も)のは、上に見た通りですからね  - 
        
Shinpeim revised this gist
Feb 13, 2013 . 4 changed files with 0 additions and 0 deletions.There are no files selected for viewing
LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed.LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed.LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed.LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed. - 
        
Shinpeim revised this gist
Feb 13, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -67,7 +67,7 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら このとき、この「ファイルの状況どうなってるっけメモ」にあたるのが、オープンファイル記述と呼ばれるものです。OSは、「ファイル開いて」っていうシステムコールを受け取ると、オープンファイル記述を作り出して自分で保持しておきます。さらに、システムコールを送ってきたプロセスのidに対して、新しい番号札(ファイルディスクリプタ)を返します。このとき、オープンファイル記述とプロセスidと番号札の関連も、自分の中に保持しておきます。 これで、たとえばpidが100番のプロセスから「5番のファイルの、次の行読み込んでよ」と言われても、「ふぇぇ」ってならずに、「100番のプロセスさんの5番の番号札に紐づいたメモはこれだな」「メモには/path/to/fileの3行目まで読み込んだって書いてあるな」「じゃあこのファイルの4行目を読み込めばいいね!」「はいできた!」と言ってデータを返すことができるわけですね! # ファイルディスクリプタ/オープンファイル記述とfork  - 
        
Shinpeim revised this gist
Feb 13, 2013 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -58,7 +58,8 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら、代わりにやってあげること」です。そのためには、OSは実際のdiskの読み書きの他に、少なくとも以下の仕事をしないといけません。 * プロセスに「ファイル開いて」って言われたら開いてあげる * ファイルを開いたら、そのファイル専用の「ファイルの状況どうなってるっけメモ」を作る * 開いたファイルの情報(書き込みモードなのか読み込みモードなのか、とか、ファイルパスはどこなのかとか、どこまで読み込んだかあるいは書き込んだかとか)を「ファイルの状況どうなってるっけメモ」に書いておく * プロセスのために番号札を作って、さっき書いた「ファイルの状況どうなってるっけメモ」がどのプロセスのどの番号札のものなのかを覚えておく * プロセスに番号札を貸してあげる  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -66,7 +66,7 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら このとき、この「ファイルの状況どうなってるっけメモ」にあたるのが、オープンファイル記述と呼ばれるものです。OSは、「ファイル開いて」っていうシステムコールを受け取ると、オープンファイル記述を作り出して自分で保持しておきます。さらに、システムコールを送ってきたプロセスのidに対して、新しい番号札(ファイルディスクリプタ)を返します。このとき、オープンファイル記述とプロセスidと番号札の関連も、自分の中に保持しておきます。 これで、たとえばpidが100番のプロセスから「5番のファイルの、次の行読み込んでよ」と言われても、「ふぇぇ」ってならずに、「100番のプロセスさんの5番の番号札に紐づいたメモはこれだな」「じゃあこのファイルのここを読み込めばいいね!」「はいできた!」と言ってデータを返すことができるわけですね! # ファイルディスクリプタ/オープンファイル記述とfork  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -59,7 +59,7 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら * プロセスに「ファイル開いて」って言われたら開いてあげる * 開いたファイルの情報(書き込みモードなのか読み込みモードなのか、とか、ファイルパスはどこなのかとか、どこまで読み込んだかあるいは書き込んだかとか)を「ファイルの状況どうなってるっけメモ」に書いておく * プロセスのために番号札を作って、さっき書いた「ファイルの状況どうなってるっけメモ」がどのプロセスのどの番号札のものなのかを覚えておく * プロセスに番号札を貸してあげる 「ファイルの状況どうなってるっけメモ」を保持しておかないと、「次の行読み込んでよ」ってプロセスから言われたときに「ふぇぇ、次の行ってどこ〜〜〜〜〜」ってなっちゃいますよね。あるいは、どの「ファイルの状況どうなってるっけメモ」がどのプロセスの何番の番号札と紐づいているのかを覚えておかないと、あるプロセスが「5番の番号のやつに書き込んでよ」って言ってきても、「ふぇぇ、書き込みたいけどどのメモ見ればいいのか忘れちゃったよ〜〜〜」ってなっちゃいます。  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 25 additions and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,15 +4,29 @@ 前回はさらっと流してしまいましたが、実はプロセスは自分自身で実際にファイルを開いたりディスクに書き込んだりディスクからデータを読み出したりすることはありません。そういう低レイヤーの処理は、プロセスがシステムコールをOSに送ることで、OSが代わりに行ってくれます。そのあたりの話を、きちんと見て行きましょう。 さて、なにはともあれ、プロセスが入出力をしたいと思ったら、ファイルを開くところから始めないといけません。 * OSは、プロセスから「ファイルを開いてね」というシステムコールを受け取ると、実際にファイルを開きます * OSは、その開いたファイルを表す「番号札」作成します * OSは、その番号札をプロセスに対して返します。 さて、ファイルを開いたら、今度はそこになにかを書き込んでみましょうか * プロセスは、さっき受け取った「番号札」をつかって、「n番の番号札で表されてるファイルにこれ書き込んでおいて」っていうシステムコールを送ります。 * OSは、「番号札」で表された、すでに開かれているファイルに対して書き込みを行います じゃあ、今度はファイルを閉じましょう * プロセスは、不要になった番号札をcloseというシステムコールでOSに返却します * OSは、番号札が返却されたので、「もうこれは使わないんだな」と判断して、ファイルを閉じます と、こんな感じでファイルの入出力が行われているのですが、この「番号札」のことを、「ファイルディスクリプタ」と呼びます。実際、ファイルディスクリプタは整数値で表現されています。 例を見てみましょう。今回もRubyを使います。 # fd.rb file = File.open("nyan.txt","w") # openシステムコールでnyan.txtを書き込みモードでopen p file.fileno # fileno メソッドで、ファイルディスクリプタ(番号札)を取得 file.close #fileをclose 1行目で、openシステムコールをOSに対して送っています。正常にopenされると、ファイルディスクリプタを内部に持ったfileオブジェクトが生成されます。2行目で、fileオブジェクトが保持しているファイルディスクリプタを取得してターミナルに出力しています。3行目で、fileを閉じていますが、これはRubyが内部でfileオブジェクトが保持しているファイルディスクリプタを使って、OSにcloseシステムコールを送っているわけです。IO#readlineとかIO#writeメソッドなんかも、内部ではIOオブジェクトが保持しているファイルディスクリプタを使って、読み込みのためのシステムコールを送ったり書き込みのためのシステムコールを使ってりしているわけですね。 @@ -39,19 +53,20 @@ # オープンファイル記述 さて、今はプロセスの側からがファイルディスクリプタをどう扱っているかについて見てみましたが、今度はOSの側から見てみましょう。 OSのお仕事は、「プロセスからファイルの操作を頼まれたら、代わりにやってあげること」です。そのためには、OSは実際のdiskの読み書きの他に、少なくとも以下の仕事をしないといけません。 * プロセスに「ファイル開いて」って言われたら開いてあげる * 開いたファイルの情報(書き込みモードなのか読み込みモードなのか、とか、ファイルパスはどこなのかとか、どこまで読み込んだかあるいは書き込んだかとか)を「ファイルの状況どうなってるっけメモ」に書いておく * プロセスに番号札を作って、さっき書いた「ファイルの状況どうなってるっけメモ」がどのプロセスのどの番号札のものなのかを覚えておく * プロセスに番号札を貸してあげる 「ファイルの状況どうなってるっけメモ」を保持しておかないと、「次の行読み込んでよ」ってプロセスから言われたときに「ふぇぇ、次の行ってどこ〜〜〜〜〜」ってなっちゃいますよね。あるいは、どの「ファイルの状況どうなってるっけメモ」がどのプロセスの何番の番号札と紐づいているのかを覚えておかないと、あるプロセスが「5番の番号のやつに書き込んでよ」って言ってきても、「ふぇぇ、書き込みたいけどどのメモ見ればいいのか忘れちゃったよ〜〜〜」ってなっちゃいます。 このとき、この「ファイルの状況どうなってるっけメモ」にあたるのが、オープンファイル記述と呼ばれるものです。OSは、「ファイル開いて」っていうシステムコールを受け取ると、オープンファイル記述を作り出して自分で保持しておきます。さらに、システムコールを送ってきたプロセスのidに対して、新しい番号札(ファイルディスクリプタ)を返します。このとき、オープンファイル記述とプロセスidと番号札の関連も、自分の中に保持しておきます。 これで、たとえばpidが100番のプロセスから「5番の次の行読み込んでよ」と言われても、「ふぇぇ」ってならずに、「100番のプロセスさんの5番の番号札に紐づいたメモはこれだな」「じゃあこのファイルのここを読み込めばいいね!」「はいできた!」と言ってデータを返すことができるわけですね! # ファイルディスクリプタ/オープンファイル記述とfork @@ -95,7 +110,7 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら end read_file.close 子プロセスと親プロセスで、nyan.txtから一行ずつ入力を受け取っています。もしもforkされたときに「ファイルの状況どうなってるっけメモ」まで複製されているならば、親プロセスが一行読み込んだとき親プロセスの「ファイルの状況どうなってるっけメモ」は一行分進みますが、子プロセスの「ファイルの状況どうなってるっけメモ」は書き変わらないので、親プロセスでの読み込みは子プロセスでの読み込みに影響を与えないはずですね。つまり、親プロセスでも子プロセスでも、同じくファイルの内容をすべて読み込むことができるはずです。逆に、親と子が共通の「ファイルの状況どうなってるっけメモ」を参照しているならば、親プロセスで一行読み込んだら、共通の「ファイルの状況どうなってるっけメモ」が1行分進んでしまい、子プロセスではその行を読み込むことができなくなってしまいます。 では実際に確かめて見ましょう。nyan.txtに以下の内容を書き込んだ上で、fork_fd.rbを実行してみましょう  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 150 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,150 @@ # ファイルディスクリプタ さて、前回、プロセスがファイルを通じて外部との入出力する様を見て見ました。今回はさらにプロセスとファイル入出力について詳しく見てみましょう。 前回はさらっと流してしまいましたが、実はプロセスは自分自身で実際にファイルを開いたりディスクに書き込んだりディスクからデータを読み出したりすることはありません。そういう低レイヤーの処理は、プロセスがシステムコールをOSに送ることで、OSが代わりに行ってくれます。そのあたりの話を、きちんと見て行きましょう。 さて、なにはともあれ、プロセスが入出力をしたいと思ったら、ファイルを開くところから始めないといけません。OSは、プロセスから「ファイルを開いてね」というシステムコールを受け取ると、実際にファイルを開いたあと、そのファイルを表す「番号札」をプロセスに対して返します。プロセスは、ファイルから入力を受け取りたいときにはこの「番号札」をつかって、「n番の番号札で表されてるファイルから入力を読み込んでくれー」っていうシステムコールを送ったり、「n番の番号札で表されてるファイルにこれ書き込んでおいて」っていうシステムコールを送ったりすることで、OSにファイル入出力をお願いします。ファイルを閉じるときには、「n番の番号札で表されてるファイル、もう閉じていいよ」というシステムコールを送るさけですね。 この「番号札」のことを、「ファイルディスクリプタ」と呼びます。実際、ファイルディスクリプタは整数値で表現されています。 例を見てみましょう。今回もRubyを使います。 # fd.rb file = File.open("nyan.txt","w") # openシステムコールでnyan.txtを書き込みモードでopen p file.fileno # fileno メソッドで、ファイルディスクリプタを取得 file.close #fileをclose 1行目で、openシステムコールをOSに対して送っています。正常にopenされると、ファイルディスクリプタを内部に持ったfileオブジェクトが生成されます。2行目で、fileオブジェクトが保持しているファイルディスクリプタを取得してターミナルに出力しています。3行目で、fileを閉じていますが、これはRubyが内部でfileオブジェクトが保持しているファイルディスクリプタを使って、OSにcloseシステムコールを送っているわけです。IO#readlineとかIO#writeメソッドなんかも、内部ではIOオブジェクトが保持しているファイルディスクリプタを使って、読み込みのためのシステムコールを送ったり書き込みのためのシステムコールを使ってりしているわけですね。 さて、説明がすんだところで、実際にfd.rbを実行してみましょう。 $ ruby fd.rb 5 「nyna.txtが書き込みモードで開かれたもの」についてる番号札が、5番なのが確認できましたね。 # 標準入出力のファイルディスクリプタ さて、勘のいいひとはそろそろ例の標準入力は0、標準出力は1、標準エラー出力は2、という謎の数字の正体について、感付きつつあるのではないでしょうか。そうです。実は、「標準入力のファイルディスクリプタは0、標準出力のファイルディスクリプタは1、標準エラー出力のファイルディスクプタは2」なのです。実際に確かめてみましょう # std_fds.rb p $stdin.fileno # => 0 p $stdout.fileno # => 1 p $stderr.fileno # => 2 おー。 つまり、前回出てきた & という記号は、「ファイルパスじゃなくてファイルディスクリプタを指定してるぜ」という意味の記号だったわけですね!そして、なぜリダイレクトのときに標準入力や標準出力にあのような数字が使われているのかが理解できたと思います。 # オープンファイル記述 さて、今はプロセスの側からファイルディスクリプタについて見てみましたが、今度はOSの側から見てみましょう。 OSのお仕事は、「プロセスからファイルの操作を頼まれたら、代わりにやってあげること」です。そのためには、OSは実際のdiskの読み書きの他に、少なくとも以下の仕事をしないといけません。 * 「開いて」って言われたら開いてあげる * 開いたファイルの情報(書き込みモードなのか読み込みモードなのか、とか、ファイルパスはどこなのかとか、どこまで読み込んだかあるいは書き込んだかとか)を保持しておく「ファイルの状況どうなってるっけメモ」を書いておく * 「ファイルの状況どうなってるっけメモ」が、どのプロセスの何番の番号札とヒモづいているのかを覚えておく 「ファイルの状況どうなってるっけメモ」を保持しておかないと、「次の行読み込んでよ」ってプロセスから言われたときに「ふぇぇ、次の行ってどこ〜〜〜〜〜」ってなっちゃいますよね。あるいは、「ファイルの状況どうなってるっけメモ」が、どのプロセスの何番の番号札とヒモづいているのかを覚えておかないと、あるプロセスが「5番の番号のやつに書き込んでよ」って言ってきても、「ふぇぇ、書き込みたいけどメモが見つからなくてどのファイルのどんなところに書き込めばいいの〜〜〜〜」ってなっちゃいます。 このとき、この「ファイルの状況どうなってるっけメモ」にあたるのが、オープンファイル記述と呼ばれるものです。OSは、「ファイル開いて」っていうシステムコールを受け取ると、オープンファイル記述を作り出して自分で保持しておきます。さらに、システムコールを送ってきたプロセスのidに対して、新しい番号札(ファイルディスクリプタ)を返します。このとき、オープンファイル記述とプロセスidと番号札の関連も、自分の中に保持しておきます。 これで、たとえばpidが100番のプロセスから「5番の次の行読み込んでよ」と言われても、「ふぇぇ」ってならずに、「はいよ」と言って読み込んでデータを返すことができるわけですね! # ファイルディスクリプタ/オープンファイル記述とfork さて、では、forkしたとき、ファイルディスクリプタやオープンファイル記述はどうなるのでしょうか? 先に答えを言ってしまいましょう。forkした場合、ファイルディスクリプタは複製されますが、複製されたファイルディスクリプタは同一のオープンファイル記述を参照します。 言い方を変えると、forkした場合、OSは新しいpidのために新しい番号札は作るけど、その番号札は同じ「ファイルの状況どうなってるっけメモ」に紐づけられてる、ということです。つまり、「ファイルの状況どうなってるっけメモ」は、親プロセスと子プロセスで共有するメモになります。 そのため、forkしたときに同じ番号札(ファイルディスクリプタ)にたいして親プロセスと子プロセス両方で操作をすると、おかしなことになることがあります。 ## オープンファイル記述は複製されない 例を見ましょう。 # fork_fd.rb # -*- coding: utf-8 -*- read_file = File.new("nyan.txt","r") # ファイルをopenしたあとにforkしてみる pid = Process.fork if pid.nil? # 子プロセス lines = [] while line = read_file.gets lines << line end write_file = File.new("child.txt","w") write_file.write(lines.join) write_file.close else # 親プロセス lines = [] while line = read_file.gets lines << line end write_file = File.new("parent.txt","w") write_file.write(lines.join) write_file.close end read_file.close 子プロセスと親プロセスで、nyan.txtから一行ずつ入力を受け取っています。もし、forkされたときに「ファイルの状況どうなってるっけメモ」まで複製されているならば、親プロセスが一行読み込んだとき親プロセスの「ファイルの状況どうなってるっけメモ」は一行分進みますが、子プロセスの「ファイルの状況どうなってるっけメモ」は書き変わらないので、親プロセスでの読み込みは子プロセスでの読み込みに影響を与えないはずですね。つまり、親プロセスでも子プロセスでも、同じくファイルの内容を読み込むことができるはずです。逆に、同じ「ファイルの状況どうなってるっけメモ」を参照しているならば、親プロセスで一行読み込んだら、共通の「ファイルの状況どうなってるっけメモ」が1行分進んでしまい、子プロセスではその行を読み込むことができなくなってしまいます。 では実際に確かめて見ましょう。nyan.txtに以下の内容を書き込んだ上で、fork_fd.rbを実行してみましょう nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan 実行します $ ruby fork_fd.rb さて、結果はどうなったでしょうか?オープンファイル記述が複製されていないことが実感できたかと思います。 ## ファイルディスクリプタは複製される では今度は、ファイルディスクリプタは複製されているのを見てみましょう # -*- coding: utf-8 -*- file = File.open("nyan.txt","r") # ファイルをopenしてからforkする pid = Process.fork if pid.nil? #子プロセス sleep 1 # 親プロセスがfileを閉じるのを待つ # 親プロセスがfdを閉じてても、自分はまだ番号札を持ってるから読み込める puts file.readlines.join file.close #自分も番号札を返す else # 親プロセス file.close #番号札をOSに返す Process.wait(pid) #子プロセスが終わるの待つ end 実行してみると、親プロセスがすでに番号札をOSに返してしまっても、子プロセスは複製された番号札を持っているので問題なくファイル操作ができているのが見て取れると思います。 ## どうするのがベストプラクティスなの? すでにfileがopenされている状態でforkすると、以上に見たように予期せぬ動作で混乱することがあります。そのため、forkした場合、親プロセスで使わないファイルは親プロセスですぐ閉じる、子プロセスで使わないファイルは子プロセスですぐ閉じるとすると、最も問題が起きにくいと思います。子プロセスでファイルを閉じたとしても、親プロセスでファイル使いたい場合に問題なく扱える(またはその逆も)のは、上に見た通りですからね # 次回予告 ソケットの話してpreforkサーバーを自分で書いてみるつもり  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 4 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,16 +2,18 @@ この文書は\*nix系のシステムにおけるプロセスやスレッド、シグナルについて説明することを目的に書かれています。「プロセスとかスレッドとかよくわかってないからちゃんと知りたいな」みたいなひとたちが想定読者です。あと、まとめることによって自分の知ってることの棚卸しをするという目的もあります。 少しずつ書いてますが、更新したら http://nekogata.hatenablog.com/ でお知らせします # 目次(予定) * プロセス、スレッド * プロセスについて * プロセスのライフサイクル * プロセスツリーについて * fork * exec * プロセスとファイル入出力 * シグナル * スレッドについて * プロセスとの違い * リソースの共有について  - 
        
Shinpeim revised this gist
Feb 12, 2013 . No changes.There are no files selected for viewing
 - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ # プロセス、スレッド ## マルチプロセスとスケジューリング  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -126,7 +126,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby print_mew.rb 1>>mew.txt とすると、 mew.txt にさらに mew が追記され、mew.txt の中身は mew(改行)mew というものになります。"1>>mew.txt"が、「標準出力の出力先はmew.txtだよ。ファイルが存在してたら末尾に追記してね」を意味するわけです。さらにもう一度 $ ruby print_mew.rb 1>mew.txt @@ -137,6 +137,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby print_mew.rb > mew.txt ## 標準入力のリダイレクト 当然、標準入力もリダイレクトすることが可能です。そのためには、"<"という記号を使います。 試しに、さっき作った mew.txt というファイルを標準入力としてみましょう。 @@ -151,7 +152,6 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby stdin.rb < mew.txt mew 当然ながら、複数のリダイレクトを同時に行うことも可能です $ ruby stdin.rb 0<mew.txt 1>mew_copy.txt  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 11 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -112,6 +112,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 リダイレクトを使うと、標準入出力に別のファイルを指定することができます。ちなみに、シェル上(sh,bash,zshを想定)では、標準入力は「0」という数字、標準出力は「1」という数字、標準エラー出力は「2」という数字で表されます(なんでこんな謎っぽい数字使ってるのかは後で説明します)。出力系のリダイレクトは ">" という記号、あるいは">>"という記号で行えます。">"の場合、指定されたファイルがすでに存在する場合はそれを上書きします。">>"の場合、指定されたファイルがすでに存在する場合はファイルの末尾に追記します。 ## 標準出力のリダイレクト 例えば、 # print_mew.rb @@ -121,7 +122,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby print_mew.rb 1>mew.txt とすると、mew とだけ書かれた mew.txt というファイルができあがります。"1>mew.txt"が、「標準出力(1)の出力先はmew.txtだよ」を意味するわけですね。その上で $ ruby print_mew.rb 1>>mew.txt @@ -135,14 +136,15 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby print_mew.rb > mew.txt ## 標準入力のリダイレクト 当然、標準入力もリダイレクトすることが可能です。そのためには、"<"という記号を使います。 試しに、さっき作った mew.txt というファイルを標準入力としてみましょう。 $ ruby stdin.rb 0<mew.txt mew "0<mew.txt"が、「mew.txtを標準入力(0)の入力ソースとするよ」を意味しているわけですね。mew.txtの内容がstdin.rbによって読み込まれ、ターミナルに書き出されたかと思います。 これも、0を省略した書き方が可能です。 @@ -152,9 +154,13 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 当然ながら、複数のリダイレクトを同時に行うことも可能です $ ruby stdin.rb 0<mew.txt 1>mew_copy.txt 上記の場合、stdin.rbの標準入力はmew.txtとなり、標準出力は mew_copy.txt となります。 stdin.rbの内容は標準入力を読み込んで標準出力にそのまま書き出すものなので、mew_copy.txtという新しいファイルに、mew.txtの内容、つまり「mew」 が書き込まれることになります。 ## 標準エラー出力のリダイレクト 標準入出力について見てみたので、標準エラー出力についても見てみましょう。 @@ -168,7 +174,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby stdout_stderr.rb 1>out.txt 2>err.txt "1>out.txt" で「標準出力(1)をout.txt」に、"2>err.txt" で「標準エラー出力(2)をerr.txt」に向けています。 すると、out.txtには "this is stdout"が、err.txt には"this is stderr"が書き出されているかと思います。  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 13 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -110,7 +110,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 # リダイレクト リダイレクトを使うと、標準入出力に別のファイルを指定することができます。ちなみに、シェル上(sh,bash,zshを想定)では、標準入力は「0」という数字、標準出力は「1」という数字、標準エラー出力は「2」という数字で表されます(なんでこんな謎っぽい数字使ってるのかは後で説明します)。出力系のリダイレクトは ">" という記号、あるいは">>"という記号で行えます。">"の場合、指定されたファイルがすでに存在する場合はそれを上書きします。">>"の場合、指定されたファイルがすでに存在する場合はファイルの末尾に追記します。 例えば、 @@ -121,11 +121,11 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby print_mew.rb 1>mew.txt とすると、mew とだけ書かれた mew.txt というファイルができあがります。"1>mew.txt"が、「標準出力の出力先はmew.txtだよ」を意味するわけですね。その上で $ ruby print_mew.rb 1>>mew.txt とすると、 mew.txt にさらに mew が追記され、mew.txt の中身は mew(改行)mew というものになります。"1>>mew.txt"が、「標準出力の出力先はmew.txtだよ。ファイルが存在してたら末尾に追記しねて」を意味するわけです。さらにもう一度 $ ruby print_mew.rb 1>mew.txt @@ -135,22 +135,28 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 $ ruby print_mew.rb > mew.txt 当然、標準入力もリダイレクトすることが可能です。そのためには、"<"という記号を使います。 試しに、さっき作った mew.txt というファイルを標準入力としてみましょう。 $ ruby stdin.rb 0<mew.txt mew "0<mew.txt"が、mew.txtを標準入力の入力ソースとするよを意味しているわけですね。mew.txtの内容がstdin.rbによって読み込まれ、ターミナルに書き出されたかと思います。 これも、0を省略した書き方が可能です。 $ ruby stdin.rb < mew.txt mew 当然ながら、複数のリダイレクトを同時に行うことも可能です $ ruby stdin.rb < mew.txt > mew_copy.txt 上記の場合、mew.txt の内容が stdin.rb によって読み込まれ、標準出力に書き出されます。標準出力は mew_copy.txt をさしているので、mew_copy.txtという新しいファイルに mew が書き込まれることになります。 標準入出力について見てみたので、標準エラー出力についても見てみましょう。 # stdout_stderr.rb puts "this is stdout" @@ -170,7 +176,7 @@ mew.txtの内容がstdin.rbによって読み込まれ、ターミナルに書 $ ruby stdout_stderr.rb 1>out.txt 2>&1 &を付けることによって、「この1ってのは、1っていう名前のファイルじゃなくて標準出力を表す数字だよ!」ってことを言っているわけですね。さあ、またまた新しい疑問がわいてきました。なんで&付けるとそれがファイル名じゃなくて標準出力ってことになるの? そもそもなんで0とか1とか2とかって謎っぽい数字使ってるの? 疲れてきたので、そのあたりは次回にまわします。 # パイプ  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -94,7 +94,7 @@ rubyの組み込みグローバル変数 $stdout には、「標準出力」と と、さっき自分で入力したのと同じ内容が出力されるはずです。 Ctrl+D を押すと、EOFというものが入力されます。この「EOF」というのは「End Of File」の略で、「ここでこのファイルはおしまいだよ」というのを伝える制御文字です。プロセスは、この「EOF」を受け取ることで、「よし、標準入力を全部読み込んだぞ」と理解して、IO待ちのブロック状態から抜けるわけですね。 ところで、最初の例では標準入力ではなくてnyan.txtを読み込んでいましたが、実はその間にも、一瞬プロセスは「ブロック中」状態になっています。ディスクからデータを読みこんでくるのが一瞬なので、普段はあまり意識しないかもしれませんが(とはいえ、コンピューターの処理の中ではdiskIOというのはかなり遅い処理の部類です。だから、パフォーマンスが必要になってくるようなソフトウェアを書くときには、なるべくIOをしないことでブロックされないようにしてパフォーマンスを稼ぐみたいな手法が取られたりするわけです)。  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -96,7 +96,7 @@ rubyの組み込みグローバル変数 $stdout には、「標準出力」と Ctrl+D を押すと、EOFというものが入力されます。この「EOF」というのは「End Of File」の略で、「ここでこのファイルはおしまいだよ」というのを伝える制御文字です。rubyのプロセスは、この「EOF」を受け取ることで、「よし、標準入力を全部読み込んだぞ」と理解して、IO待ちのブロック状態から抜けるわけですね。 ところで、最初の例では標準入力ではなくてnyan.txtを読み込んでいましたが、実はその間にも、一瞬プロセスは「ブロック中」状態になっています。ディスクからデータを読みこんでくるのが一瞬なので、普段はあまり意識しないかもしれませんが(とはいえ、コンピューターの処理の中ではdiskIOというのはかなり遅い処理の部類です。だから、パフォーマンスが必要になってくるようなソフトウェアを書くときには、なるべくIOをしないことでブロックされないようにしてパフォーマンスを稼ぐみたいな手法が取られたりするわけです)。 こんな感じで、「実際はdisk上のファイルじゃないもの」も、「disk上のファイルとおなじように」扱える。そういう仕組みがLinuxには備わっています。今はそれが「すべてがファイル」の意味だと思ってください。  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -77,7 +77,7 @@ rubyの組み込みグローバル変数 $stdout には、「標準出力」と の行で、プロセスが「ブロック中」になっているからです。前回の内容を思い出してください。プロセスの実行中の状態のうちのひとつに、「ブロック中」があったと思いますが、ブロック中というのは、「IOとかを待ってて今は処理できないよ」という状態でしたね。 この行では、標準入力からの入力を「全部」読み込もうとしています。そして、標準入力のデフォルトはターミナルからの読み込みを行います。しかし、すでに何が書かれているか決まっているdisk上のファイルと違って、ターミナルへの入力は「終わり」がいつ来るものなのかわかりません。だから、このプロセスは「終わり」が入力されるまで、ずっとずっと「ブロック中」の状態で待ち続けているのです。けなげですね。 では、ひとまず以下のような感じで、プロンプトが戻ってきてないターミナルに何かを打ち込んでみてください。 @@ -102,6 +102,8 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 ちなみに、標準入力/出力の他にも、「標準エラー出力」というのがあり、これもデフォルトの出力先はターミナルになっています。 余談ですが、IO#readlinesは「ファイルの内容を全部読み込む」という挙動をしますが、では一行だけ読み込む IO#readline を使うとどういう挙動をするかなど、自分で確かめてみると、「あっブロックしてる」「あっ今読み込んでブロック中じゃなくなった」みたいなのがわかっておもしろいかもしれません。 # じゃあデフォルトじゃないのはなんなんだよ 先ほどから標準入出力のデフォルトはどうこうみたいな話をしていますが、それはつまり標準入出力はその他の場所にもできるってことですね。そのための機能が「リダイレクト」と「パイプ」です。  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 185 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,185 @@ # プロセスとファイル入出力 さて、前回、プロセスというのは「自分が独占したメモリーの中で動いているので、その中で何をしても他のプロセスのメモリーに影響を与えない」というのを見れたかと思います。でも、そんな自分の中だけで完結してる引きこもりみたいなプロセスじゃあ、意味がないですね。外界からなんかデータをもらって、自分の中で処理して、それを外の世界に知らせる方法が必要になってきます。 そこで、プロセスに外から何かを入力したり、プロセスが外に何かを出力する方法として、「ファイルの入出力」というのがあります。たとえば、ファイルに書かれたデータをプロセスがメモリー上に読み込んでなんか処理をするとか、処理を行った結果をテキストファイルに書き込みをするとか。例を見てみましょう。 まず、以下のようなテキストファイルを nyan.txt という名前で適当な場所に作ってみます。 nyan nyan nyan nyan nyan nyan では、このファイルをプロセスの中に読み込んでみましょう。今日は Ruby を使います。 file = File.open("nyan.txt","r") lines = file.readlines #ファイルの中身を全部読み込む file.close ファイルを open して、その内容を lines という変数に読み込んで、最後にファイルを close しています。ファイルの中のデータはディスクに書かれたものであり、プロセスがもともとメモリー内に持っていたものではありません。このディスクに書かれた内容を lines = file.readlines の行でlines変数に読み込むことで、プロセスの「外界」の情報を、プロセスの内部のメモリーに読み込んでいますね。 では今度は出力をしてみましょう。 # nyan_copy.rb file = File.open("nyan.txt","r") lines = file.readlines file.close file = File.open("nyan_copy.txt","w") file.write(lines.join) file.close nyan_copy.rbを、nyan.txtと同じディレクトリに作って、実行してみましょう。nyan.txtと同じ内容の、nyan_copy.txtというファイルが生まれたかと思います。さきほどディスクから読み込んでメモリー上に展開したデータを、そのまま別のファイルに対して出力したためですね。 こうして、プロセスはファイルを通じて外部との入出力を行うことができます。 # すべてがファイル??? さて、いまは「テキストファイル」への読み書きを行ってみましたが、「Linuxではすべてがファイルなんだよ」みたいな話を聞いたことがないでしょうか? そんなこと言われても、「はっ?」って感じの話ですよね。「Linuxではキーボードもファイルだからね」みたいなことを言うひとに至っては「こいつ頭大丈夫か、キーボードはキーボードだろうが」みたいな気持ちになりますよね。わたしは最初にこの話を聞いたときに「なにそれ、禅問答?哲学?頭大丈夫?ファイルはファイルだしキーボードはキーボードだろ」って思いました。 「全てがファイル」とか言われると「世の中のすべてはファイルなのだ、そう、きみも、わたしも」みたいな禅問答をやられてるみたいな気持ちになるので、こういう言い方はあまりよくない感じがしますね。だったら、こんなふうに言われたらどうでしょうか? 「Linuxは、すべての入出力がファイルと同じ感じで扱えるような設計になっているんだよ」。つまり、プロセスが「ここでターミナルからの入力を受け取りたいんだけど」とか、「ネットワーク越しに入力もらってネットワーク越しに出力したいんだけど」みたいなことを言うと、OSさんが「はいよ、実際はHD(さいきんだとSSDかな)上のファイルじゃないんだけど、いつもファイルを通じてディスクを読み書きするのと同じやり方で扱えるように用意しといたよ!」みたいな感じでそのためのインターフェイスを用意してくれてるのです。 # 例:標準入出力 さて、例を見てみましょうか。 # stdout.rb file = File.open("nyan.txt","r") lines = file.readlines file.close file = $stdout # この行だけ書き換えた file.write(lines.join) file.close nyan.txt と同じディレクトリに、今度は stdout.rb を作って、実行してみましょう。nyan.txtの内容が、ターミナルに出力されたかと思います。 rubyの組み込みグローバル変数 $stdout には、「標準出力」と言われるものが、すでにFile.openされた状態で入っています。この「標準出力」の出力先は、デフォルトではターミナルをさします。そのため、さっきテキストファイルに内容を出力したのと同じやりかたで、ターミナルに対して出力ができるわけです。 標準出力があるなら標準入力もあるの?当然あります。 rubyだと標準入力はFile.openされた状態で $stdin というグローバル変数に入っています。標準入力のデフォルトの入力ソースはターミナルになります。例を見ましょう。 # stdin.rb file = $stdin lines = file.readlines #標準入力からの入力を全部受け取る file.close file = $stdout file.write(lines.join) # 標準出力に対して内容を書き出す file.close 上記のような stdin.rb というファイルを作成して、実行してみましょう。何も出力されず、かつプロンプトも返ってこない状態になると思います。これはなぜかと言うと、 lines = file.readlines #標準入力からの入力を全部受け取る の行で、プロセスが「ブロック中」になっているからです。前回の内容を思い出してください。プロセスの実行中の状態のうちのひとつに、「ブロック中」があったと思いますが、ブロック中というのは、「IOとかを待ってて今は処理できないよ」という状態でしたね。 この行では、標準入力からの入力を「全部」読み込もうとしています。そして、標準入力のデフォルトはターミナルからの読み込みを行います。しかし、ターミナルへの入力は、すでに何が書かれているか決まっているdisk上のファイルと違って、「終わり」がいつ来るものなのかわかりません。だから、このプロセスは「終わり」が入力されるまで、ずっとずっと待ち続けているのです。けなげですね。 では、ひとまず以下のような感じで、プロンプトが戻ってきてないターミナルに何かを打ち込んでみてください。 $ ruby stdin.rb #さっき実行したコマンド aaaa bbbbbb ccc 打ち込みましたか?そうしたら、改行したあと、おもむろにCtrlキーを押しながらDを押してみましょう。すると、ターミナルに、あたらしく aaaa bbbbbb ccc と、さっき自分で入力したのと同じ内容が出力されるはずです。 Ctrl+D を押すと、EOFというものが入力されます。この「EOF」というのは「End Of File」の略で、「ここでこのファイルはおしまいだよ」というのを伝える制御文字です。rubyのプロセスは、この「EOF」を受け取ることで、「よし、標準入力を全部読み込んだぞ」と理解して、IO待ちのブロック状態から抜けるわけですね。 ところで、最初の例では標準入力ではなくてnyan.txtを読み込んでいましたが、実はその間にも、一瞬プロセスは「ブロック中」状態になっています。ディスクからデータを読みこんでくるのが一瞬(とはいえ、コンピューターの処理の中ではdiskIOというのはかなり遅い処理の部類です)なので、普段はあまり意識しないかもしれませんが。 こんな感じで、「実際はdisk上のファイルじゃないもの」も、「disk上のファイルとおなじように」扱える。そういう仕組みがLinuxには備わっています。今はそれが「すべてがファイル」の意味だと思ってください。 ちなみに、標準入力/出力の他にも、「標準エラー出力」というのがあり、これもデフォルトの出力先はターミナルになっています。 # じゃあデフォルトじゃないのはなんなんだよ 先ほどから標準入出力のデフォルトはどうこうみたいな話をしていますが、それはつまり標準入出力はその他の場所にもできるってことですね。そのための機能が「リダイレクト」と「パイプ」です。 # リダイレクト リダイレクトを使うと、標準入出力に別の場所を指定することができます。ちなみに、シェル上(sh,bash,zshを想定)では、標準出力は「1」という数字、標準エラー出力は「2」という数字で表されます(なんでこんな謎っぽい数字使ってるのかは後で説明します)。出力系のリダイレクトは ">" という記号、あるいは">>"という記号で行えます。">"の場合、指定されたファイルがすでに存在する場合はそれを上書きします。">>"の場合、指定されたファイルがすでに存在する場合はファイルの末尾に追記します。 例えば、 # print_mew.rb puts "mew" # putsは標準出力に対して引数を出力する というrubyスクリプトがあるとき、 $ ruby print_mew.rb 1>mew.txt とすると、mew とだけ書かれた mew.txt というファイルができあがります。その上で $ ruby print_mew.rb 1>>mew.txt とすると、 mew.txt にさらに mew が追記され、mew.txt の中身は mew(改行)mew というものになります。さらに $ ruby print_mew.rb 1>mew.txt とすると、mew.txtは上書きされてしまい、「mew」とだけ書かれたファイルになります。 ちなみに、標準出力をリダイレクトする際は、「1」を省略した書き方も可能です。 $ ruby print_mew.rb > mew.txt 標準入力もリダイレクトすることが可能です。そのためには、"<"という記号を使います。 試しに、さっき作った mew.txt というファイルを標準入力としてみましょう。 $ ruby stdin.rb < mew.txt mew mew.txtの内容がstdin.rbによって読み込まれ、ターミナルに書き出されたかと思います。 当然ながら、複数のリダイレクトを同時に行うことも可能です $ ruby stdin.rb < mew.txt > mew_copy.txt 上記の場合、mew.txt の内容が stdin.rb によって読み込まれ、標準出力に書き出されます。標準出力は mew_copy.txt をさしているので、mew_copy.txtという新しいファイルに mew が書き込まれることになります。 標準エラー出力についても見てみましょう。 # stdout_stderr.rb puts "this is stdout" warn "this is stderr" # warnは標準エラー出力に引数を出力する 普通にstdout_stderr.rbを実行すると、標準出力も標準エラー出力もターミナルに向いているので、どちらもターミナルに出力されます。 では、以下のようにしてみましょう。 $ ruby stdout_stderr.rb 1>out.txt 2>err.txt "1>out.txt" で標準出力をout.txtに、"2>err.txt" で標準エラー出力をerr.txtに向けています。 すると、out.txtには "this is stdout"が、err.txt には"this is stderr"が書き出されているかと思います。 ちなみに、"2>&1"みたいにして標準エラー出力を標準出力へ向けることもできます。 $ ruby stdout_stderr.rb 1>out.txt 2>&1 &を付けることによって、「この1ってのは、1っていう名前のファイルじゃなくて標準出力を表す数字だよ!」ってことを言っているわけですね。さあ、またまた新しい疑問がわいてきました。なんで&付けるとそれがファイル名じゃなくて標準出力ってことになるの? そもそもなんで1とか2とかって謎っぽい数字使ってるの? 疲れてきたので、そのあたりは次回にまわします。 # パイプ パイプについても簡単にみておきましょう。シェル上では、パイプは「|」という記号で実現されます。 $ command_a | command_b とすると、command_aの標準出力に出力された内容がcomman_bの標準入力に入力されます。この時、command_aの出力が全部終わってなくても(EOFに達しなくても)、command_bのプロセスは「来たデータから順々に」処理していきます。データがcommand_aから出力されたら、すぐにcommand_bはそのデータを処理します。まだEOFが来てないけどcommand_aからの出力が来ないぞ、というときにはcommand_bはどうするでしょうか。そうですね、標準入力からのデータを読み込む部分で「ブロック中」になって、command_aが標準出力になにかを吐くのを待ち続けるわけです。けなげですね。ちなみに、このように入力と出力をパイプでつないで、「ファイルの終わりを待たずにきたデータから順々に」なにか処理をするのを、パイプライン処理、とか、ストリーム処理、と言います。 また、パイプはシェル上でふたつのプロセスの標準入出力をつなぐだけではなく、プロセス上でも新しい入出力のペアを作ることができます。RubyだったらIO.pipeを使うと実現できるでしょう。Perlだったらpipe関数ですね。詳しくはrubyの公式リファレンスやperldoc,piep(2)を参照してください。 ## 次回予告 次回はファイルの入出力について、もっと深くまで潜っていきますよ!ファイルディスクリプタの話をして、ソケットの話をします。そのあとようやくファイルディスクリプタとforkの話ができたらいいな!さーて、次回も、サービス!サービスゥ!  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 0 additions and 185 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,185 +0,0 @@  - 
        
Shinpeim revised this gist
Feb 12, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -98,7 +98,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」 ところで、最初の例では標準入力ではなくてnyan.txtを読み込んでいましたが、実はその間にも、一瞬プロセスは「ブロック中」状態になっています。ディスクからデータを読みこんでくるのが一瞬(とはいえ、コンピューターの処理の中ではdiskIOというのはかなり遅い処理の部類です)なので、普段はあまり意識しないかもしれませんが。 こんな感じで、「実際はdisk上のファイルじゃないもの」も、「disk上のファイルとおなじように」扱える。そういう仕組みがLinuxには備わっています。今はそれが「すべてがファイル」の意味だと思ってください。 ちなみに、標準入力/出力の他にも、「標準エラー出力」というのがあり、これもデフォルトの出力先はターミナルになっています。  
NewerOlder