あるごリズム備忘録

学んだことの記録と日記

Treasure2018の記録

はじめに

 VOYAGE GROUPの夏のインターンTreasure2018に参加して来ました。Treasureを通して自分には経験をアウトプットすることがもっと必要だと思ったので、まずはインターンでの経験を書き残すため、1年以上前から更新が止まっているこのブログを復活させてみようと思います。インターンの内容自体は、他の人が結構書いているので、個人的な部分をピンポイントで書きます。

参加動機

 はじめに、自分は地方の大学で情報系を専攻している修士1年の学生です。博士課程に進む予定はなく就職するつもりです。ただし、仕事でもプログラミングによるものづくりやコンピュータ・サイエンスに関わりたいという思いはあるものの、その就職先として、大きく分けてもメーカーやSIer、Web等と進む方向を決めきれずにいました。特にWeb業界を進路の選択肢として本気で意識しだしたのはここ半年程度です。それまでは、メーカーやSIerがメインで、Webについては具体的なイメージが湧いてませんでした。また、業務内容や企業文化など、新卒として自分が働くときどのようなものが相性がいいのかがイメージできていなかったため、夏のインターンシップでは、大手のSIerベンチャーっぽいWeb系の企業のある意味正反対(のように見える)の組織を自分の目で見て、その経験を進路を考えるための材料にしようという狙いがありました。もちろん、インターンでスキルを上げたいという思いもありましたが、モチベーションとしてはこちらのほうが大きかったように思います。
 Web系のインターン先としてVOYAGE GROUPのTreasureに応募した理由は大きく3つです。
 1つ目は、技術力的な成長です。これまである程度プログラミング経験があり、動くものを作ることはできます。ただ、いつまでたっても「とりあえず動く」というレベルを脱することができていないような気がしていました。今考えると何を学ぶべきかわからないという状態だったのかもしれません。この一年くらいは技術的な成長の実感を感じることも少なく、そのような停滞を突破するきっかけを探していました。過去の参加者の話などから、Treasureならばそのきっかけになるのではないかと思っていました。
 2つ目は、インターンに一緒に参加する同世代の優秀な学生の人たちと話して、彼らの考えを聞きたいと思っていたからです。これは地方に住んでいる学生にありがちなことだと思いますが、プログラムを書いている同世代の人間が周りにおらず、技術的な話題や将来について真剣に話す機会が極端に少ないです。都心の企業でお金を受け取ってコードを書いている学生たちは自分にとっては、雲の上のような存在で、実際のところ彼らがどんなんことを考えているのかは、想像するしかありませんでした。
 最後の理由は、VOYAGE GROUPの中の人達と話してみたかったということです。会社が配信しているポットキャスト「ajitofm」を以前から聞いていおり、「この人たちと話してみたい」という憧れのような気持ちと、なんとなく「この人たちなら自分の質問にも真剣に答えてくれそう」という思いがありました。
 

講義内容

 インターン期間の最初の3分の2は座学中心の講義で、残りがチーム開発です。
 Treasure参加前の自分の経験としては、競プロやiOS、Unity、 組込みなどが中心で、Web開発の経験は簡単なAPIサーバ(mainファイル1つのみで完結)程度だったので、講義で学ぶ内容はどれも新鮮で学びの多いものでした。具体的には、Golang、TDD、フロント(React)、APIの設計、セキュリティ、データベース(モデリング)、チーム開発手法(アジャイル)、アイデアの出し方、等です。どの講義も非常に濃い内容でまだ消化しきれていないくらいなのですが、現状で特に印象にの残っているものを2つ挙げます。(強調しておきたいのは、他の講義で学んだことが少ないという意味でないということです。どの講義も学んだことが多すぎて、それらをまとまった文章を書く自信がないので、ほんの一部書きやすい印象的なエピソードを抽出しています。)
 まず、フロントエンドの講義。自分はWebのフロントエンドどころか、jQueryも触ったことがありませんでした。「HTMLとCSS、JSは読めなくはない」くらいの事前知識でした。そんな状態だったので、講義を受け終わったあとは10数年くらい未来にタイムスリップしたような感覚でした。フロントエンドには興味があまりなかったのですが、フロント側のstateの管理などが案外面白そうで興味が湧いてきたのと同時に、やはり見た目が変わるインパクトが楽しく勉強してみたくなりました。
 次に、API設計の講義について。ここで得た知識は、後半のチーム開発で最も生かされた内容かもしれません。過去に自分がPHPやGoで作った簡単なAPIは、エンドポイントのURIは操作自体の名称を使用して、ほぼ全てGETメソッドでやっていました。具体的には「GET /api/addUser」、「GET /api/deleteUser」などです。本当にひどいですね。このへんが、まさに先に述べた「とりあえず動く」状態というのが最もよく現れている部分です。講義で学んだような、リソースを中心にそれに対する操作ベースで考えるAPI設計は、多くのAPIが必要な後半の開発でも非常に見通しが良くなり、開発しやすかったです。
 すべての講義を通して、講師やTAの方のサポートが圧倒的で、こちらが驚くくらい丁寧に質問に答えてもらえました。そのおかげで、自分の知識が薄い部分についても、ついて行けなくなるようなことはなく、理解しながら講義を聞くことができました。

チーム開発

 自分たちが作るサービスを考えるパートは、途中まではわりと順調に進み、初日の夕方には作るサービスが一つ決まりました。アイデアが一旦決まってみんなで食事に行ったあと、どうしても先程までの自分たちのアイデアが既存サービスと同じに見えてしまい、それを正直にチームメンバーに言いました。ほとんど決まったものに対していまさらちゃぶ台を返すようなことをやるのかどうかはかなり悩みましたが、ここで言わないと途中で作る意味を見失ってしまいそうな気がしたので伝えました。その後、予定の時間をかなり延長してアイデアを再考しました。結局、アイデアの中心的な部分が大きく変わることはなかったのですが、この議論を通して納得感を持って開発をスタートすることができました。
 開発パートでは、最初期でフロントエンド担当のメンバーが見た目のモックを作ってくれました。完成イメージが明確になることで、作るモチベーションが上がったのと同時に、実装のイメージも具体的になって設計もすぐに考え始めることができました。過去のチーム開発でデザインができる人がいた経験はほとんどなかったので、このような開発初期段階でのスピード感は初体験でした。
 また反省点としては、APIの変更を曖昧にしたまま進めることがあったことです。最初の大まかな設計が決まった段階で、APIの仕様を決めてwikiにまとめてからフロントエンドとバックエンドに別れて開発をスタートしました。開発の途中で、実装面の問題からAPIの仕様が微妙に変更になることが多々ありました。その場合、口頭で伝えて実装してしまってからwikiを修正するよなことが多かったです。このような流れでやっていると、wikiにかかれていることと実際に動いている実装が異なっている場面が多くなり、そのズレが蓄積してコードが機能しなくなり修正に余計な時間を取られることが何度かありました。おそらく、多少時間を使っても、全員の共通意識としてのwikiをしっかり修正してから実装するという手順を守る方が、全体としての開発スピードは落ちなかったような気がします。
 個人的にはとてもいいチームメンバーに恵まれました。前半の期間で話したことがある人もない人もいましたが、どちらもチーム開発での関わりの中で、技術的な部分ではもちろん、人間的に尊敬したいと思える部分が数多く発見できました。真似できる部分は真似して、自分の行動方針に取り入れて行きたいと思います。

終わりに

 3週間のインターンは本当にあっという間でした。毎日の密度が非常に濃く、新しく学んだことを必死に理解しようとしていたら終わっていました。内容が本当に盛りだくさんだったので、消化しきれていないことも多いです。正直、「圧倒的成長」できているのかはわからないですし、実際そのようになれるかは、この消化不良感に目を背けずに自分で学びを進める必要があると思います。これまでの自分だと、この圧倒的成長した「感覚」の満足感で止まりがちだった気がしますが、Treasureの期間で一緒だったみんなの技術へ取り組む姿勢を見ていると、とても満足できなくなりました。
 成長の実感があまりないと書きましたが、いまの時点でひとつだけ成長を実感していることがあります。Treasure直前に、軽く「Real World HTTP」という本に目を通していました。正直そのときは、目から入る情報がそのまま頭を抜けていく感じでした。しかしTreasure終了後に同じ本の目次を見てみると、景色が変わっていました。各項目がインターン中に解決しきれなかった疑問に答えてくれているような内容に感じられ、読むときの吸収率が桁違いになっていました。同様にTreasureでの経験をきかっけに吸収しやすくなっていることがまだまだありそうなので、そのあたりを突破口に進んで行きたいです。

組み込みOS(第7回)

ステップ7の内容

割り込み処理を実装する。前回までのシリアル入力の受取りはビジー・ループによって行われていたが、今回はシリアル割り込み処理によるものに変更する。

割り込み入力ピン

部割り込みに対しては、ピンへの入力によって割り込みハンドラを呼び出す。
ピンの数が外部からの割り込み出力の種類より多い場合はそれぞれのコントローラにピンを割り当てればよい。しかし、ピンが1つしかない場合は、複数のコントローラの出力のORをピンへの入力とし、各コントローラの持つ割り込み関連のレジスタを、ハンドラ内で調べて処理を変更する。

割り込みコントローラ

複数の出力をOR論理で結合してピンに伝えるが、それぞれの割り込みの優先度などの制御ができる。

割り込み処理の流れ

割り込みが発生すると、プログラムカウンタの値を、割り込みベクタに登録してある割り込みハンドラへのアドレスに書き換える。
このとき、もとのプログラムカウンタの値は自動的にスタックや専用レジスタに保存される。(保存先はCPUによってことなる。)同時に、モード・レジスタの値も保存され、自動的にモード・レジスタの設定は割り込み禁止に変更される。
割り込みハンドラの最後では、プログラム・カウンタとモード・レジスタの状態を復帰させる処理を行う。

H8の仕様

CCR

H8では、モード・レジスタの機能をもつCCRというレジスタがある。CCRはモード・レジスタの役割の他にも、フラグ・レジスタの役割も担っており、各桁のビットがそれぞれの役割を果たしている。

PCとCCRの保存先

割り込みが発生した時の、プログラム・カウンタとCCRの保存先は、スタック・ポインタ(PC)のER7レジスタになる。ER7の指す先のアドレスの上位1バイトにCCR、下位3バイトにPCの値が格納される。

割り込み復帰方法

プログラム・カウンタとCCRの値を復旧させるrte命令を、割り込みハンドラの最後で呼び出す。

インライン・アセンブラ

C援護のプログラムの中で、アセンブラを記述るためのもの。
今回は、ブートローダのプログラム中でCCRを操作するために使用する。


12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門

組み込みOS(第6回)

ステップ6の内容

前回ステップで、転送したELF形式ファイルを解析し、セグメント情報を読み取り、とりあえず内容を表示することだけはできた。今回のステップでは、読み取った情報をもとに、ファイル内容ををRAM上にコピーし、エントリーポイントに処理を渡すプログラムを実装する。

エントリ・ポイント

実行を開始するアドレスのこと。ELFヘッダの中には、エントリ・ポイントを保持するために割り当てられた領域がある。

関数へのポインタ

ELF形式ファイルから読み取ったエントリポイントのアドレスは、関数へのポインタとして変数に持っておく。関数へのポインタ f は次のように宣言できるらしい。

[返り値の型] (*f)( [引数] );

コード中に次のように書くと、f にポインタが渡されている関数が呼び出される。

f([引数]);

疑問点

runコマンドの動作

ブート・ローダのrunコマンドは、1回目は必ず失敗する。2回目はうまくいく。
理由はわからなかった。
アドレスを表示させてみたところ、一度目のコマンド実行時は、エントリーポイントのアドレスが0になっているが、2度目は正常にアドレスが入っている。しかし、1回目と2回目のコマンド実行の間に何か処理が入っているわけではないので、原因がわからない。

OS側のmain関数

OSのコマンド”exit”で、os側のmain関数はreturn されるが、その後の処理がどこに渡されるかがわからない。書籍のままのコードだとexitにあとフリーズする。これはおそらくリンカ・スクリプトの無限ループのため、この部分を丸々削除するとブート・ローダに処理が戻るはずだと考えた。しかし実際は、1回目のexitコマンドでは再びOSが起動し、2回目はブート・ローダに処理が戻った。原因が特定できなかった。


12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門

組み込みOS(第5回)

ステップ5の内容

ステップ4までで、とりあえずファイルの転送まではできるようになった。ここからは、転送したELF形式ファイルをRAM上に展開できるようにする。ステップ5では、展開はせず、ELFの解析プログラムまでを作成する。

マジック・ナンバ

ELF形式ファイルの先頭にある16バイトの領域に配置されている数値データのこと。様々な識別情報が書かれており、特に最初の4バイトは、「0x7f 0x45 0x4c 0x46」となっていて、これによってELF形式であることが判別できる。

「セクションと」と「セグメント」

セクション

リンカが、複数のオブジェクト・ファイルをリンクして一つの実行形式ファイルにする際に使用する。それぞれのオブジェクト・ファイルのセクションに従って、同じ種類のデータは、実行形式ファイルにおいて、同じデータ領域(.textとか)にまとめられる。

セグメント

ローダは、実行形式ファイルの内容を、書かれているセグメント情報にしたがって、セグメント単位でメモリ上に展開する。

疑問点

リンカについて

リンカ・スクリプト(ld.scr)は、リンカそのものではなく、リンカへの指示が書かれたファイルであることを理解できた。しかし、そうなるとリンカ本体はどれになるのかまだ理解できてない。


12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門

組み込みOS(第4回)

ステップ4の内容

今までは、プログラムの書き換えのたびに直接ROMを書き換えてきたが、ROMの書き換え回数には上限があるため、この方法を変更する。ROMにはブート・ローダーを書き込んでおき、起動のたびに、シリアル経由でプログラムをダウンロードし、RAMに展開する。この方法なら、ブート・トーダーを完成させてしまえば、プログラム本体を修正するたびにROMの書き換えを行わなくてすむ。
今回は、XMODEMプロトコルによって、ファイルを転送するところまでやる。

XMODEM

シリアル経由でのファイル転送プロトコル。古くからある(パソコン通信時代に流行ったらしい。)
プロトコルの実装」という響きのワクワク感。

シリアル受信用のライブラリ

今後はH8側でも受信を行うので、そのためのライブラリを実装する。

エコーバック

シリアル通信を行うときのエコーバックはどこに書かれているんだろうかという疑問は以前から持っていた。シリアルドライバで行ってしまうこともあるらしいが、今回、ドライバは送受信に専念するという意味でライブラリの中に書いた。

ファイル転送手順

kz_xmodemを使う方法

書籍で標準で使われている転送ツールではなく、kz_xmodemを使用すると、操作手順が少なくて楽。
(参考サイト: http://blog.livedoor.jp/noanoa07/archives/1994090.html

$ kz_xmodem [送りたいファイル名] /dev/tty.usbserial-FT0UMQ8C

(その後普通に通信)

$ sudo cu -l /dev/tty.usbserial-FT0UMQ8C
書籍通りやる方法(macの場合)

多少操作が増えても、せっかく実装したloadコマンドを使いたい場合は次のようにする。

kzload> load
$ ~+ lsx [送りたいファイル名]

(あとは待つだけ)

ABI(Application Binary Interface)

スタックポインタには、レジスタER7が使われている。だから、ジャンプ命令jrsやリターン命令rtsでは、引数を指定しなくても、勝手にER7が使われている。これはコンパイラが自動で行っているが、多くの場合、ABI(Application Binary Interface)という規格でCPUごとに決められている。したがって、CPUのABIをサポートしているなら、違うコンパイラコンパイルしたオブジェクトファイルでもリンクして正常に動作することができる。

スタートアップの無限ループ

スタートアップ(starup.s)の最後にある

1:
	bra 1b

このbraは戻り先を覚えない簡易的なジャンプ命令のようなもの。引数の1bは、「この命令より前にある”1:”というラベルの内最も近いもの」という意味である。つまり、直前にそのラベルがあるので、無限ループを表す。これ以前のスタックmain()へのジャンプの後、つまりmain()から戻ってきたとき、おかしな動作をして暴走しないようにする対策になっている。

疑問点

ファイル転送のに関して

lsxと、実装したXMODEMに関するプログラムの役割の範囲がよくわからない。
lsxはひたすらプロトコルのルール通りにビットデータを投げてきて、実装したプログラムは、それを意味のある形で上手く受け取っている?
さらに、kz_xmodemを使用した場合は、実装部分に関しては丸々使わないことになるのでは・・・と思っているけど、その認識はあっているのだろうか。 

ABIに関して

対象CPUのためにどのABIを使うのかというコンパイラの設定は、どこでやっているのかがわからない。Makefileの項目のどれかがその設定なんだろうか。


12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門

組み込みOS(第3回)

ステップ3の内容

ステップ2まではできなかった静的変数の書き換えを可能にするため、各ファイルを修正する。

変数の種類

変数にには「静的変数」と「自動変数」という種類がある。これらは、プログラムで宣言された位置によって種類が決まり、割り当てられるメモリ領域も異なる。

静的変数

関数の外で宣言される。初期値がないものはROMのデータ領域に置かれ、あるものはROMのBSS領域に置かれる。

自動変数

関数の内側で宣言される。RAM上に置かれる。

静的変数の書き換え対応

静的変数は、ROMに置かれるが、これだと内容の変更ができないので変数として使用できない。この対策として、マイコンの電源を入れた直後、ROMのデータ領域の内容を、RAMにコピーして、プログラムの変数からはそのコピー先にアクセスするように設定した。コピーはmain.cの最初で行い、設定はリンカ・スクリプトで行う。このような一連の処理を、「
VA≠PAにする」というらしい。

PA(ロード・アドレス/物理アドレス

静的変数の初期値が配置されるアドレス

VA(リンク・アドレス/仮想アドレス)

プログラムから静的変数を使うときにアクセスするアドレス

ELF形式

コンパイラが生成する実行形式ファイルのフォーマット。linuxにおいては、readelfコマンドで解析することができ、メモリのセクション情報や、各シンボルが割り当てられている番地などが確認できる。初期値のない静的変数が、BSS領域に置かれている事などもここで確認する事ができる。
(本の環境と異なる部分)
macでは、readdlfコマンドが使えないので、インストールする必要があった。

% brew install binutils

使用するときは、readelfではなく、greadeflとする。

% greadelf -a kzload.elf

その他

疑問が2つほど残った

  • リンクとは、プログラム中の関数や変数などのシンボルに対して、メモリを割り当ててやる作業という認識あっているのか?
  • リンカの実体は、リンカスクリプトなのか?


12ステップまで、3月中にやりきりたい。なんとしても時間を作り出して確保する。


12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門

組み込みOS(第2回)

 ステップ2の内容は、シリアルコントローラのレジスタによる設定、標準ライブラリ風関数の実装が主な内容だった。今回気になったのは「分周」と「ボーレート」の関係くらい。
 
 まず分周とは、CPUのクロック周波数をnで割ってほしい周波数を作ることらしい(n:分周比)。そして、この作った周波数をシリアルモジュールに供給する。
メインクロックを分周するnをBRRに設定すると思っていたんだけど違うらしい。どうもBRRの値とボーレートの対応表がマニュアルにマイコンごとに用意されていて、それを見て設定すれば良いらしい。
 マニュアルを見ると、表の他にBBR値の計算式も書かれていた。確かに歩調同期方式の式でBBRは64になる。ただし、(64*2^(2n-1))と−1の部分の意味がわからない。64*2^(2n-1))は設定するシリアル通信で使うビット長とかで違うのかもしれない。ビット長8なら8になるとかだったら理解できる。
 クロック同期方式と歩調同期方式の違いもよくわからなかった。この辺も確かに理解したい部分ではあるんだけど、この辺にかかりっきりになってしまうと進まない。この本で一番やりたいのは、メモリ管理とかスレッドの部分なので、あまりここで時間は使わないようにしたい。少し調べて、その時理解できなくても、「具体的にどこが」わからないかを記録しておけば(数学ガール的にいうと「分からないの最前線」)、最低限またあとで考えることができるからよしとしたい。


(蛇足)
 第1回の記事からだいぶ間が空いてしまった。期末試験その他いろいろあったが、時間を作れなかったわけではないので結局いいわけ。とにかく、本を進める過程で書き残し続けることはやめたくないので、今回はかなり雑な文章になってしまったが形に残すことを優先した。時間があれば書き直したい。


12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門