goo blog サービス終了のお知らせ 

gooブログはじめました!

写真付きで日記や趣味を書くならgooブログ

Prita Diary 24072025

2025-07-25 01:31:23 | 日記

オペレーティングシステム パート7
ユーザーインターフェース
ユーザーインターフェース(UI)は、人間とコンピュータのインタラクションをサポートするために不可欠です。あらゆるコンピュータで最も一般的なユーザーインターフェースは、以下の2種類です。

 

コマンドラインインターフェース(コンピュータコマンドを1行ずつ入力する)

 

グラフィカルユーザーインターフェース(GUI)(視覚的な環境を使用する。最も一般的なのは、ウィンドウ、アイコン、メニュー、ポインター要素を組み合わせたもの。WIMPとも呼ばれる)

 

スマートフォンやタブレットなどのパーソナルコンピュータやワークステーションでは、ユーザー入力は通常、キーボード、マウス、トラックパッドまたはタッチスクリーンの組み合わせで行われ、これらはすべて専用のソフトウェアによってオペレーティングシステムに接続されます。ソフトウェア開発者やプログラマーではないパーソナルコンピュータユーザーは、入出力の両方にGUIを好む傾向があり、GUIはほとんどのパーソナルコンピュータでサポートされています。GUIをサポートするソフトウェアは、入力とプレーンテキスト出力のためのコマンドラインよりも複雑です。プレーンテキスト出力はプログラマーに好まれることが多く、サポートも容易です。

 

趣味としてのオペレーティングシステム開発
趣味のオペレーティングシステムとは、既存のオペレーティングシステムから直接派生したコードではなく、ユーザーやアクティブな開発者が少ないオペレーティングシステムのことです。

 

趣味の開発は、6502マイクロプロセッサを搭載したシンプルなシングルボードコンピュータなど、自作のコンピューティングデバイスをサポートするために行われる場合もあります。また、既に広く使用されているアーキテクチャを対象とした開発が行われる場合もあります。オペレーティングシステム開発は、全く新しいコンセプトから生まれる場合もあれば、既存のオペレーティングシステムをモデル化することから始まる場合もあります。いずれの場合も、趣味の開発者は自ら開発を行う場合もあれば、同じ興味を持つ少数の、時には組織化されていないグループと交流する場合もあります。

 

趣味のオペレーティングシステムの例としては、SyllableやTempleOSなどが挙げられます。

 

オペレーティングシステムの多様性と移植性
特定のオペレーティングシステム向けに作成されたアプリケーションを別のOSに移植する場合、そのアプリケーションに必要な機能がそのOSによって異なる方法で実装されている(関数名、引数の意味など)ため、アプリケーションの適応、変更、その他のメンテナンスが必要になります。

 

オペレーティングシステムの多様性をサポートするコストは、JavaやQtなどのソフトウェアプラットフォーム向けにアプリケーションを作成することで回避できます。これらの抽象化は、特定のオペレーティングシステムとそのシステムライブラリへの適応コストを既に負担しています。

 

もう1つのアプローチは、オペレーティングシステムベンダーが標準を採用することです。例えば、POSIXとOSの抽象化レイヤーは、移植コストを削減する共通性を提供します。

 

人気のオペレーティングシステム
2024年9月現在、Android(Linuxカーネルベース)が46%の市場シェアで最も人気のあるオペレーティングシステムであり、次いでMicrosoft Windowsが26%、iOSとiPadOSが18%、macOSが5%、Linuxが1%となっています。Android、iOS、iPadOSはモバイルオペレーティングシステムであり、Windows、macOS、Linuxはデスクトップオペレーティングシステムです。

 

Linux
LinuxはGNU General Public License(GPL)に基づいて配布されるフリーソフトウェアであり、すべての派生ソフトウェアはソースコードを公開することが法的に義務付けられています。 Linuxはプログラマーが自らの使用を目的に設計したため、シンプルさと一貫性が重視され、少数の基本要素をほぼ無限に組み合わせることができ、冗長性も排除されています。

 

その設計は、マイクロカーネルを使用しない他のUNIXシステムと似ています。C言語で記述され、UNIX System V構文を使用していますが、BSD構文もサポートしています。Linuxは標準的なUNIXネットワーク機能とUNIXツール群をすべてサポートし、複数のユーザーをサポートし、プリエンプティブ・マルチタスクを採用しています。当初は最小限の設計でしたが、16MB未満のRAMでも動作可能な柔軟なシステムであり、大規模なマルチプロセッサシステムでも使用されています。他のUNIXシステムと同様に、Linuxディストリビューションはカーネル、システムライブラリ、システムユーティリティで構成されています。Linuxには、デスクトップ、フォルダ、ファイルアイコンを備えたグラフィカルユーザーインターフェース(GUI)があり、コマンドラインからオペレーティングシステムにアクセスするオプションもあります。

 

Androidは、Linuxをベースにした部分的にオープンソースのオペレーティングシステムであり、スマートフォンでの人気に加え、スマートウォッチ、自動車のダッシュボード、飛行機のシートバック、医療機器、家電製品など、GUIを必要とする組み込みシステムでも、ユーザーの間で最も広く利用されているオペレーティングシステムとなっています。Linuxとは異なり、Androidの大部分はJavaで記述されており、オブジェクト指向設計を採用しています。

 

Link 1  Link 2  Link 3  Link 4  Link 5  Link 6  Link 7  Link 8  Link 9  Link 10  Link 11  Link 12  Link 13  Link 14  Link 15  Link 16  Link 17  Link 18


Prita Diary 22072025

2025-07-23 06:25:48 | 日記

オペレーティングシステム パート6
ファイルシステムのもう1つの構成要素は、ファイル名とメタデータを、その内容が格納されているデータブロックにマッピングする辞書です。ほとんどのファイルシステムは、ディレクトリを使用してファイル名をファイル番号に変換します。ブロック番号を見つけるために、オペレーティングシステムはインデックス(多くの場合、ツリー構造で実装されています)を使用します。これとは別に、空きブロックを追跡するための空き領域マップがあり、これは通常ビットマップで実装されます。空きブロックはどれでも新しいファイルを格納できますが、多くのオペレーティングシステムは、パフォーマンスを最大化するために同じディレクトリ内のファイルをグループ化したり、断片化を軽減するために定期的にファイルを再編成したりします。

 

コンピュータのクラッシュやハードウェア障害が発生した場合でも、データの信頼性を維持することがもう一つの課題です。ファイル書き込みプロトコルは、書き込み中にクラッシュが発生した場合でも、永続ストレージが部分的に書き込まれた不整合な状態にならないように、アトミック操作で設計されています。データ破損は、冗長ストレージ(例えば、RAID(Redundant Array of Innovative Disks))と、データが破損したかどうかを検出するためのチェックサムによって対処されます。ファイルのチェックサムとバックアップを多層的に構築することで、システムは複数のハードウェア障害から復旧できます。バックグラウンドプロセスは、データ破損の検出と復旧によく使用されます。

 

セキュリティ
セキュリティとは、同じコンピュータを使用する他のユーザーだけでなく、ネットワーク経由でリモートアクセスしようとするユーザーからもユーザーを保護することを意味します。オペレーティングシステムのセキュリティは、CIAトライアド(機密性、完全性、可用性の3要素)の実現にかかっています。CIAトライアドとは、機密性(不正なユーザーがデータにアクセスできない)、整合性(不正なユーザーがデータを変更できない)、可用性(サービス拒否攻撃が発生した場合でも、承認されたユーザーがシステムを利用できる状態を維持すること)です。他のコンピュータシステムと同様に、セキュリティドメイン(オペレーティングシステムの場合、カーネル、プロセス、仮想マシン)を分離することが、セキュリティを実現する鍵となります。セキュリティを強化するその他の方法としては、攻撃対象領域を最小限に抑えるためのシンプルさ、リソースへのアクセスをデフォルトでロックすること、すべての承認要求をチェックすること、最小権限の原則(タスクの実行に必要な最小限の権限を付与すること)、権限の分離、共有データの削減などがあります。

 

オペレーティングシステムの設計によっては、他の設計よりも安全なものもあります。カーネルとアプリケーションが分離されていないものは最も安全性が低く、ほとんどの汎用オペレーティングシステムのようにモノリシックカーネルを持つものは、カーネルの一部が侵害されると依然として脆弱です。より安全な設計では、カーネルの権限を多数のセキュリティドメインに分割し、単一のカーネル侵害による影響を軽減するマイクロカーネルが採用されています。ユニカーネルは、カーネルを最小化し、他のオペレーティングシステムの機能をアプリケーションごとに分離することでセキュリティを向上させるもう1つのアプローチです。

 

ほとんどのオペレーティングシステムはCまたはC++で記述されており、潜在的な脆弱性を悪用される可能性があります。これらの脆弱性に対する防御策は講じられていますが、境界チェックの欠如によって発生するバッファオーバーフロー攻撃によって脆弱性が引き起こされます。ハードウェアの脆弱性(一部はCPUの最適化に起因する)も、オペレーティングシステムを侵害するために利用される可能性があります。オペレーティングシステムのプログラマがバックドアなどの脆弱性を意図的に埋め込んだ事例が知られています。

 

オペレーティングシステムのセキュリティは、複雑さの増大とそれに伴うバグの不可避性によって阻害されています。オペレーティングシステムの形式検証が実現不可能な場合があるため、開発者はオペレーティングシステムの強化(アドレス空間レイアウトのランダム化、制御フローの整合性、アクセス制限など)によって脆弱性を軽減します。オープンソースオペレーティングシステムへのコードの貢献者には制限がなく、このようなオペレーティングシステムは変更履歴が透明で、分散型のガバナンス構造を備えています。オープンソース開発者は、コードレビューと型チェックによって悪意のあるコードを排除し、セキュリティ上の脆弱性を発見・排除するために協力的に取り組んでいます。アンドリュー・S・タネンバウムは、すべてのオペレーティングシステムのソースコードを公開することを推奨しています。これは、開発者が秘密主義に頼り、信頼性の低い「隠蔽によるセキュリティ」に頼ることを防ぐためです。

 

Link 1  Link 2  Link 3  Link 4  Link 5  Link 6  Link 7  Link 8  Link 9  Link 10  Link 11  Link 12  Link 13  Link 14  Link 15  Link 16  Link 17  Link 18


Prita Diary 18072025

2025-07-18 18:54:28 | 日記

オペレーティングシステム パート5
仮想メモリ
仮想メモリアドレッシング(ページングやセグメンテーションなど)の使用は、カーネルが各プログラムが使用できるメモリをいつでも選択できることを意味します。これにより、オペレーティングシステムは複数のタスクで同じメモリ位置を使用できます。

 

プログラムが、アクセスできないメモリにアクセスしようとしたが、すでに割り当てられている場合、カーネルは割り込みを受けます(§ メモリ管理を参照)。この種の割り込みは、通常、ページフォールトです。

 

カーネルがページフォールトを検出すると、通常はページフォールトを引き起こしたプログラムの仮想メモリ範囲を調整し、要求されたメモリへのアクセスを許可します。これにより、カーネルは特定のアプリケーションのメモリがどこに保存されるか、あるいはメモリがまだ割り当てられているかどうかさえも、自由に制御できます。

 

現代のオペレーティングシステムでは、アクセス頻度の低いメモリをディスクなどのメディアに一時的に保存し、他のプログラムがその領域を使用できるようにします。これはスワッピングと呼ばれます。メモリ領域は複数のプログラムで使用でき、そのメモリ領域の内容は必要に応じてスワップまたは交換できるためです。

 

仮想メモリは、プログラマーやユーザーに、コンピュータに実際よりもはるかに多くのRAMが搭載されているという印象を与えます。

 

同時実行性
同時実行性とは、オペレーティングシステムが複数のタスクを同時に実行できる能力を指します。ほぼすべての最新のオペレーティングシステムは同時実行性をサポートしています。

 

スレッドは、プロセスの作業を複数の部分に分割し、それらを同時に実行することを可能にします。スレッドの数は、利用可能なプロセッサの数によって制限されることはありません。プロセッサよりもスレッドの数が多い場合、オペレーティングシステムカーネルはスレッドのスケジュール、一時停止、再開を行い、各スレッドの実行タイミングとCPU時間を制御し、各スレッドが受け取るCPU時間を制御します。コンテキストスイッチ中、実行中のスレッドは一時停止され、その状態はスレッド制御ブロックとスタックに保存され、新しいスレッドの状態がロードされます。歴史的に、多くのシステムでは、スレッドは制御を放棄するまで実行できました(協調的マルチタスク)。このモデルでは単一のスレッドがプロセッサを独占できるため、現在ではほとんどのオペレーティングシステムがスレッドに割り込むことができます(プリエンプティブマルチタスク)。

 

スレッドは独自のスレッドID、プログラムカウンタ(PC)、レジスタセット、スタックを持ちますが、コード、ヒープデータ、その他のリソースを同じプロセスの他のスレッドと共有します。そのため、スレッドを作成する方が新しいプロセスを作成するよりもオーバーヘッドが少なくなります。シングルCPUシステムでは、並行処理とはプロセス間の切り替えです。多くのコンピュータは複数のCPUを搭載しています。異なるCPUで複数のスレッドを実行する並列処理は、同時実行できる量に応じて、プログラムを高速化できます。

 

ファイルシステム
21世紀のコンピュータで使用される永続ストレージデバイスは、揮発性のダイナミックランダムアクセスメモリ(DRAM)とは異なり、クラッシュや停電後もアクセス可能です。永続(不揮発性)ストレージはバイトあたりのコストがはるかに安価ですが、アクセス、読み取り、書き込みに数桁長い時間がかかります。主な2つの技術は、磁気ディスクで構成されるハードドライブと、フラッシュメモリ(電気回路にデータを保存するソリッドステートドライブ)です。後者は高価ですが、高速で耐久性に優れています。

 

ファイルシステムは、オペレーティングシステムが永続ストレージへのアクセスを簡素化するために使用する抽象化です。ファイルシステムは、人間が判読できるファイル名やその他のメタデータを提供し、アクセスのアモゼーションによってパフォーマンスを向上させ、複数のスレッドが同じメモリ領域にアクセスするのを防ぎ、破損を識別するためのチェックサムを備えています。ファイルシステムは、ファイル(任意のサイズを持つ、名前の付いたデータの集合)と、人間が判読できるファイル名やその他のディレクトリを列挙するディレクトリ(フォルダとも呼ばれます)で構成されます。絶対ファイルパスはルートディレクトリから始まり、句読点で区切られたサブディレクトリを列挙します。一方、相対パスはディレクトリからのファイルの位置を定義します。

 

システムコール(ライブラリによってラップされる場合もあります)は、アプリケーションがファイルを作成、削除、開く、閉じるだけでなく、リンク、読み取り、書き込みも可能にします。これらの操作はすべて、アプリケーションに代わってオペレーティングシステムによって実行されます。オペレーティングシステムは、レイテンシを削減するために、最近要求されたメモリブロックをキャッシュに保存したり、アプリケーションが要求していないが次に必要になる可能性のあるデータをプリフェッチしたりします。デバイスドライバは、各入出力(I/O)デバイスに固有のソフトウェアであり、オペレーティングシステムが異なるハードウェア上で変更を加えることなく動作できるようにします。

 

Link 1  Link 2  Link 3  Link 4  Link 5  Link 6  Link 7  Link 8  Link 9  Link 10  Link 11  Link 12  Link 13  Link 14  Link 15  Link 16  Link 17  Link 18


Prita Diary 17072025

2025-07-17 02:58:09 | 日記

オペレーティングシステム パート5
コンピュータプログラムがブロックI/O書き込み操作を実行するシステムコールを実行する場合、システムコールは以下の命令を実行する可能性があります。

CPUレジスタ(プログラムカウンタを含む)の内容をプロセス制御ブロックに設定します。

デバイスステータステーブルにエントリを作成します。オペレーティングシステムは、どのプロセスがどのデバイスを待機しているかを追跡するために、このテーブルを管理します。テーブルのフィールドの1つは、プロセス制御ブロックのメモリアドレスです。
デバイスに送信するすべての文字をメモリバッファに格納します。
メモリバッファのメモリアドレスを、事前に定義されたデバイスレジスタに設定します。
バッファサイズ(整数)を、別の事前に定義されたレジスタに設定します。
マシン命令を実行して書き込みを開始します。
レディキュー内の次のプロセスへのコンテキストスイッチを実行します。
書き込みが行われている間、オペレーティングシステムは通常どおり他のプロセスへのコンテキストスイッチを行います。デバイスが書き込みを完了すると、デバイスは割り込み要求をアサートして、現在実行中のプロセスに割り込みます。また、デバイスはデータバスに整数を配置します。割り込み要求を受け入れると、オペレーティングシステムは以下の処理を行います。

プログラムカウンタ(レジスタ)の内容をコールスタックにプッシュし、続いてステータスレジスタの内容をプッシュします。

その他のレジスタの内容をコールスタックにプッシュします。(レジスタの内容をシステムテーブルに配置することもできます。)
データバスから整数を読み取ります。この整数は割り込みベクターテーブルへのオフセットです。ベクターテーブルの命令は、以下の処理を行います。

デバイスステータステーブルにアクセスします。

プロセス制御ブロックを抽出します。

書き込みプロセスへのコンテキストスイッチを実行します。

書き込みプロセスのタイムスライスが満了すると、オペレーティングシステムは以下の処理を行います。

ステータスレジスタとプログラムカウンタ以外のレジスタをコールスタックからポップします。

ステータスレジスタをコールスタックからポップします。

次の命令のアドレスをコールスタックからポップし、プログラムカウンタに再度設定します。

プログラムカウンタがリセットされたので、割り込みを受けたプロセスはタイムスライスを再開します。

メモリ管理
マルチプログラミング・オペレーティング・システム・カーネルは、とりわけ、各プログラムが現在使用しているすべてのシステムメモリを管理する責任を負います。これにより、あるプログラムが別のプログラムが既に使用しているメモリを妨害することがなくなります。プログラムはタイムシェアリングを行うため、各プログラムはメモリに独立してアクセスする必要があります。

初期の多くのオペレーティング・システムで使用されていた協調メモリ管理は、すべてのプログラムがカーネルのメモリマネージャを自発的に使用し、割り当てられたメモリを超えないことを前提としています。しかし、プログラムには割り当てられたメモリを超えるバグが含まれていることが多く、このメモリ管理システムは今ではほとんど見られません。プログラムに障害が発生すると、他の1つ以上のプログラムが使用しているメモリが影響を受けたり、上書きされたりする可能性があります。悪意のあるプログラムやウイルスは、他のプログラムのメモリを意図的に改ざんしたり、オペレーティング・システム自体の動作に影響を与えたりする可能性があります。協調メモリ管理では、1つのプログラムが不正に動作するだけでシステムがクラッシュする可能性があります。

メモリ保護により、カーネルはプロセスによるコンピュータのメモリへのアクセスを制限できます。メモリ保護には、メモリのセグメンテーションやページングなど、さまざまな方法があります。どの方法も、ある程度のハードウェアサポート(80286 MMUなど)を必要としますが、これはすべてのコンピュータに搭載されているわけではありません。

セグメンテーションとページングのいずれにおいても、特定の保護モードレジスタがCPUに対し、実行中のプログラムがアクセスを許可するメモリアドレスを指定します。他のアドレスにアクセスしようとすると割り込みが発生し、CPUは再びスーパーバイザモードに入り、カーネルが制御権を握ります。これはセグメンテーション違反(Seg-V)と呼ばれます。このような操作に意味のある結果を割り当てることは困難であり、また通常はプログラムの誤動作の兆候であるため、カーネルは通常、問題のあるプログラムを終了させ、エラーを報告します。

Windowsバージョン3.1からMEまではある程度のメモリ保護機能を備えていましたが、プログラムは簡単にそれを回避することができました。セグメンテーション違反が発生したことを示す一般保護違反が生成されますが、それでもシステムはクラッシュすることがよくありました。

 

Link 1  Link 2  Link 3  Link 4  Link 5  Link 6  Link 7  Link 8  Link 9  Link 10  Link 11  Link 12  Link 13  Link 14  Link 15


Prita Diary 14072025

2025-07-14 23:16:52 | 日記

オペレーティングシステム パート4
ソフトウェア割り込み
ソフトウェア割り込みは、イベントが発生したことをプロセスに通知するメッセージです。これは、イベントが発生したことを中央処理装置(CPU)に通知するハードウェア割り込みとは対照的です。ソフトウェア割り込みはハードウェア割り込みに似ており、現在実行中のプロセスから別のプロセスへの変更が行われます。同様に、ハードウェア割り込みとソフトウェア割り込みはどちらも割り込みサービスルーチンを実行します。

 

ソフトウェア割り込みは、通常発生するイベントである場合もあります。タイムスライスが発生することが予想されるため、カーネルはコンテキストスイッチを実行する必要があります。コンピュータプログラムは、データが多すぎてアルゴリズムの実行に時間がかかりすぎる場合に備えて、数秒後にタイマーが切れるように設定する場合があります。

 

ソフトウェア割り込みは、不正なマシン命令などのエラー状態である場合もあります。ただし、最も一般的なエラー状態は、ゼロ除算と無効なメモリアドレスへのアクセスです。

 

ユーザーはカーネルにメッセージを送信して、現在実行中のプロセスの動作を変更できます。例えば、コマンドライン環境では、割り込み文字(通常はCtrl+C)を押すと、現在実行中のプロセスが終了されることがあります。

 

x86 CPU でソフトウェア割り込みを生成するには、アセンブリ言語の INT 命令を使用します。構文は INT X で、X は割り込みベクターテーブルへのオフセット番号(16 進数形式)です。

 

シグナル
Unix 系オペレーティングシステムでソフトウェア割り込みを生成するには、kill(pid,signum) システムコールを使用して別のプロセスにシグナルを送信します。pid は受信側プロセスのプロセス識別子です。signum は送信するシグナル番号(ニーモニック形式)です。(kill という不快な名前が付けられたのは、初期の実装ではプロセスを終了するだけだったためです。)

 

Unix 系オペレーティングシステムでは、シグナルはプロセスに非同期イベントの発生を通知します。非同期通信には割り込みが必要です。プロセスが別のプロセスと非同期通信する必要がある理由の 1 つは、古典的なリーダー/ライター問題のバリエーションを解決することです。ライターは、出力をリーダーの入力ストリームに送信するためのパイプをシェルから受け取ります。コマンドライン構文は alpha | bravo です。 alpha は計算準備が整うとパイプに書き込み、その後待機キューで待機します。bravo はその後準備完了キューに移動し、すぐに入力ストリームから読み取りを開始します。カーネルはパイプ処理を調整するためにソフトウェア割り込みを生成します。

 

シグナルは7つのカテゴリに分類できます。カテゴリは以下のとおりです。

 

プロセスが正常に終了したとき。
プロセスにエラー例外が発生したとき。
プロセスがシステムリソースを使い果たしたとき。
プロセスが不正な命令を実行したとき。
プロセスがアラームイベントをセットしたとき。
プロセスがキーボードから中断されたとき。
プロセスにデバッグ用のトレースアラートが発生したとき。
ハードウェア割り込み
入出力 (I/O) デバイスは CPU よりも低速です。そのため、CPU が各 I/O の完了を待たなければならないと、コンピュータの速度が低下します。代わりに、コンピュータは I/O 完了時に割り込みを実装することで、ポーリングやビジーウェイトの必要性を回避できます。

 

一部のコンピュータでは、文字または単語ごとに割り込みが必要になり、CPU 時間を大量に消費します。ダイレクトメモリアクセス(DMA)は、デバイスがCPUをバイパスしてメインメモリに直接アクセスできるようにするアーキテクチャ機能です。(アーキテクチャとは別に、デバイスはメインメモリとの間で直接またはバス経由で直接メモリアクセスを実行できます。)

 

入出力
割り込み駆動型I/O
コンピュータユーザーがキーボードでキーを入力すると、通常、文字が即座に画面に表示されます。同様に、ユーザーがマウスを動かすと、カーソルが即座に画面上を移動します。キー入力やマウスの動きごとに、割り込み駆動型I/Oと呼ばれる割り込みが発生します。割り込み駆動型I/Oは、プロセスが文字またはワードを送信するたびに割り込みを発生させる場合に発生します。

 

ダイレクトメモリアクセス
ハードディスクドライブ、ソリッドステートドライブ、磁気テープドライブなどのデバイスは、非常に高速にデータを転送できるため、バイトまたはワードが転送されるたびにCPUに割り込みをかけ、デバイスとメモリ間でバイトまたはワードを転送させると、CPUに過大なCPU時間が必要になります。その代わりに、チャネルやダイレクトメモリアクセスコントローラなどのハードウェアによって、CPUとは独立してデバイスとメモリ間でデータが転送されます。すべてのデータが転送されたときにのみ割り込みが送信されます。

 

Link 1  Link 2  Link 3  Link 4  Link 5  Link 6  Link 7  Link 8  Link 9  Link 10  Link 11  Link 12  Link 13  Link 14  Link 15