2015年12月30日水曜日

TOPPERSにトライ

自分は組み込み系のプログラムを作る仕事はほとんどしたことがないのですが、最近IoTが騒がれており、Raspberry Piもいじっているので小型のRTOS(リアルタイムOS)に興味がでてきました。
ただ、RTOSというのは通常特殊なメーカから販売されている高価なものしか使ってみたことがなく(しかも組み込み用途では使ったことありません)、オープンソースで手頃なものというとITRONになります。
TOPPERSというITRONの実装がかなり有名なようで、ぐぐると結構かかります。調べてみると、純粋なITRONの仕様から少し拡張されているようですが、活動が2015年現在でも活発に行われており、勉強用にLinux/Windows上でも動作できるエミュレータがついているとか。いきなりRaspberry Piにインストール、という前にちょっと勉強がてらLinux上で動かすのにトライしてみました。
(注:結論を先に述べておきますが、結局失敗しました。どうもLinux上のエミュレータの実装が、かなり古いLinux(おそらくkernel2.6系?)の時から更新されていないようで、今のUbuntu等のkernel3.x系で動かすのはかなり困難が伴うようです

1.TOPPERSのDL
TOPPERSのHP(http://www.toppers.jp/)に行ってみると、様々なバージョンがあるのにびっくりしました。各バージョンがどんな内容かひと通り読むだけで結構時間がかかりましたが、とりあえずスタンダードな以下をDLしてきました。

・TOPPERS/JSP-1.4.4.1

2.TOPPERSのビルド
以下のコマンドでLinuxエミュレータ上で動くものができるようです。(Linuxでは、sample1というプログラムが動くそうです)

$ ./configure -C linux
$ make depend
$ make

ところが当然ながら問題がでてきます。

(1)configureのperlスクリプトでエラーがでる。
configure:40lineで以下の行があります。

require "getopt.pl";

これ、perlのスクリプト起動時のオプションを拾ってくる機能拡張モジュールらしいんですが、現在では使われておらず、以下の様にするようです。

use Getopt::Std;
あと、Getoopt()→getopt()にする。

TOPPERSのMLの2012年にこの指摘があり、提案するとありましたがいまだに採用されていないようです。他の箇所にも同じような所がありましたが、修正してconfigureは通しました。

(2)make dependで、cfg/cfgがないと怒られる。
TOPレベルにcfg/というディレクトリがあるのですが、どうもここのビルドが実行されていないようです。make dependの段階でこれが必要ということは、configureの時点でアーキテクチャに応じてcfg/cfgがビルドされていることを期待しているんでしょうが、先の(1)の修正だけではまだ問題があったようです。
とりあえず手動でビルドして、cfg/cfgを作ります。
→これは、動かしたいTASKの登録をここでしておくので、先に設定をしてビルドしておくものだと判明。

(3)makeでエラー
さて、(2)までの修正でmake dependはパスしました。やっとビルドですが、以下の様なエラーがでてきました。

In file included from ../kernel/task.c:47:0:
../config/linux/cpu_context.h: In function ‘activate_context’:
../config/linux/cpu_context.h:74:35: error: ‘JB_PC’ undeclared (first use in this function)
     ((int *) &(tcb->tskctxb.env))[JB_PC] = (int) activate_r;
                                   ^
../config/linux/cpu_context.h:74:35: note: each undeclared identifier is reported only once for each function it appears in
../config/linux/cpu_context.h:75:35: error: ‘JB_SP’ undeclared (first use in this function)
     ((int *) &(tcb->tskctxb.env))[JB_SP] = (int)(((VB *) tcb->tinib->stk) +

何のことやらさっぱりですが、どうも自分の環境のライブラリには、"JB_PC", JB_SP"の定義がない、と怒られています。
そもそも、これらのdefineが何なのかぐぐってみると、cの関数で、setjmp()/longjmp()のための定義とのことでした。それらは本来以下の場所に定義されているようです。

/usr/include/setjmp.h
/usr/include/bits/setjmp.h

一つ目が2つ目をincludeしており、そこが肝心な部分なんですが、自分の環境(Ubuntu-14.04)ではそもそも2つ目のincludeが位置が違い、内容も大幅に変わっていて、JB_PC/JB_SPとかのdefineがありません。
bits/setjmp.hの定義場所が、CPUのアーキテクチャ毎のところになっていました。そもそも、このsetjmp()/longjmp()が何者なのかですが、c++等で例外処理にthrow→catchが使われていますが、cの時代にはこれが使われていたようです。つまり、ある関数を実行中(これが大事!)にエラーが発生したことを検知した場合、強制的に別のエラー処理用の関数にジャンプする関数だそうです。
かなりやばい系の関数ですが、当然その実装のためには様々なCPUのレジスタの退避とかしないと、エラー処理なんてできません。kernel2.x系の頃は、なんとかc言語だけで実装していたのを、kernel3.x系になったところでCPUアーキテクチャ別にしたようです。(性能とか、完全な動作を考えるとまともな考えですね。)
doc/linux.txtを見てみると、kernel2.2/glibc2.1の頃に作られた模様...、ちょっとこれはお手上げです。(・_・)


さてそうなるととりあえず自分ではお手上げの状態です。自分はc言語で、setjmp()/longjmp()を使ったことないので、既存のエミュレータをどう改修したらいいかわかりません。
自分のところにはまだ別の環境があるので、そちらで動かないか試してみます。


PS@20151231
自分の持っているVMPlayerの環境に、CentOS4と古いバージョンがありました。これで試してみると、

・(1)の問題は発生しません。やはりPerlの古い使い方のようです。
・makeで別の問題が発生しました。
 ../config/linux/tool_config.h:70: undefined reference to `software_term_hook'
ぐぐってみると、2007年に同じ問題にぶつかった人がMLに残していましたが、問題の箇所の関数はjsp-1.4.2のころは全部コメントアウトしてあったのが、jsp-1.4.3からコメントアウトが外されていて、エラーがでるようになったとのこと。別途、関数定義例があり、それを使ってビルド終了。

サンプルの、./jspが動くようになりました!ただコマンドをシリアルから入力することになっていますが、Linuxエミュレータ上で./jspプロセスのシリアルにどうやってつないだらいいのかわかりません。ttyで送ってみましたがダメでした。
まあ、とりあえず動かせる環境はできたんで、後はこれで勉強してみます。



2015年12月26日土曜日

gitによる開発に関して:特定部分を別branchで管理し、master等にコピーしたい

最近、悩んでいるのがgitの操作方法についてです。しかもやりたいことが少し特殊です。

・プログラムの開発をしているが、異なるbranchで開発しているもののうち、自分の担当している部分だけmasterに持ってきて(コピー)、全体の試験がしたい

通常ならば、branch間でmergeすればいいんですが、そうすると自分が使っているdevelop branchにある別のフォルダの内容がmasterと完全に別内容にしているため、それができない!
本来なら、リポジトリのディレクトリ設計の時に考慮しておくべきことなんですが、それに失敗した例です。ただ開発が最終局面にきていて、いまさらリポジトリの再設定をしてしまうと、これまでの各ファイルの改定履歴がリセットされてしまいます。(git等のバージョン管理システムではよくあることですが、ファイルのディレクトリの位置を変えただけでも、当該ファイルの履歴が消えてしまいます。せいぜい、当該ファイルが移動されたよ、というログが残る程度です。subversionは特別に履歴を引き継ぐオプションがあるようですが、特別扱いのようです。詳細は後の雑談で。)開発が結合試験に入っているため、ログが消えるのはかなり痛いです。

開発が終わってしまえば、ログが消えてもまだ我慢できますが、このフェーズではきついです。mergeできなくてもコピーだけでもできればいい・・・、最後はbranchを切り替えて、一旦別の場所にコピーしておいて、再度master branchに切り替え、(手動で)コピーしようかと考えました。色々とぐぐってみると、かなりトリッキーな技があるようですが、最終的に以下のコマンドで可能なことがわかりました。

$ git branch master
$ git checkout develop /home/hoge/git/repo/A/B
                    (branch名) (path名:フルパスで!)

これでdevelop branchのBディレクトリ以下のファイルがmaster branchのBディレクトリ以下にコピーされてきます。(master, developともrepo/以下のディレクトリ構造は基本的に同じはずですから、コピー元を規定するだけで問題ないはずです。万一、構造が変わっていたりすると・・・、ちょっと考えないと。)もちろん、modifyのstatusになりますので、commitとしとくなりしとかないといけません。(当然、ログには「developからコピーしたよ」等入れておきましょう)

注:上記checkoutではpath名をフルパスで規定しています。ぐぐったサンプルでは如何にもリポジトリのtopからのpath名と推測できるような書き方がしてあるものばかりでしたが、どうもフルパスが必要なようです。

具体的に例を示します。

1.gitリポジトリの作成

$ mkdir git/repo
$ cd git/repo
$ git init

2.リポジトリの中身を作る
今回は以下の様な階層を作ってみました。(詳細なコマンドは省略します)

A/ A.txt
     B/ B.txt
          D/ D.txt
     C/ C.txt

Aディレクトリのあるところに、".git"があります。各ディレクトリにあるテキストファイルには以下の様なテキストが入っています。(注:赤字の部分を別branchからコピーしてきます。ちなみに、B/とC/は同じA/以下の階層にいます。)

A.txtサンプルのテキスト
master branchのファイル

最初にファイル名を入れ、後はbranch名を入れてあります。(もちろん、この後commitしておきましょう)

3.develop branchの作成

$ git checkout -b develop

これで全く同じ内容でdevelop branchが作成され、develop branchに移動しました。

4.develop branch内のファイルの変更
次に、後にmaster brachの特定の部分(Bディレクトリ以下)にこのdevelop branchの内容をコピーするので、それがわかるようにファイルの中身を変更します。具体的には、以下の様にします。(これを、A, B, C, Dの4個のtxtファイルに行います)

A.txtサンプルのテキスト
develop branchのファイル

5.master branchへの移動、develop branchの一部をコピー

$ git checkout master
$ git checkout develop /home/hoge/git/repo/A/B

2回めのcheckoutはbranchを移動しません。単にdevelop branchのBディレクトリ以下をmaster branchにコピーしてくるだけです。これにより、B.txtと配下にいるD.txtがコピーされます。git statusをしてみると、この2つのファイルがmodifiedだといってきて、各ファイルの内容は以下の様になっています。

B.txtサンプルのテキスト
develop branchのファイル


これで自分のメインの開発はdevelop branchで行い、結合試験の必要に応じてmaster branchにコピーすることができます。


雑談:
今回、gitのことを色々調べていて思ったのが、こういうリポジトリ管理ではリポジトリ間でも「部分的な」コピーはどうやっても行えないということです。よく考えてみると、一つのリポジトリは一つの目的を持って作っているのであり、その一部だけを(履歴つきで)持ってくるのはかなり危ないことだと気づきました。そもそもその「履歴」(ログ)は別のリポジトリ(環境)で開発中のことが記録されているので、それを別のリポジトリで見ても何の意味もないか、逆に勘違いの元になる可能性があります。
同じように、一つのリポジトリ内でも、ファイルの移動を行うだけで「履歴」が消えてしまいますが、これはファイルの位置が変わるということは、当該ファイルの(開発)目的が変わる場合がほとんどで
、昔の履歴は害になることが多いはずです。
本来は、そんなことのないようにリポジトリを設計する段階できちんと決めておくべき(将来の可能性・拡張性を考えて)なのですが、世の中万能の人間ばかりではないので、filter-branch等駆使して、強引にログを書き換えてしまう参考例がいくつかありました。
ただ、最初から別リポジトリで開発を進め、途中から一つのリポジトリ管理下に置くようにすることは認められているようです。
後、調べている最中に"git-new-workdir"というツールの紹介がありました。このコマンドを使ってbranch間のコピー(共有?)を設定すると、どちらのbranchで操作を行っても、相手のbranchに反映される上に、履歴もつくのだとか。一瞬これこそ求めていたものだと思いましたが、よく考えたらこんなの使いはじめると「多用」しそうで危ないツールだと思いました。