こんにちは。ZMSの清水です。早くも2回目の記事になりました。前回はZMSの目指す未来という少々お堅い話をしてしまいましたので、今回からはTechブログらしい内容にしていきたいと思います。
突然ですが、ZMSは中途入社のエンジニアが多く、個々人が持っている技術的なスキルも様々です。
私は元々PC向けセキュリティソフトの開発エンジニアでした。そしてその当時の開発言語はアセンブラです。
アセンブラで開発していましたと言うと、大体2通りの反応が返ってきます。
①「アセンブラって何ですか????」
②「懐かしいなぁ、俺の若いころは~・・・」
この反応からもわかる通り、現在のアプリ開発の現場でアセンブラを目にする機会はほとんどありません。
しかし、実はこの言語を理解すると、コンピュータの仕組みについてより深く知ることができます。(正確に言えば、コンピュータの仕組みを理解していないとアセンブラでのプログラミングは難しいと思っています。)
この記事では主に①の反応をされた方々に向けて、アセンブラとは何かを簡潔にご紹介しようと思います。②の反応をされた(素敵な)おじさま方は、ぜひ懐かしさを感じながらあの素晴らしい言語を一緒に振り返っていきましょう。
プログラムの実行
アセンブラを理解するにあたりまず初めに覚えるべきことは、コンピュータがどのようにプログラムを実行しているかということです。
当然ですが、コンピュータは人間の言葉を理解できません。現在の一般的なコンピュータが処理できるのは0または1の2進数で表現された命令だけです(これを機械語といいます)。この2進数で記述された命令を電気信号として送信することで、コンピュータは様々なデバイスを制御しています。
しかし、人間が2進数で直接命令を記述することは現実的ではありません。そこで、より人間が理解しやすいよう人間の言葉に近い表現で記述できるプログラミング言語や、その言語をコンピュータが理解できるように変換するプログラム(コンパイラ)が開発されてきました。
こういった経緯があって、現代ではPythonやJavaなどのいわゆる高級言語を用いてプログラミングができるようになっています。
アセンブラとは
プログラムを読み込んで実行するのはCPUの役目です。CPUはプログラムに記述された命令を1つずつ解釈し実行します。
アセンブラは、CPUが発行する命令単位でプログラムできる言語です。高級言語では複数のCPU命令がまとまって1つの関数のように扱われますが、アセンブラでは1つの命令がそのままCPUの1つの命令に相当します。
つまり、アセンブラでプログラミングを行う際には、CPUがある命令を出すとそのほかのコンピュータリソースにどう影響するかを理解している必要があります。
Hello, World
では、実際にアセンブラがどれだけ他の言語と違うのかについて、定番のHello Worldを例に見ていきましょう。
※アセンブラでは取り扱うCPUやOSによって記法や命令セットが異なりますので、ここではLinux OSを前提とした一例とご理解ください。
まず、Python(厳密にはPython3)でのHello World標準出力では以下のコードを実行します。
- print("Hello, World")
このコードはprintという関数が英語で直感的に理解できることから、プログラミングをしたことがない方でも理解しやすいコードになっています。(これがいわゆる高級言語の特徴ともいえます。)
では、同じ内容をアセンブラで記述してみます。
- hello: db "Hello, World"
- mov rax, 1
- mov rdi, 1
- mov rsi, hello
- mov rdx, 12
- syscall
- mov rax, 60
- xor rdi, rdi
- syscall
初めてアセンブラのコードを見た方は何が何やらわからないと思いますので、それぞれ簡単に解説していきます。
1行目のdbはそれに続く文字列をASCIIコードに変換し、数値としてラベル(ここではhello)が示すアドレスに書き込んでいます。(helloラベルは書き込んだデータの先頭アドレスを指している、C言語で言うところのポインタのようなものです。)
次に2行目ですが、アセンブラでの演算は基本的に全てレジスタと呼ばれるCPU内部の記憶装置で行われます。上記のコードに出てくるrax、rdi、rsiなどがレジスタです。
movは値をコピーするコマンドです。左側で指定したレジスタに右側で指定した値を代入するために利用します。
つまり2~5行目ではそれぞれレジスタraxに1、rdiに1、rsiに出力したいデータの参照先アドレス、そしてrdxに13を代入しています。
ラベルの中身は出力したい文字列なのでrsiはなんとなくわかると思いますが、それ以外のmovコマンドは何をしているかこれだけでは意味が分かりません。
実は、ここでは6行目のsyscallコマンドで必要な情報を格納しています。
syscallコマンドはOSカーネルの機能呼び出しを実行するためのコマンドです。このコマンドでは特定のレジスタの値が発行される命令のパラメータになっています。
2行目で指定したraxの値「1」はwrite命令を指定しており、ファイルディスクリプタによって指定された出力先に書き込みを行います。ファイルディスクリプタはrdiの値に従い、ここでは「1」の標準出力が指定されています。rdxでは書き込むデータのバイト数を指定します。ここでは"Hello, World"の文字列を出力したいので12バイトを指定します。
ここまでで標準出力が完了しましたが、最後の3行でもう一度syscallを発行しています。
これはraxで60が指定されており、exit命令を意味します。同じsyscallコマンドでもraxの値によって先ほどのwriteコマンドとは別の命令が発行されるわけです。
xorコマンドは2つのレジスタの排他的論理和を左のレジスタに返すため、ここではレジスタrdiをクリアする意味を持ちます。
これで、晴れてアセンブラプログラミングによるHello World出力が完成しました。
アセンブラを学ぶ意味
いかがでしょうか?この例を見ると、今の時代に通常のアプリ開発でアセンブラが利用されない理由も何となくわかるかと思います。Pythonだとわずか1行で済むコードにこれだけの理解が必要では、学習コストも高く開発の効率が悪いのは事実です。
現代ではコンピュータリソースが豊富に確保できるため、厳密なメモリ管理やsyscall単位での効率化を意識する必要性が薄れていることもあり、今後ますますアセンブラを知るエンジニアは減っていくのではないかと思います。
しかしその一方で、技術者としてはアセンブラプログラミングを学ぶことがCPUアーキテクチャやOS機能などコンピュータの本質を理解する一助になると考えています。
もちろん、省リソースでの組み込みプログラムやOSブートローダなど低レイヤの開発では現在でもアセンブラのスキルは必須です。こういった開発に携わりたい方はぜひアセンブラを学びましょう。
今回の記事で少しでもアセンブラに興味を持ってくださった方は、簡単なアセンブラプログラミングを学ぶとともに、今まで意識することのなかったコンピュータの本質を少し覗いてみてはいかがでしょうか?