→-O3以上にしても、あまり結果は変わらない。自分が定義したもの?-L/usr/local/mylibs↑このdirectoryの中から優先的に探しだしてlinkする。newlib.oで"new"ライブラリにnewlibを追加nm/usr/lib/libc・静的リンク全部取り込む→programが大きくなりやすい!注意!・構造体データ構造のみを定義。logger2;→structを宣言しないで済む。typedefが主流。・Pointer※2000番地に10が入っているものとする。*p;→integer型のpointerに10が入る。func(&val);→OSがCで書かれている理由の一つ→pointer予断:javaの場合classはcall-by-referenceになる☆文字列の最後は必ず'0'が入っている。arrayは同じ意味++arrayとするとarray[1]から読み込まれる。↓データ構造の文だけincrementされるから。→箱のサイズは気にしなくてもよい。作業効率が大幅に変わってくる。minute;};アクセスするには2つの方法がある。linejavaとはちょっと違う。動的に領域を割り当てる。→deflagmentationが起こるのであまり使われない。自分のファイルがどこに保存されているかを格納しているところがあり、それを参照して、ファイルの格納場所を知る(←これをディレクトリという。)→これが最も一般的なやり方cf.ページテーブル☆directoryをどうやってストレージデバイスにおいていくか、が実現上大きな問題にある。よって、それぞれのファイルとdirectoryをどうやって実現していくか、をこれから説明する。各ファイルごとに一対一対応するデータ構造。全体の数とかを管理するファイルへのアクセスはメモリへのアクセスと比べると時間がかかる。一度オープンしたファイルに関しては一旦メモリ上にコピーされる。ファイルデータ構造というのは、readすると順番にブロックが呼び出されていく。どこから呼び出されていくかはOSが知っている。プロセスデータ構造:プロセスごとに存在し、ファイルデータ構造にある内容をファイルディスクリプタ内の情報に変換する?つまり、ファイルディスクリプタの番号はプロセスごとに閉じている。ストレージは非常に大きい。よって、メモリ上に持っていくことは出来ない。一部のファイルに関してはメモリにコピーしていることがある。ストレージデバイスで、良く使う部分のみがメモリ上にコピーされる。・i-nodeとブロックの関係i-node内部構造:内部構造は固定長で、"所有者、アクセス権、アクセスタイム、変更時間、10個のエントリ、3個のエントリ"が含まれている。ファイルシステムは何年も使われて、安全が保障されてから人に使われる。よって、開発から実用まで10年ぐらいのスパンがある。10個のエントリ:それぞれが独立に10個のブロックを指している。3個のエントリ:それぞれが独立に3個の間接ブロック(=中身はエントリ)を指していて、そのブロックはそれぞれまた別なブロックを指している。一つのブロックには256個のエントリが入る。256*256*256*1Kよって、ファイルのサイズが大きくなればなるほど深い層までもぐりこまなければならないのでperformanceは低下する。i-nodeの数だけファイルが作られるので、ファイルの上限はi-nodeの数に制限される。filesystem(4)ファイルデータ構造は、複数のプロセスから共有されることがある。例:プロセスAがオープンしたファイルデータ構造はプロセスBからも共有されるforkしたときに、現在オープンしたファイルディスクリプタは共有される。オフセット:現在書き込みしているところの次はどこに書き込むのか、という情報が格納されている。(ちなみに、Windowsでは、fork関数は使えない)・スーパーブロックスーパーブロックはファイルシステム全体を管理するデータ構造。新しくブロックを割り当てる場合、空いているところをビットマップから判断し、割り当てる。i-nodeが分かったとき、その後どうやってファイルにアクセスするか。・read/writeとファイルシステムの関係ファイルをオープンすると、今アクセスしようとしているi-nodeを認識する。tableのコピーする。ファイルデータ構造にallocationして、プロセスデータ構造からファイルデータ構造に書き込む。ファイルデータ構造のoffsetを見て、次に読み込む場所を認識。i-nodeテーブルを見て、i-nodeの何バイト目を見るかを決める。間接ブロックを辿っていく。i-node:3,5,7,19,17と書いてあるとする。10→ファイルを直接アクセスしている。ファイルのブロックが順番にアクセスされる。3は"."を指しているので、親のディレクトリに行く。tableの必要なブロックを全部呼び出す。前から順々にアクセスしていく。よって、ファイル名から、i-nodeの指すファイルを探して、ファイルをopenする。・ハードディスクが複数合った場合はどうするのか?ディレクトリ+ブロックが入っている。あるハードディスクのあるディレクトリを、他のハードディスクをマウントするようにする。tablemntはディスクテーブルにある。mntを見つけたら、ディスクBをアクセスし始める。mnt=mountディレクトリ自体は一つのtree構造であることには変わらない。・物理的なディスクの構造ハードディスク上にファイルシステムを作る場合を考える。ディスクは同心円の円がたくさん入っていて、そこにデータがたくさん入っている。その輪の中にデータを入れていく。それぞれの輪の部分をトラックという。トラックを固定長に分割した部分をセクタという。各ディスクのことをシリンダという。シリンダ->トラック->セクタアームで読み込む。ディスクが回っていて、アームと接触した部分を読み込む。よって、読み込むまでの時間は、アームのところに読み込みたいセクタまでディスクを回してあげて、完了するまでの時間。セクタを指定した位置に持っていくまでの時間よって、ハードディスクの読み込みは比較的遅い。・バッファキャッシュキャッシングとプリフェッチ。prefetch:次に読み込むものが分かっている場合は、事前にフェッチしておくこと。ファイル全体をメモリにコピーしていくことが多い。使用例:ファイルは頭から順番にアクセスしていくことが多いことを利用する。具体例:Unixは次のブロックを指していたら、その次のブロックがアクセスされる場合が多いので、事前にメモリ上に呼び出しておく。→CPUの処理効率が向上する。タイムスタンプ:いつアクセスされていたか。キャッシュの管理アルゴリズムは、タイムスタンプをつけてLRUで管理している。・ディスクの書き出し通常、バッファキャッシュで変更する時間間隔は30秒から1分に1回。これにより、同じバッファブロックに対して何度も何度も書き出すことを制限することが出来る。30秒に一回変更されているブロックをディスクに書き出す。i-nodeの変更はすぐにディスクに書き出す。→システムがファイルへ書き出す前にクラッシュしたとき、ファイルシステム全体が壊れる可能性がある。→ファイルのデータ構造をいじくるときは、常にファイルシステムの一貫性を保てるようにする。・ディスクスケジューリングFIFOスケジューリングは行ったり来たりする。アームの動く速度は遅いので、アームの動く量を最小にしたい。→C-SCANというアルゴリズムがある。いくつかのタスクを溜めておいて、ソートする。ディスクへ書き出す時間になったら、頭から尾に向かって順番にアクセスしていく。その後、アームを頭の位置に戻す。他のもディスクスケジューリングアルゴリズムがある。今は、もうちょっと頭のいいアルゴリズムも存在する。☆ハードディスクのアクセスには物理メモリと違って、トラックがどこにあるかによってアクセス時間が大幅に違う。最小限のアームの動きでアクセスする必要がある。・分散ファイルシステムリモートにあるマシンに対して同じプログラムを使いたいときはアクセス透過性と位置透過性を考える必要がある。位置透過性:ファイル名にマシン名が含まれていない。systemとは滅多に言わない。各I/Oの関係↓プログラム⇔デバイスドライバ⇔デバイスデバイスの種類はたくさんある!デバイスドライバが出来るだけ隠蔽してあげることが大切。・バスの種類PCIISASCSI...たくさん!・デバイスドライバデバイス独自のプロトコルの使用にあわせて作り、出来るだけ簡略化し、隠蔽する。デバイスドライバ:各デバイスのアクセスプロトコル(デバイスの制御手順)を実装する。デバイスドライバはデバイスを自動的に発見し、どのようにアクセスするかを自動的に決定する。デバイスドライバをインストールする。USBを読み込む昔は数百行だったが、今は数千行必要になる。→デバイスドライバを作る人はこれから増えるかもしれない。☆重要なことデバイスとデバイスドライバのやり取りをどうするかを決めること。割り込み・データの転送デバイスとCPUのデータ転送方法を決定する。デバイスの一部をメモリとしてみることが出来る。メモリアクセスとして実現されるが、アクセスコストが異なる。CPUからデバイスに対して以下のことを確認する。処理の終了あるデータの書き出すが起きているときには、それが終わるまでは次の書き出しを禁止する。ポーリングを使って、"毎回毎回処理が終わったかどうか"を聞く。データの書き出しが終わったらデータをメモリ領域に移すdataPort:どっかのレジスタ注意:イベントの発生などをポーリングで待つことはCPUの無駄遣い他の処理の合間にポーリングをすることが可能だが、システム全体の構造がクリアではなくなるよって、ポーリングを使用するときは、結果が分かるまでの時間が短い場合のみ。20μsならよいが、20nsでは無駄。今実行しているプログラムを中断して割り込んだデバイスの処理をさせるときに使う。etc.各デバイスはIRQの何番を用いるか知っている。場合によっては、割り込みベクタを複数のデバイスで共有することが出来る。→割り込み処理でもらってきた値を評価して、どのデバイスの割り込みかを認識する。これにより、IRQの数以上のデバイスを管理することが出来る。・多重割り込み割り込みが、同時系列に、複数個存在している状態のこと。割り込みには優先度が付いている。優先度を見て割り込み処理の順序を判断。level4,...,level7(優先度高→)levelの値が高い方が優先して処理する。デバイスによっては、あまり長い時間放置されると破綻するものもある。driverは連続的にパケットが送信されるので、データが来たときにすぐ処理しなければならない。・時間制約によって勝手に割り込んでくる処理がある。割り込みの数が多いと、今実行されているプログラムは実行できなくなる。時間を測定する関数を使った場合、割り込み処理によって正確な値が測定できない。割り込みハンドラを実行後レジスタを回復する必要がある。例:handler(){save_register();process_interrupt();restore_register();}割り込みは割り込みようのスタックを用いる。これにより、スタックがオーバーフローすることを避けている。・割り込みのオーバーヘッド割り込み処理のコストは結構大きい。ポーリングより大きいので、ポーリングを使った方が良いときも多い。serverはデータが来る毎に割り込みが発生する。昔は10MB/bpsだったので、そんなにCPUが早くなくても取りこぼさない。今は10GB/bpsなので、パケット量が圧倒的に増えて、割り込みが毎回発生する。serverが実行できずに割り込み処理しか実行されない。→原因:割り込みハンドラの呼ばれる頻度が高い。結果:10GB/bpsぐらいだと、CPUは有意義な計算を行うことが出来ない。対処例:割り込みハンドラの呼ばれる頻度が高くなったら、ポーリングに切り替える。システムコールの処理と割り込みハンドラ間の排他制御cf.複数のスレッドを実行するときは排他制御する必要がある。例:readやwriteがデータ構造をいじっているときは割り込みを禁止する。排他リソースのアクセス割り込み可能}注意:割り込みを禁止している時間はなるべく短くするように心がける。→割り込み禁止期間が長いと、マウスが動かなかったり、画面が表示されなかったりと、UIに悪影響を及ぼすから。Access各デバイスはメモリから直接データをやり取りする。WRITEスタートCPUとは関係なく実行される・サイクルスティーリングとは?メモリとCPUを繋ぐバスがあるDMAデバイスはバスを使う。→バスは共有されている。→DMAが動いているときはパイプラインが使えない結果:DMAはCPU上で動いているプログラムのパフォーマンスを低下させる可能性を持っている。CPUとは無関係に動いているので、どのくらい低下するのか予想するのが難しい。デバイスからDMAを使ってデータを物理メモリの呼び出されたとき、キャッシュの中をクリアする必要がある。→DMAをたくさん使うとパフォーマンスが低下する。・物理空間か仮想空間かデバイスが、MMUを解さずメモリをアクセスする場合に、デバイスにバッファのアドレスを教えるときは物理メモリアドレスを教える必要がある。CPUから見えるアドレスとデバイスから見えるアドレスは違うので、マッピングする必要がある。経緯:ページテーブルによる仮想アドレスと物理アドレスの変換のオーバーヘッドを減少するため、TLBが導入された。☆割り込みを考えるとTLBもパフォーマンスを低下させる可能性を持っている。TLBはコンテキストスイッチが起こるたびにクリアする必要がある。ページテーブルの変更はTLBに影響を及ぼさない。以下の場合において、TLBをクリアする必要がある。1↑このファイルをオープンするとメジャー番号(10)とマイナー番号(1)に変換する。arg);何でもかんでもI/Oコマンドを定義していくと移植性が低下するので、I/Oコマンドの定義は最小限にした方が好ましい。・/proc特別なデバイス。各プロセスの色んな情報を取り出すことが出来る。プロセス1のfd3の情報を取る。readシステムコールはデータが来るまで待っている。FASYNC);}・付録PDFの"付録"にデバイスドライバのサンプルソースコードがあるので目を通しておく。レジスタのセット・現在使用中のNWソケットや読み書き中のファイルなどのI/Oデバイスの利用状況のセット。上記中にあるプログラム自体は単純な動作の集合に過ぎない。単純な動作の集合から、複雑な動作になっている。execlでプロセスが変わる。→execlのあとにプログラムを書いても意味がない。環境変数を指定して呼び出す。・クライアント・サーバ型(もっとも基本的な型)UNIXにはもともとスレッドという概念がない。スレッド使えるようになったのは5年前の話プロセスをフォークすることは難しい。Write"→毎回毎回プロセスをフォークするとコストが大きい→スレッド使ったり別の方式を使ったりする。・反復サーバ型サーバがクライアントのリクエストをリプライする。受け取った順に処理する。→もし、サーバの待ち時間が長い場合はサーバが無駄に動いてしまう。→多くのクライアントからのリクエストを処理する時には向いていない。クライアント→サーバはリクエストを受け付けて、子プロセスを生成するだけですむ→ほとんどのサーバはこの形で作られている。スレッドに変えてもそんなにオーバーヘッドが減る分けではない。非同期通信リクエストが帰ってくるかどうかにかかわらず動きつづける。割り込みのような形でリプライを受け取る。下位プロトコルの指定0=default・ソケットの名前付け:bind取ってきたファイルディスクリプタに対して名前をつける。addrlen)・バイトオーダーとホストオーダーBIG-endianとLittle-endianの違い→各マシンごとに違うフォーマットを使っている場合は、意識して直す必要がある。→CPUなどのアーキテクチャを使用する必要がある。hl・ソケットアドレスの構造体-port送り側と受け手側で違うポート番号を使う必要があるので、要注意。0Xac105504)←バイトオーダーで考える必要がある。ライブラリ関数を使用(後述)引数にホスト名が入る。hostentから、アドレスが返ってくる。IPaddressを知ることができる。→いくつ、接続を格納するキューを確保するか、のために使う。sockaddr:相手のアドレスこの関数が返す引数が使うファイルディスクリプタになる。さんのコメント...もうこんなにメモしたら、wiki化も遠くはないですね。さんのコメント...wiki化できるほどarchiveっぽくなったら、このblogもそれなりに価値があがるんだがなぁ。