Linuxの設計に関する世界観です。
普段、WindowsやLinuxなどのOSを使っていると、GUIのアプリケーションは、さまざまなボタンやメニューを持ち、スクロールバーでスクロールしたり、マウスやキーボードを使って画面を操作することができ、画面には文字列やグラフィックスが表示されます。
これらをインタラクティブに、余計な待ち時間や画面の停止なく行えるのは、GUIツールキットやウィンドウシステムのおかげです。
Linuxカーネルは、こうしたGUIの仕組みの裏側に居て、同時にCUIの裏側にも居て、このようなGUIやCUIのシステムを裏方として実現しています。
CUIでLinuxを使った時、Linuxを起動すると、initがデーモンなどを一通り起動した上で、CUIのコマンドプロンプト、すなわちシェルが起動します。
この時、シェルにコマンドを入力すると、プログラムが実行されます。プログラムはシェルから実行され、実行中のプロセスが起動し、プログラムは目的の処理を行い、結果を表示して終了し、次の入力を待つシェルに戻ります。
このような時に、カーネルは何をやっているかというと、「プログラムそのものの実行の実現」と、「ユーザーがプログラムの実行中にも操作ができるような並列処理の実現」をしています。
Linuxカーネルは、プログラムそのものが実行できるように、プログラムの実行形態そのものを管理し、プロセスにシステムコールやAPIの実行環境と、CPUとメモリ、あるいはハードウェアのリソースを与えます。
同時に、プログラムが実行している時にでも操作が可能となるように、IOデバイスやハードウェアデバイスを制御した上で、並列処理を実現し、またプログラムを同時に複数実行できるようにしています。
このような機能は、ハードウェアの機能を応用することで行われます。Linuxカーネルは基本的にハードウェアの延長線上の機能を与えるシステムです。IO処理やファイルシステムやネットワークのような仕組みは、ハードウェアの機能を基に、Linuxカーネルが独自にハードウェアの機能を応用し、活用することで実現しています。
このようにすることで、Linuxではインタラクティブにプログラムを実行し、プログラムの実行中であっても別の作業を行ったり、プログラムを複数同時に実行することができます。
Linuxのプログラムには、大きく分けて「コマンド」と「デーモン」がある。
普通、Linuxでは、コマンドシェルと呼ばれる文字の入力と出力を行う端末上のプログラムで、コマンドを実行することで、作業を行う。
コマンドは、その時限りのプログラムであり、実行した時点でのこうしたプログラムのことを「プロセス」と呼ぶ。
コマンドは通常、実行して、処理が完了した段階で終了し、シェルの入力に戻る。もし、プログラムの実行中にもなんらかの作業を別にやりたい場合は、コマンド入力の最後に&をつけることで、コマンドをバックグランドで実行できる。端末からFirefoxのようなGUIアプリケーションを実行する場合はそのようにするとよい。
コマンドは、正常に完了した場合、基本的に何も出力することなく終了する。ファイルの操作などで出力が必要ならば、出力をした上で終了する。出力を別のプログラムの入力に与えたい場合は、パイプを使うことができる。パイプを連続して使うことで、「findの内容をxargsやgrepに送り、その内容をsedで変換してbashに送る」といったように、複数のプログラムをパイプラインのように繋げることができる。また、ファイルに出力を保存したり、ファイルから入力したい場合はリダイレクトを使う。
コマンドでのプログラムと違って、デーモンはシステムで常に実行され続ける。システムが起動した段階でinitやsystemdなどによって起動し、システムが終了するまで常駐し続け、たとえばネットワークサーバーであれば外部のリクエストに応答する形で処理を行う。
また、UNIXにはコマンドでもデーモンでもないカーネルという特殊なプログラムがある。カーネルはハードウェアのさまざまな制御を行い、システムコールを通じてユーザープログラムに対してコンピュータにとって基本的かつどんなプログラムでも必要となるような機能を提供する。コマンドもデーモンも、すべてカーネルの上で実行される。カーネルは仮想化システムなどを使わない場合、ホストマシン1台につき1つ起動する。
UNIXのプログラムにおいて、プログラムは静的に記述されたテキストデータである。この実行内容を変えるには、いくらかの方法がある。
主なものを挙げると、
・プログラムをconfigureやmakeでビルドする際に、コンパイルオプションを用いて、プログラムそのものの記述をマクロ的に変える方法。
・プログラムが実行される際に、コマンドオプションやファイル名やディレクトリ名やサブコマンド名などを入力することで、プログラムに応じた適切なオプションを与える方法。
・シェル変数や環境変数を用いて、プログラムに環境への適応をさせる方法。たとえばカレントディレクトリやロケール(日本語環境など)などの指定。
・設定ファイルを通じて、プログラムの内容を書き換えることなく、外部に存在する/etc/fstabやhttpd.confやphp.iniなどの内容を変えることで、プログラムのビルドや実行方法とは無関係に設定を記述する方法。設定ファイルは、システム全体の設定ファイルは/etcなどに存在する。ユーザー別の設定はホームディレクトリ以下にドットファイル(.で始まるファイル)として存在する。ドットファイルは隠しファイルとして扱われる。
・プログラムが処理すべきデータそのものを変える方法。たとえば*.htmlをWebブラウザで開く場合など。データがローカルにあるとは限らない。ネットワーク上にある可能性もある。
・UNIXのシェルやX11などに特有の方法として、シェルスクリプトを初期化に使う方法。設定内容をシェルの記法で記述し、その内容を/bin/shで実行する。
・そのほかいろいろな方法。たとえばMakefileとかm4などのマクロを使ったり、PerlやPythonスクリプトを書いたりする方法がある。Debianのdebパッケージならdebconfなどでパッケージ導入の際に設定ファイルをユーザーによるオプションとして設定できる。
・また、GNOMEアプリケーションの場合はWindowsのレジストリと同様の設定内容データベースであるdconfによって格納され、インタラクティブに変更を監視してdconfの設定をアプリケーション側にコールバックで通知できるようになっている。
このように、UNIXではさまざまな方法を用いて、プログラムの実行内容を変えることができる。
コマンドではなくデーモンの起動の場合、起動するデーモンを設定したり、順序を変えたりするためには、昔ながらのSysV initであれば、起動スクリプトと呼ばれる順序関係のあるシェルスクリプトで起動処理を記述することが一般的だったが、これはもう古い方法。今のLinuxでは、systemdのUnitファイルを使うことで、並列処理のできるデュアルコアCPUで最大限パフォーマンスを向上させ、起動時間の短縮を図っている。だが、いったん起動してしまえばデーモンプログラムの実行速度はどちらでも変わらない。systemdはUNIXの古きよき慣習を破壊しているため、ネット上(たとえば英語版のSlashdotなど)では批判的な意見も多い。
プログラムは、バイナリファイルに過ぎないため、パーミッションで実行許可がついていれば、どこでも実行できる。基本的には/binや/usr/binなどのPATHに入っていることが望ましいが、カレントディレクトリのプログラムであれば./hogeのように相対パスで実行することもできる。
ただし、共有ライブラリを使っている際には注意が必要。共有ライブラリとは、単一で実行されるプログラムではなく、ほかのプログラムによって関数などのAPIが呼び出されるプログラムのこと。共有ディレクトリは/usr/libなどのディレクトリに置き、/etc/ld.so.confで設定を行い、設定を変更したらldconfigを使ってキャッシュを更新する必要がある。
また、ビルドされたプログラムを利用する時には必要ないが、ライブラリを使ったプログラムをビルド・再コンパイルする際にはヘッダファイルが/usr/includeに置かれている必要がある。ヘッダファイルはディストリビューションのパッケージであれば、一般的に*-develパッケージに含まれていることが多い。
このほか、カーネルの中の一部の機能を必要に応じて有効にしたい場合などは、カーネルモジュールを使うことができる。これはカーネルのコンパイルの際に有効にしたり無効にしたりできる。コマンドでもロードできるが、カーネルの起動時やudevなどによって自動でロードされる。カーネルのコンパイルは面白いので、一度挑戦してみることをおすすめする。
このような内容は、Gentoo Linuxのインストールを行うと実践的に学ぶことができるため、僕はGentooに触れてみることをおすすめする。
僕は、OSとしての設計には、「コンパイル」と「アーキテクチャ」があると思う。
まず、アプリケーション・プログラムは、コンパイルした時に動作が変わる部分と、実行時に動作が変わる部分がある。
たとえば、コンパイルオプションと設定ファイル。
コンパイルオプションで指定した内容は、再コンパイルした時にしか変わらない。アプリケーションの動作はコンパイルした時に静的に決定される。
この場合、設定内容を変えるためには、再コンパイルする必要がある。
それと対照的に、実行オプションや設定ファイルで設定する方法なら、実行時に動的に決めることが出来る。
これにより、多様なプログラムの動作が期待出来る。
これと同じことが、テキストファイルを入出力するプログラムや、モジュールを動的に読み込むプログラムについても言える。
また、スクリプト言語のようなプログラムでは、スクリプトがそのまま動的に読み込むテキストファイルとして扱われる。
簡単に言ってしまえば、コンパイルする時に設定を決定するプログラムと、動的に設定を実行時に読み込むプログラムがある。
後日注記:つまり、コードを変えなくても、設定ファイルやオプションを変えることで、プログラムの動作を変えることができる。さらに言えば、これをシェルスクリプトのようなインタープリタのプログラムにすることで、単なる設定ファイルではなく、プログラムの実行によって設定を行うことができる。すなわち、「プログラムによってプログラムの動作を変えられる」。UNIXにはそうした設計思想がある。
後日注記:設定ファイルには、UNIX由来のプログラムではシェルから設定できる環境変数や空白やタブによって区切られたプレーンテキストの設定ファイルを用いるほか、JavaやPHPではXMLやINIによる設定ファイルも見られる。もちろん、GNOMEやKDEでは設定はGUIで行うことができる。もともとコマンドラインのプログラムであっても、GUIによる設定ユーティリティが用意されている場合もある。プログラムによって設定の種類は千差万別である。
後日注記:基本的に、「プログラムコードそのもの」「ビルドオプション」「実行オプション」「データファイル」「設定ファイル」「シェル変数・環境変数」などがプログラムの実行内容を決める。また、システムに存在する「ライブラリ関数(libc, Xlibなど)」「カーネルのシステムコール」「ヘッダファイル」などもシステムとしてプログラムの実行内容に作用する。またシェルスクリプトである「初期化スクリプト」や「デーモンの起動スクリプト」もシステムの設定と管理に使用する。
後日注記:この中で、重要なのは「実行オプション」と「データファイル」です。なぜなら、プログラムとは事前に記述された自動の実行処理にすぎません。システムをプログラムとして見た時に、「データファイルに対してコマンドを実行する」ということと「実行オプションによってさまざまな変化を付けてコマンドを実行できる」ということが、プログラムを使うだけではなく作る時に重要になります。また、システム全体としてみると、設定ファイルや初期化スクリプトによるシステム構成や環境変数などの設定内容が、「コマンドの実行形態やシステム全体の設定そのものを変える」ということになります。
コードとファイルも参照のこと。
次に、CPUアーキテクチャへの対応がある。
Windowsなどでは、x86/x86_64にしかOSは対応しないが、UNIXはC言語で書かれたポータブルなOSであるため、移植性が高いと言われる。
これは、プログラム自体をC言語と言う、さまざまなバイナリフォーマットに対応出来るいくらか高水準な言語で書くことによって、移植性を高める、と言う発想だ。
カーネルの低レベルな部分で、最小限アーキテクチャ別のCとアセンブリ言語でのプログラムを書いて、高レベルな部分は全て共通のC言語で書く。
GCCと言うさまざまなアーキテクチャのバイナリが吐けるコンパイラを使うことで、さまざまなプログラムを違うCPUのLinuxで使うことが出来る。
後日注記:OSやカーネルの役割として重要なのは、CPUアーキテクチャやハードウェアデバイスの差異を共通のインターフェースで隠蔽し、共通化すること。どんなプログラムであっても、再コンパイルすれば別のCPUアーキテクチャで動作するようにする。
後日注記:プログラムの内容がビルドオプションで決まるということは、すなわちビルドされた段階で、どれかのCPUアーキテクチャの機械語のバイナリファイルが吐かれて、そのCPUアーキテクチャに依存するということを意味している。すなわち、ソースコードの時点ではどのCPUアーキテクチャ向けでもなかったプログラムが、ビルドした時点でどれかのCPUアーキテクチャ向けに依存するようになる。ソースコードのビルドにはそのような意味がある。また異なるCPUアーキテクチャ向けに対応するには、カーネルやビルドツールのほうでも対応する必要がある。
CPUアーキテクチャや移植性も参照のこと。
また、パッケージ管理と言う意味では、「依存性の解決」と「自動更新」を行うことが出来る。
低レベルな部分では、パッケージ管理はOSにインストールされている全てのパッケージの依存関係の解決を行うことが出来る。
これにより、OSが壊れることを防ぎながら、簡単にUNIX上のユーザーランド・アプリケーションの無数のパッケージを管理することが出来る。
Linuxが管理しやすいのは、パッケージ管理のおかげでもある。
パッケージ管理があるおかげで、不要なシステムの破壊を防ぎ、簡単にディストリビューションと言う「OS全体」を作って配布することが出来る。
先に言う、コンパイルと設定ファイルの考え方のようなものが、パッケージ管理システムにおいても良く似た形態で見ることが出来る。
また、apt-getやyum/dnfに見られるように、パッケージは自動更新され、常に最新のパッケージリストをダウンロードし、最新のパッケージに更新することが出来る。
僕は、ここでもGentoo Linuxに注目したい。Gentoo Linuxでは、Portageと言う独自のパッケージ管理を行っている。
使うコマンドはemerge、パッケージの記法はebuildである。常に最新のebuildをダウンロードしておき、emergeを使ってパッケージをインストールする。
だが、Gentoo Linuxの特徴は、「全てのパッケージがソースベース」であることだ。emergeは、ほとんど全てのパッケージをソースコードからコンパイルする。
ソースコードからコンパイルすることで、先に言ったようなコンパイルにおける設定をmake.confで変えることが出来る。
また、USEフラグを使うことで、たとえばGTK+サポートをONにするかOFFにするかのような、パッケージのコンパイル依存関係を変えることが出来る。
そして、基本的にパッケージは設定ファイルを手動で設定する。rc-updateのようなランレベルとデーモンの設定のようなものはある。
ここで、DebianやRed Hatのようなシステムに戻ると、DebianやRed Hatでも、ソースパッケージとバイナリパッケージと言うものがある。
そして、それはGentoo Linuxと同じであり、そこまで考えると、ソースコードとUNIXシステムの関係が分かりやすく、全貌を掴むことが出来る。
後日注記:Gentoo Linuxでは、make.confを設定することで、システム全体のビルドオプションを設定した上で、システムのソフトウェア全体を再ビルドできます。FreeBSDにおけるmake worldと同様です。
最後に、Linuxカーネルである。Linuxカーネルは、手動でコンパイルすることで、さまざまなオプション機能を有効にしたり、無効にしたりすることが出来る。
後日注記:カーネルの手動コンパイルを行うことで、カーネルのチューニングや機能のON/OFFを設定できます。たとえば特定のファイルシステムのサポートを追加したり、特定のデバイスドライバを有効にしたり、特定のプロセッサやCPUアーキテクチャへの対応を設定したりできます。
Linuxカーネル(ソースコード・カーネルモジュール)を参照のこと。
入出力とパイプによって、コマンドを関係で扱うことが出来る。
$ find / | less
後日注記:UNIXには、「機能は単一のプログラム単体からではなく、複数のプログラムの関係から生まれる」という哲学がある。上の例では、findで検索したファイル一覧をlessというページャに送って、lessを使ってスクロールしてfindの内容を見ている。たとえば、findではなくgrepの結果であっても、コマンドの入力を変えるだけで、lessを使ってあらゆるコマンドの出力結果を簡単に閲覧することができる。
UNIXシステム管理(シェル)やBashのさまざまな機能も参照のこと。
シェルは、新しいプログラムを実行するためのプログラム。
コマンド入力を受け付けて、PATHからバイナリを探し、子プロセスを起動出来る。
シェルの初期化ファイルを使うことで、シェルの起動時にシェルの初期化をすることが出来る。
X11でも、初期化ファイルを使うことで、Xサーバーの設定をしたり、最初に起動しておくアプリケーションの起動が出来る。
コマンドプログラムとX11のGUIプログラムは大きく異なっているが、XクライアントはXサーバーに対する指令・通信を行う、Xプロトコルを使ったアプリケーションである、と理解すると、理解しやすい。
また、CUIとは異なり、プログラムは階層化され、ツールキット・ライブラリであるGTK+やQtからXlibを操作する形になる。
これは、命令である、と言うよりは、どのような仕組みを用いてX11を操作するか、と言う、GUIにおけるRailsのようなフレームワークである、と理解すれば良い。
X11も参照のこと。
パーミッションは、システムを守る仕組みで、Windowsのような管理者権限を標準で使うOSとは違い、Linuxにはファイルやディレクトリごとにユーザーの権限が決まっている。
ユーザーに見せられない機密データの閲覧や、実行してはならない実行ファイルの実行に、制限をかけることが出来る。
rootユーザーだけが、全ファイルの全データにアクセス出来る。
これは、Linuxのセキュリティに大きく貢献しているが、その反面、サーバーなどでのファイル管理において、ややこしい仕組みとなっている。
だが、分かってしまえば簡単だ。
UNIXシステム管理(ユーザーとパーミッション)も参照のこと。
もっとセキュリティを高めたい場合は、SELinuxのようなセキュリティ強化の仕組みを使うことも出来る。HTTP、FTPなどのプロセスごとにアクセス制限をかけ、rootユーザーを含む全ユーザーへのアクセス制限をかけ、rootユーザーへの権限の集中をもっと分散させることが出来る。
AppArmorと言うSUSEなどの方式もある。
SELinuxはFedoraやRHELなどでは標準で有効になっている。Fedoraは、そういう、Red Hatのやりたいように作っている、と言う側面がある。
最後に、関数のレベルでの抽象化がある。
関数は自動の実行処理であり、引数に何かの値を設定することで、実行内容が変わる。
僕は、関数の本質とは、再利用性にあると思う。ライブラリのように、既に書かれている関数を再利用して、その利用を書く。
だから、正しいプログラミング言語とは、Rubyのように、継承やブロックによって、既に書かれているコードの内容を上書き出来るようでなければならない。
一度書かれたコードを無駄にせず、再利用と共有を行うために、関数は設計されなければならない。
ある意味、オブジェクト指向も、そのように考えれば、「クラスの再利用」であると理解することが出来る。
Linuxやオープンソースでプログラミングの勉強がしたいなら、いくつかの選択肢がある。
1.XAMPPなどでWindowsのままでローカル環境でPHPを動かす。→出来るようになってきたらLinuxを入れれば良い。
2.Eclipseを使うことでJavaプログラミングをする。→出来るようになってきたら、Tomcatを入れるなりSwingで開発するなりすれば良い。
3.paiza.IOやAWS Cloud9でブラウザでさまざまな言語のプログラムを動かす。→簡単で手軽。プログラミング入門におすすめ。
4.ドットインストールやProgateなどの入門サイトを閲覧する。→プログラミングの経験が全くなく、基本を知りたい方におすすめ。
5.ネットでリファレンス的なホームページを読む。→既に言語のことを知っていて、「詳しいところだけ調べれば作れる」ような中級者におすすめ。
6.Linuxでruby, python, perl, gcc, makeなどを使って自分で作って勉強する。→これが一番難しい。どのように開発していいか分からない。
特に、誰もが6を目指して6で挫折する。いきなりLinuxを無計画に入れるよりも、「どんな風に勉強していくか」を良く考えた方が良い。Linuxは行き当たりばったりに開発者へとなる道は存在しない。
本当は、
7.Linuxの仕組みを知ってMINIX本などを読み、ソフトウェア技術者としての基礎と理解を身につける。
というとても良い「正解の道」が存在する。それが、このホームページだと思えば良い。このホームページを読みながら、MINIX本(「オペレーティングシステム 設計と理論およびMINIXによる実装」というA・S・タネンバウムの書籍)を読むのが一番、ソフトウェアの開発がきちんと分かる。僕はこの7をお勧めする。プログラミングの入門ではない、技術者への道である。Linuxの仕組みを知って、ITライターのみんなが「参考文献」などに挙げている定番の書籍を読めば良い。それが一番、きちんとプログラマになれる。
本当は、最近のプログラミング環境というのはとても簡単になってきていて、WindowsでRubyを簡単に動かすこともできるし、あるいはgitなどを使って簡単に既存のオープンソースプロジェクトのリポジトリをクローンしたり、貢献者として参加することが簡単になっている。だが、簡単にはなったものの、それで逆に技術レベルが下がっているところがある。Railsなんか、コードをほとんど書かず、ファイルを自動作成するだけでサービスの7割は完成する。ある意味、Debianのような「玄人向けディストリビューション」を入れることは、エンジニアであっても(特にシステムエンジニアでも、ネットワークエンジニアでも、フロントエンドでもバックエンドでも)、学生であっても、アマチュアやボランティアの開発者であっても良い経験になる。特にバックエンドを作りたいのであれば、明らかにLinuxは参考になるだろう。そういう、「教養のためにLinuxを入れる」のは良いことだが、「Linuxを入れれば魔法のようにIT業界に参入できる」というものではない。
「自分次第」という言葉は「自分の能力次第」という言葉だ。自分の能力を高めたい人は、きっとDebianやGentooなどの「手動管理と手動設定」の知識が将来活かせるようになるだろう。だが、知識の全くない人間には、そもそもどういう風に管理して良いか分からない。ネットワークなどの仕組みを勉強し、公式のマニュアルやハンドブックを読むことで、きっとLinuxのことをマスターできるようになると思う。
もうひとつ、挫折しやすい選択肢として、「本を読む」というのが挙げられる。本を読むのはマスターの道としては王道だが、僕は「きちんと読まなくて良い」という。IT系の本は、きちんと読んでいるとどうでも良いプラクティスやイディオムばかりが載っていて、作りたいものがなかなか作れない。だから、僕は「調べ読み」をすることをおすすめする。開発環境をインストールしたら、本を読むよりも自分の分かる知識だけで「考えてコードを書く」こと。そして、必要になってきたら、その時その時に応じて「調べ読み」をする。そうした自分の経験は必ず生きる。だから、本を読んでプログラミングの勉強をすると、99%挫折する。本なんか読まずに自分で考えてコードを書くこと。その方が良い。
一応番号をつけておくと、
8.教養のためにLinuxサーバーの管理方法を学ぶ。→悪くない。Linuxの管理が分かるとネットワークエンジニアになれる。
9.本を読む。→間違い。99%挫折する。
10.いきなりコードを書いてみる。→大正解。いきなりC/C++でコードを書くと成功する。たまに壁にぶち当たった時は、自分の技術と能力で克服していこう。
プログラミング初心者にありがちな迷いとして、「何を作ったら良いか分からない」というものがある。これは、作れそうなものならなんでも作っていくのが良いと思う。タブブラウザを作りたいなら、継承で作ったらどうか、インスタンスの包有で作ったらどうかなどを考える。あるいは、書籍にあるサンプルプログラムを改良して、自分のプログラムとして拡張していく。だが、それくらいの方向が分かったら、僕は「僕なんかが教えるよりも、あなた自身で考えてほしい」と思う。僕はこう見えて、プログラミングが今でも出来ない馬鹿だから、こんな人間の言うことは参考にしない方が良い。プログラミングの王道というものはなくて、往々にして「出来ない人間が適当なことを言っている」という現象が発生する。だから、上の10を無理にやろうとしなくて良い。僕の経験から言って、C++の開発はとても難しい。昔Delphiでコードを開発していた時も、修正できないバグに遭遇したり、Object Pascalの記法が良く分からなかったりして、すぐに挫折してしまった。いきなりコードを書くのは、必ずしも正解ではない。僕が本をきちんと読んでいないだけで、「標準C++の基礎知識」などはとても良い本で、これを読めば既にC++のプログラマになっていると書かれている。Windowsなどのプログラミングにおいても、自分の力で習得できるぐらいになるだろうと、この本の最後に書かれている。だから、こうした本を全否定するのは良いことではない。Ruby作者のmatzも、昔から本を読むのが好きだったという。本を読むのは決して間違いではない。
Delphiのように、マイナーな言語をした経験というのは別の場合にも活きてくることがある。特に、僕はC#でプログラムを書いた時に、Delphiの経験が生きて、すぐにネットワークからデータを取得するコードを別のURLで書き変えられるようになった。これによって、2ちゃんねる以外の掲示板で2ちゃんねるブラウザを使えるようにした。(正確に言うと、ログデータを読み込んだりネットワークからダウンロードして閲覧できるようにしただけ。)
だが、子供の知識と理解というものには限界があって、そのごろはまだ10代半ばだったが、すぐに問題やバグが発生して、これも挫折してしまった。明らかに、大人の方が、そうした時の問題の対処能力のようなものは存在する。逆に、大人は今までの自分の知識は分かっていても、外部から知識をとり入れるということをしない。よって、僕は今でも15年前のネット知識のままで止まっていて、最新のLinuxの情勢などはきちんと分かっていなかったりする。言えることとしては、「自分独りで全部やろうとするよりも、周りの人と協力したり教え合ったりする環境があった方が良い」ということ。自分独りでプログラムを完成させなくて良い。これは、最近のデザインの経験から言える。ほとんどは他人に教えてもらったことや、データを見てやり方を真似たり盗んだりしたことであって、ネットの文章というものはほとんど読んでいないし、たまにまとまっている書籍を読んで方法を吸収するぐらいで、あとは「現場で自分の力で作る」しかない。これはデザインだけの話ではない。プログラミングについても、みんなで作るべきものを決めていかないといけないし、大学のような場所でも研究室のみんなで本を読んだり実験したりしないといけないのだ。
僕は一時期2ちゃんねるにとてもハマっていたことがあって、DelphiでOpen Janeを改造したり、C#でtwintailを改造したり、PHPでp2のコードを参考にしたりしていた。ログデータのダウンロードと閲覧のような改造は、元のプログラムが良く出来ていたせいで、正規表現とC#の少しの知識があれば簡単にできてしまった。Delphiについては、15歳ぐらいでポトペタでGUIを書き、既に書かれていたコードを見ながらコピペで作っていた。また、PerlのCGIによるゲームのようなものを作って、適当にWindows上のApacheとブラウザ上で動かしていたこともある。それは14歳の、不登校になる前、中学生時代に、ネットのブラウザゲームにハマって、「自分でもこんなゲームを作りたい」と思って始めた。だが、逆にブラウザゲームのようなものに飽きる方が早かった。17歳のごろは、Wikipediaのマクロテンプレートのようなものも書いていた。だが、きちんとしたプログラムのようなものは作れなかったし、プログラミングの知識もそんなに多くは無かった。逆に、多くの時間をLinuxのシステムを知ることに費やした。15歳から17歳の多くの時間、LinuxのWikiでLinuxの仕組みをGentoo Linuxを参考にして書いていた。Windowsよりも本気でLinuxが好きで、Linux向けの何かしらのアプリケーションを作りたかった。無料でオープンという環境は魅力的だったが、インストールしても何も分からず、設定はそんなに意味がなかった。ここでも2ちゃんねるのWikiが参考になった。そして、Gentoo Linuxをやりだした辺りから、本格的にプログラミングの世界が分かった。それくらいしか、昔は分かっていない。ある意味、本当にプログラマのたまごレベルの人間だが、Gentoo Linuxが好きだった。
蛇足になるが、MINIX本はとても良い本だ。僕の知りたかったことがたくさん書いてある。このホームページを読み終えた後は、MINIX本を読んでほしい。特に、OSについて理解したいのであれば、システムコールや実装のことがきちんと書かれているこの本がお勧めだ。リーナス・トーバルズも読んだ本だ。そして、内容はUNIXオペレーティングシステムの概論、プロセス、入出力、メモリ管理、そしてファイルシステムだ。巻末にはMINIXのソースコードが掲載されていて、自分で作ろうと思った時に取り掛かりやすい。この本をきちんと読めば、きちんと分かるだろう。
思えば、ここまで文章を書きながら、色んなことがあった。18歳ぐらいから、僕は大量に毎日文章を書くようになった。最初は半ばおかしな戦いをやっていたが、自分の人生から心理学や経済学を考え、哲学や歴史の勉強をし、宗教的な境地に達して、その後はデザイン技術の習得や、科学(人文・社会・自然)やコンピュータのことを学び直した。今29歳だが、そろそろ30歳になる。詩集も出版できたし、きちんと(障害者施設ではあるが)福祉的な労働をしている。これからは、ピアノや英会話について、もっと継続して学校に通いたいと思う。デザイナーになれるかは分からないが、僕は就職したいとはあまり考えていない。もっと教養とスキルを増やして、色んなことが分かる人間になりたい。そのために、このホームページの執筆をやめることはないだろう。だが、本当は、この文章執筆をやめれば、やっと精神の病気がはじめて治るのは分かっている。だが、むしろ、治りたくない。いつまでも楽しいこの病気を楽しみたい。まさに、「楽しい地獄」である。この地獄は神と永遠の時のある、素晴らしい魂の“死”の経験だった。
Windows 95が世に出た時に、子供心に「すごい。ウィンドウがぐにゅぐにゅ動く。こんなに面白いものはない」と思った方は多いのではないでしょうか。特に、「このOSというものの仕組みを知りたい」とか、「自分でも作ってみたい」と思った方が多いと思います。
また、ペンチや金づちで現実の道具を作るよりも、とても楽しく開発できるかのような印象を抱いた方が多いと思います。テレビゲーム世代には、感動もひとしおだったと思います。
ですが、Windowsは公開されていません。ソースコードと呼ばれる開発のための設計図は非公開で、マイクロソフトが独占的な権限を持っています。ユーザーは、OSが提供した関数とAPIを使って、Windowsに依頼する形でしか、アプリケーションを作ることができませんし、ウィンドウシステムはどのように動いているのかすら分かりません。
また、PerlやRubyのような高度なプログラミング言語は、たくさんの関数を提供しますが、この関数を使ってプログラミングを行うだけで、それ自体を開発することは多くの場合現実的ではありません。
よって、オープンソースの出番です。オープンソースで公開されているLinuxや*BSDならば、自分でプログラミングを行うことができます。特に、C言語やアセンブリ言語を用いてカーネルやウィンドウシステムを開発することは自由にできます。
ですが、UNIXはシステム管理のOSで、技術的に成熟しています。仕様のほとんどはPOSIXの標準に準拠しており、機能も多く、安定しています。それに、C言語での開発はとても難しく、初心者や独学者にとってはとっつきにくいのではないかと思います。
僕が思うに、WindowsでもLinuxでもなく、自分でOSを作ってみる、という発想をすべきではないかと思います。
たとえば、2ちゃんねるの助けを借りながらひげぽんと呼ばれる平凡なプログラマーが作った、Mona OSというOSがあります。Mona OSはC++とマイクロカーネルで設計されており、一部ではグラフィックやウィンドウシステムの機能やネットワークの機能も実装されています(詳しくは知りません)。
Mona OSはオープンソースであるため、2ちゃんねるで批判される覚悟があれば、誰でも参加できます。
そう、OSを開発することは、現実的に可能なのです。Intelなどの資料を読み解き、自分でコードを書いて実験すれば、書けてしまうのです。
僕の何も知らない知識から言って、まずはIBM-PCとあなたのハードウェアで動く、16bitのDOSのようなシングルタスクのようなカーネルを作ります。
ですが、その前に、FATファイルシステムから起動できるブートローダを作り、それを外部の(既存の)OSからハードウェアにインストールできる必要があります。
ブートローダはカーネルを起動します。
また、カーネルは8086アセンブラで作る必要があります。
コンパイラは最初はGCCを使いますが、将来的にはC言語をコンパイルできるコンパイラを作ります。
また、FAT互換か、あるいは独自のブロック型のファイルシステムを作り、ハードウェアデバイスを操作することのできるデバイスドライバと割り込み機能を作ります。
そして、仮想アドレス空間(物理アドレスとアプリケーションが参照する仮想アドレスの空間を分ける機能)とタイムスライス式のスケジューラ(他のプロセスを実行させる際は今実行しているプロセスを別のメモリに退避して停止させ、時間単位で切り替える機能)を作ってマルチタスクにします。
アプリケーションを実装する前に、カーネルモードとユーザーランドのメモリ空間を分離する必要があります。
アプリケーションをバイナリファイルから読み取って実行できるようにし、initとコマンドラインシェルを実装して、各種のコマンドを作ります。
そして、ウィンドウシステムとネットワーク機能を作ります。
最初はフォントは英語を使いますが、将来的に日本語の入力や表示もできるようにします。
ネットワークについては、ソケット機能を実装し、OSI標準プロトコルを全て実装します。
ネットワークでは、本当に低レベルな部分では、IPアドレス(インターネットのアドレス)とMACアドレス(ハードウェア機器のアドレス)を変換したり、パケット通信の機能を作ってルータとやり取りする機能などが必要です。
また、ウィンドウシステムでは、ユーザーがマウスやキーボードを操作した際にそれをメッセージとしてウィンドウに伝えるための機能や、ボタンやメニューなどのツールキットも必要になるでしょう。
あなたがとても素晴らしいOSを作ってくださることを祈っています。
昔から良く言われることとして、「オープンソースはバグが無く安定している」というのがある。
これは、バグがあるかどうかチェックする目玉がたくさんあれば、どんなバグも重要ではない、と言うリーナス・トーバルズの言葉から逸脱したものである。
実際のところ、Linuxカーネルには昔からバグが少ない。それは、Linuxをきちんとした品質マネジメントで管理しているリーナスの高い技量と、はやめにしょっちゅうリリースしながらみんなの力でバグを無くしていく「バザール開発」の2つの要素があいまって、オープンソースであることや無料であること、そしてGNUツールの品質がUNIXで使われていたことから既に高水準にあったことなど、多くが、「最初期のLinux神話」から言われていることである。
だが、今のLinuxでは、2つの要素が言える。それは、
1.今では、会社が作るようになって会社員のプログラマによるバグが増えた。あるいは、初心者が加わったことで、セキュリティ的に狙われるようになり、見つかったセキュリティホールが増えた。
2.サーバーやエンタープライズの部門では、とても安定しているが、デスクトップは使われていない分野であるため、不安定かつバグが多い。
そして、もう1つの要素として、
3.バグが多かったWindowsが、Microsoftの努力によってバグが減り、NTカーネルは9xカーネルに比べてはるかに安定している。
そういうわけで、Linuxにバグが少ないとか、セキュアであるという神話は、本当に神話になりつつある。それでも、サーバー用途ではバグが少なく、特に性能や安定性に長けているという特徴はある。企業が開発していることは悪いことではなく、エンタープライズレベルの稼働率やセキュリティ能力は向上しており、またデスクトップの分野ではWindowsと全く同じぐらいのデスクトップやアプリケーション環境がそろいつつある。
また、それ以上はアプリケーションの問題である。MySQLよりもPostgreSQLの方が堅固だとは良く言われるし、PHPはセキュリティ的に不安だとは良く言われる。JavaやPerlと比べてPythonやRubyはものすごく遅いと良く言われる。また、MozillaはどこでもChromeよりはるかに遅くて重い。Linuxカーネルがいくらバグがないからといって、ミドルウェアまでバグがないわけではなく、LinuxをベースにしているはずのAndroidはバグだらけで出荷している。Linuxカーネルも、機能とコード数が増えてとても巨大になっている。誰も、Linuxカーネル全てのコードに目を通している人間なんか、最近はほとんど居ない。また、Red Hatという会社母体が居なければ、Linuxは開発できないものになってきている。企業もデスクトップも悪ではない。KDE Plasma 5を使うとその最高の使いやすさに感動する人間が多い。その代わり、KDEはまともに動かない。DebianのKDEは、起動すらしない環境が多い。
コードとファイルも参照のこと。
Linuxの設計については、Gentoo Linuxを設定・管理すると理解しやすいです。Gentooも参照のこと。