私が理解していること
・コンピュータは0と1しかあつかえない
・ハードディスクは、磁石がのった円盤で、磁力の向きで1,0を表現している
・ファイル読み取りは、これらの1,0の並びを認識すること
・ファイル書き込みは、これらの並びを変更すること
では、開く、閉じるは?
シェルで、
echo "hello" > hello.txt
と実行すると、物理的にはHDDにデータが書き込まれます。
それは理解できます。
しかし、C言語などで
FILE *fp;
fp = fopen( "test.txt", "r" );
としたとき、fopenの裏側では何が行われているのでしょうか?
ファイルポインタ(とかファイルハンドル)とは一体何なんでしょうか?
読み取り、書き込みは、物理メディアに対しての物理的動作をイメージできますが、
オープン、クローズは何もイメージできません。
オープン、クローズとは何でしょうか?
ファイルストリーム
http://www.geocities.jp/ky_webid/cpp/library/033.html
残念ながら私の知識では回答することはできませんが、調べてみると面白そうな pdf を見つけたのでリンクを貼っておきます。
"Linuxカーネル2.4の設計と実装。ファイルシステム(後編)
http://www.mars.dti.ne.jp/~otk/bak/200105-linuxkernel24.pdf
この pdf の、図14のあたりにファイルのオープンやクローズについても書かれています。斜め読みしただけですが、参考になると思います。
あとは「C言語によるUNIXシステムプログラミング入門」という本が参考になるとおもいます。fopen などの関数の核になる、よりハードウェアに近い関数(低水準入出力関数)やファイルポインタが指しているもの(利用者ファイル記述子表)などについての説明もあります。
私が回答できるのはこれだけですので、参考程度に思ってください。
面白い質問だと思うので、私も他の方の回答を楽しみにしています。
なるほどなるほど。すごい難しいですね。
でもちょっと答えに近づいた気がします。
ありがとうございます!
基本的にプログラムは直接メディアにアクセスしてデータを読み書きするのではなくて、OS にお伺いを立てて行います。
OS は「ファイルオープンしたいんですけどー」という申請を受けて、そのファイルがシステム的に保護されてないかとか、他のプログラムから「独占的に使いたいから他のプログラムに使わせちゃダメ」とか言われてないかとか調べて、使ってもオーケーならファイルハンドルを渡します。
プログラムはファイルを使い終わったら OS に「終わったよー」と申請してクローズします。
http://q.hatena.ne.jp
なるほど。プログラムが直接HDDにアクセスすることはないと。OSに「ファイルオープン」を依頼してるだけだと。
しかしオープンの際のHDDの動作は謎のままですね。
あと、ファイルハンドルの正体は何でしょうか?
コンピュータの世界ではレイヤーという考え方が基本にあります。
このレイヤーをごっちゃにしてコンピュータを理解することはできません。
例えば「ファイルを開く」はOSがデバイスドライバを使って下層のBIOSにデータの読み出しを指示します。BIOSは物理レイヤーからデータを取り出して上層のOSに返します。OSはもらったデータを上層のアプリケーションレイヤーに渡します。という手順になります。
OSには、ファイルシステムという仕組みがあります。WindowsではNTFSなどがファイルシステムです。これはファイルの目次情報と、ディスク上のデータのアドレスの番地情報の橋渡しをします。ディスク上では単なる01の磁気ですが、ファイルシステムによってファイルごとの意味のある情報になり、BIOSからOSに渡されてファイルという論理的な情報になります。
http://blog.livedoor.jp/papavolvol/
URLは私のブログです。今回の回答と直接の関連ではありませんが、ご縁がありましたら一度お越しくださいね。
なるほど。
プログラム⇔OS間でのopen/closeは、例えば、PHP⇔MySQL間でのconnect, closeのようなものと考えればよいでしょうか。セッション開始とセッション終了のようなものであると。
ハンバーガー屋さんの例は大変わかりやすいですね!
ありがとうございます。
セッション開始と終了のようなもの、という理解でよいと思います。
なお、どこかのコメントで、わざわざ「fopenでいうopenの場合」みたいな断りを入れましたが、セッションや開始、終了という概念は、いろんなレイヤで使われます。ファイルのオープンからクローズまでの一連の流れもセッションと捉えることもできますし、逆にconnect-closeを、セッションのオープン、セッションのクローズという表現で記載することもできます。
コンピュータの中でOPEN、CLOSEと言われると確かに現在ではイメージしづらいかも知れません。
元々の発想や、旧来のコンピュータを考えると逆にイメージしやすいと考えます。
ファイルってご存知ですかね。コンピュータのではありません。キングやコクヨといった事務用品メーカの作っているファイルです。後、フォルダも。
キングジム キングファイルG A4タテ500枚 グレー 975GX
A4-SIF-B 個別フォルダーエコノミータイプA4青10冊入
コンピュータの黎明期というか、ノイマン以前の事務機械ってファイルとかバインダといった紙書類の保管システムの更新として期待されていたものですから、その当時の用語が残っています。
さらに初期のコンピュータだとメモリやDASDといった外部記憶媒体の容量が小さかったから差替えながら使っていましたからね。用語をそのまま使っていても違和感を感じなかったのでしょう。
実際にファイルをOpenするということは、パソコンだとFATなどを参照してハードディスク上のデータを特定してメモリに展開する。closeはメモリのロックを外して開放するといった動作になります。
http://support.microsoft.com/kb/100108/ja
「パソコンだとFATなどを参照してハードディスク上のデータを特定してメモリに展開する。」
何をメモリに展開するのでしょうか?
これは、readの説明に見えます。
紙ファイルを「開く」ときは、紙ファイルそのものの形状が変わりますよね。
HDD上のファイルを「開く」とき、HDDそのものは変化するのでしょうか?
「概念」として理解したいのであれば、裏側をあまり意識しない方が理解できると思います。
裏側にしても、ハードディスクへの読み書きから順番に理解していくのは相当技術力が必要だと思います。
土地に倉庫を建てるという例でOSあたりを中心に概念的に説明すると。
といった前準備がまず発生します。ついでファイルの作成になりますが、
といった作業が発生します。
ファイルをオープンする場合は
といった感じになると思います(少し抜けもれあると思いますが)
これらを実際にはプログラムで行うので、プログラムでどう実装されているかはまた別のレベルの理解が必要になります。
URLはダミーです http://www.google.com/
なるほど、わかりやすい説明をありがとうございます。
openというのが、「プログラムからOSへの依頼とその返答」であることがよくわかりました。
基本的にopenというのは、プログラム⇔OS の相互作用の話であって、OS⇔物理メディアの相互作用の話ではない、と理解しました。
この理解であってますでしょうか?
openというのがfopenなどのことであれば、その理解でよいと思います。別のところで、階層構造になっているという話もでていると思いますが、これは言い換えると下の階層の動作が(いい意味で)隠蔽されているということも意味します。
たとえば、郵便を出すときに、その郵便物がどうやって相手の手元に届くかは理解していなくても郵便を出す、期日を指定するといったことが可能なのと似ています。
わたしの例で言うと、たとえば4平米の荷物を置いてくださいという指示の際に、お客さんは、どこの区画にどうおくかは意識する必要ないし、できないということです。
アプリケーションのファイル要求に対して裏側でどのようなことが行われているかを知るには、OS が提供する機能の一部であるファイルシステムについて学ぶ必要があります。
まずはファイルシステム - Wikipedia を、さらに深く知るならファイルシステムでググるのが良いと思います。
なるほど、ファイルシステムについて勉強すればよいのですね。
つまり、「私はファイルシステムをわかっていない」ということがわかりました。
これは大きな一歩です。
ありがとうございました。
昔は「ファイル」なんて便利なものはなくて、ディスクのトラックやセクタを番号で指定して読み書きしていたんですよ。読み書き、とは補助記憶装置と主記憶との間でバイト列をやりとりすることです。
http://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%82%BB%E3%82%AF%E3%82%BF
でもそれだとディスクを読み書きするプログラムがどのセクタに何のデータを格納するかきめなくちゃいけないし、いろいろめんどくさいので、「ファイル」という考え方を賢い人が思いついたわけです。ファイルの中身はバイト列にすぎないわけだけど、そのバイト列がディスクのどのトラックのどのセクタに格納されているか、なんてのはファイルを読み書きする側からは知らなくてよい、そういうのはファイルシステムが全部めんどうをみてくれるわけです。
そのファイルシステムとアプリケーションプログラムとの間でおこなう取り決めとして、「ファイル名」という名前を使って「開く」ことでファイルを掴む「ハンドル」を得て「読み書きする」、もう使わなくなったら「閉じ」て、掴んでいたハンドルを開放する、というアクセスの手段が「ファイルを開く」ということです。
ふつう大きなファイルなら二つ以上のセクタに格納されていますからハンドルとセクタがそんな単純に対応づいたりはしないです。このファイルはディスク上のどこからどこまで使っている、などというのをファイルシステムが管理しています。
ハンドルを開放しなければならないのは、ファイルアクセスがコンピュータにとって非常に重くて遅い仕事だっていう感じを持てばわかると思います。ディスク上にファイルを大量につくっておくことはできても、ファイルを本当に同時に読み書きできるのはせいぜい数個にすぎません。だから、いまからこのファイルを読み書きしますよ、と言って開いて、もう読み書きしませんと言って閉じるわけです。そうしないと誰かがファイルを読み書きしていると他の人がファイルを読み書きできなくなります。そのもうアクセスしませんと言うのがファイルを閉じるでありハンドルを開放するということです。
そうすると、ハンドルを獲得するというのは、読み書きする権利を獲得すること、ハンドルを開放するとうのは、その権利を手放すこと、ということになるんですかね。ちょっとイメージがわいてきました。
http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0
OSはディスクに対して0か1かをビット単位で直接アクセスすることは稀です。
一般的には
アプリケーション <> OS <> ディスク <> 物理的な円盤 という通信を行います。
この時 ディスク <> 円盤 の間では0 1 の通信が行われます。
OS <> ディスク の間では セクタやクラスタ といった ある一定の0と1の集合単位で操作をします。これは、0や1しかない通信で 何十バイトも通信をすると非効率なので まとめて通信しよう ということから セクタ クラスタ という考え方が生まれました。
さて、アプリケーション <> ディスク ですが
たとえば、物を書きこむときに ディスクの先頭から数えて 何クラスタ目の何セクタ目にXを書き込む と指定するのは 毎回毎回 すべてのデーターで ディスクの先頭から数えていかないといけないので、覚えておくのが大変です。
そこで、アプリケーション <> OS <> ディスク
と中間にOSが入り ファイル という仮想的な単位を見せてくれます。
こうすることによって アプリケーションは ディスクの先頭からXセクタ目 という事を いうのではなく XXという名前のファイルの先頭からXバイト目という指定をできることになります。
OSは この ファイル名とその先頭から数えて何バイト目という信号を ディスクの先頭から数えてXクラスタ目のXセクタ目のXバイト目という信号に書き換えます。
つまり、通訳をしています。
これをファイルシステムと呼びます。
OSはファイルオープンと呼ばれると ファイルシステムから ファイルの先頭がディスクの先頭から数えて何クラスタ・セクタ・バイト目なのか?という相関関係の表を読み出します。 クロースを呼ばれると、この相関関係の表をメモリーから捨てます。
要するにファイルのオープン・クローズとは この相関関係の表を読み出せ・捨てろという事です。
他にもこのオープン・クローズはバッファリングなどいろいろなアプリケーションにとって有利なことをします。
相関関係の表はWindowsであればFATやNTFS Linuxであればext3 などが有名です。
その他アプリケーションが オープン・クローズ と 読んでいるのは この歴史的なファイルシステムのオープン・クローズ から 由来している名前であり 一意な定義は ありません。 アプリケーション固有の動作となります。
しかし、一般的に OSのファイルシステムの オープン・クローズ を呼んでいる という動作は 同じだと思います。
また、厳密なオープン時 クローズ時の動作は OSにより若干異なりますが概念的には 上記です。
なぜ、ディスクの先頭から数えないのか?というのは、毎回毎回ディスクの先頭から数えているのは面倒だからと思うプログラマーが多く ファイルシステムのほうが人気だったから。という歴史的経緯です。
もし、人間の頭が機械のように暗記に長けていて、とんでもなく長い数列を暗記可能でディスクの先頭からを覚えるほうが楽であればファイルシステムはなかったかもしれません。
総合的な回答としては、歴史的経緯でプログラマにファイルシステムという物が人気で使われており、その時の処理の名前にたまたま 比較的処理をイメージしやすいOpen,Closeという名前がついた。ということで
Open,Closeとは上記のような処理についた名前である。というのが回答になります。
以上です。
fopenというレイヤのopenであれば、大雑把に言えば、プログラムとOS間の処理ですね。
最も、fopenは少し皮をかぶせていて、もう少し低レベルなopenであれば、openという関数を呼び出すこともできます。
様は、最近のソフトウェアは何層にも階層化されていて、基本的にはその下のことは意識する必要もないし、基本できないということに尽きます。
たとえば、openの延長上で、ディスクに何か操作を行うか、メモリ上で完結させるかはOSが適宜判断することであって、openを呼び出すアプリケーションが意識する必要はないということです。
たとえば、RAMDISKであれば、read/writeも含めてすべてメモリ上で完結しますが、アプリケーションは呼び出し先がRAMDISKか、ハードディスクか、はたまたUSBメモリかといったことを意識して操作方法を変えたりすることはないということです。
そうすると、open時にOS⇔HDD間でなんらかのやりとりが発生してるかもしれないが、それはプログラマにとっては隠ぺいされていて、知る必要はないし知ることもできないと。
また謎が深まりました。
確かにRAMDISKだと全てメモリ上の話ですね。
(ポイント不要 話題が長いので別途書いておきます。)
はい。基本的にOpen,CloseとはOSが提供する機能であって、ディスクが提供する機能ではありません。
ただし ファイルシステムはディスクを使って実現されているので 完全にメモリだけで実現されているわけではありません。相関関係の表=ファイルテーブルはディスク上に書かれています。
しかし、一般的には OSが提供する機能とりかいして問題ありません。
http://ja.wikipedia.org/wiki/%E7%A3%81%E6%B0%97%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E8%A3%85%E7%BD%AE
ディスク=磁気ディスク装置(以後 ディスクシステム)の事です。略してしまってすみません。
HDDの中には複数または1枚の円盤が入っています。
ディスクシステムはPCとのインターフェイス
BIOSのような ディスクシステムのOS
円盤絵の書き込み装置
円盤
からなっています。
アプリケーションから直接円盤に書きこもうとすると とんでもなく複雑な知識が必要になるため(円盤の回転数や電圧などを管理したくないですよね?)
ディスクシステム側で クラスタやセクタなど 便利な機能を提供し
それを OSがデバイスドライバ経由で利用し OS は アプリケーションに ファイル という便利な機能を提供しています。
あ、自分の回答に作者さん以外がコメントできるんですね。すみません、知りませんでした。連投になってすみません。
たしかに、HDD=円盤ではなくて、HDDの中も階層構造になってますね。円盤の枚数と回転とか意識したことはありません。
「Open,CloseとはOSが提供する機能であって、ディスクが提供する機能ではない」
この一言に尽きると思いました。
ありがとうございます!
ファイル(ディスク上のものも仮想のものも)はメモリに読み込む必要があります。
そのメモリ上の場所を示すのがファイルポインタ(orハンドル)で、多くの言語ではファイルのオープン時に変数として割り当てられます。「この"text.txt"ってファイルを開いてよ」という依頼に対し、「じゃぁ、このfpっていう変数が指している場所に読み込んでやるよ」と教えてくれるわけです。
多くの場合、読み書きが終わるとファイルを読み込むためのメモリ領域が不要になりますし、(ディスクなど)アクセス競合の管理があるメディアの場合は「もう使わないよ」と宣言してもらう必要があったり、必要がないにしても、並行に実行されている他の処理がスムーズに進む場合があります。プログラム内でクローズするとそのようなことが(必要なら)行われます。
URLはダミーです。
http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0
あえて、質問者の最初の問題提起にそって述べます。
・ファイル読み取りは、【ディスク内のデータ領域のある特定のアドレスの】これらの1,0の並びを認識すること
・ファイル書き込みは、【ディスク内のデータ領域のある特定のアドレスの】これらの並びを変更すること
ですが、では、そのアドレスはどうやってディスク装置が知ることができるか、です。なにしろ、ユーザはファイル名しか与えていないからです。
で、ディスク内には、ディレクトリ領域というのがあって、そこに「ファイル名とアドレスの対応表を格納することができます。まだ対応ができていない(新規書き込み)なら対応表を作ります。すでにあるときには、その対応表を読みます」。これは物理的には、ディレクトリ情報(ファイル名、アドレス、書き込み権利など)が、0,1で構成される文字情報、数字情報なので、それらの0,1情報を読んだり書いたりしています。つまりディスク本体では、オープンやクローズでは、ディレクトリ領域に対する読み書きが発生しています。
で、ディレクトリ領域がどこにあるかというと、それはいわゆるOSが知っているわけですが、そのOSというのも、ほとんどの場合Disk OSなので、ディスク内のOS領域に対して読み書きしています。
もちろん、オープン、クローズは主記憶内でもいろいろなことをやっていますが、ディスクへの物理的なアクセスは発生しています(それを仮想ディスクであるメモリ空間であるとか、細かくいえばありますが、おおざっぱな認識として)。
ダミー:http://q.hatena.ne.jp/answer
●追記: ついでにいうと、「ファイル削除」は、決して、「データ領域のビットを全て0で埋め尽くす」ということでなく、ディレクトリ領域の対応表を消すことです。したがって、実際のディスクのアドレスは、別のファイルによって再利用されるかもしれないし、再利用されないで、元のデータが残っているかもしれない、です。
ふむふむ。
オープン時のコンピュータ内の動きが少しイメージできました。
しかし、「プログラムからは、ディスク本体の動きは知ることはできない」という意見と対立しますね。
「ファイル名とアドレスの対応表」というのが鍵のようですね。
これが、ファイルシステムということなのでしょうか。
「プログラムからは、ディスク本体の動きは知ることはできない」ですが、
それが上位の、fopenを記してあるメイン・プログラムということなら、
ディスクの実アドレスは知りません。fopenの引数で実アドレスを
引いて、それをfwriteに渡してはいません。しかし、下位では知っています。
ちょうど、下記コメントに書いた社長(?)と秘書の文書格納の比ゆでいえば、
社長は文書が格納されている棚の書類ファイルのありかを知らない(関心がない)
のと同じです。
なお、簡単のため、「ファイルの先頭アドレス」のように書きましたが、
長いデータは、ディスク上の連続したアドレスに格納されるとはかぎらず、
レコード単位に別々に格納されていますが、では、次のレコードはどこか、
といったことまでファイルシステムは管理しています。
既に色々な方の回答もありますので、ちょっと違う観点からの回答とします。
自分もC言語を勉強し始めの頃は、同じような疑問を持ちました。
自分の経験を踏まえて考えますと、コンピュータを勉強し始めの頃は、
シンプルなモデル上で、プログラムをイメージしていたように思います。
メモリ上には、自分のプログラムだけが配置され、入出力機能も独占しているような状態。
であれば、やりたい事が、ディスクへの書き込みであれば、
何故に、直接、ディスクへ書き込んでくれないの?といった感じです。
初期のコンピュータでは、きっと、直接書き込んでいたように思います。
でも、みなさんが説明して頂いているように、低レベルでの読み書きは、
実は、色々と面倒な手順を踏まないといけないんですね。
「こんなの毎回やってられね~よ。
ファイル名指定するだけで、後は書き込みたいデータだけ渡せば、
(もしくは読み込みたい領域を指定すれば)
面倒な事は代わりに誰かやってくれ~!!」
そんなこんなでOSが登場するわけです。
現在のOSは、色々と芸達者で、規模も大きくなっていますが、
ここでの説明では、便利なサブルーチンの集合とでも捉えて下さい。
で、まぁ、ディスク読み書き用のサブルーチンを用意してもらったので、
今までの面倒な作業をする代わりに、
読み書きする前にサブルーチン(f)openを呼び出すと、
OSは読み書きに必要な物をあれこれと用意してくれます。
ファイルポインタやファイルハンドルといった物は、
その必要な物のあれやこれやを参照する為の物です。
その後、(f)readや(f)writeなどで、好きに読み書きを依頼すれば、
OSが責任を持って面倒な作業を肩代わりしてくれます。
また、OSは、調停役であり監視役でもありますので、
他のプログラムとの兼ね合いやら権限のチェックやら、
もしくはシステム全体の効率なども踏まえて、
適切と思われるタイミングで物理的な読み書きを実行します。
そのような理由もあって、依頼した内容が、即反映されるとは限りませんが、
基本的にはOSが帳尻を合わせてくれます。(電源ぶち切りとかしなければ)
で、最後に、読み書きが終了した時点で、(f)closeを呼び出すと、
OSは、あれこれと用意した物を、綺麗に片付けます。
これを忘れると、OSは、まだ読み書きを続けるつもりで、
あれこれと必要な物を残したままにしておきます。
昔、新米プログラマの時、closeを忘れ、
メモリ不足でアプリが落ちた苦い経験がありますが、
きっと、多分、そのような経験を重ねる事で、
コンピュータの内部の動きが実感出来るような気もしますので、
たくさん書いて、たくさん動かしてみる、というのも大事かな、なんて思います。
http://d.hatena.ne.jp/rascal_k2/
なるほどなるほど!
「新米プログラマの時、closeを忘れ、
メモリ不足でアプリが落ちた苦い経験がありますが、
きっと、多分、そのような経験を重ねる事で、
コンピュータの内部の動きが実感出来る」
非常に実践的なアドバイスですね!
苦労する、痛い目を見る、というのがポイントですね。
すごく腑に落ちました。
ありがとうございます!
むむむ・・よくわからなくなってきました。「開く」と「読む」は違うと思ったのですが。
2011/12/04 08:05:25『「開く」と「読む」は違うと思ったのですが。』確かに違います、回答者の文を読めばそう言う理解にならないと思いますが。
2011/12/05 13:01:39紙製の「ノート」をイメージしてみて下さい。
ノートを開く事、つまり「読み書き」する為の準備が「開く」です。
「開いた」あと、書き込まれた事を「読んだり」「書き込みを修正したり」「新たに書き加えたり」「消す事」ができます。
「閉じる」は「読み書き」作業が終了した後、「読み書き」できない状態にすることです。
コンピュタのメモリーの場合、読み書き(書き換え・消去を含む)のできるものをRWM、読むことだけ出来るものをROMといいます。(1度だけ、書き込みができるものもありますが)そこが「紙製」と少し違いが有りますが、兎に角「開く」「閉じる」の概念は共通です。
既に印刷された本などは、「開いて」も基本的には内容を「読む事」だけできます、「消去・修正・追加書き込み」はできません。もっとも、行間にメモや塗りつぶしはできますが。