SSブログ

clibと呼ばれるライブラリー開発の思い出(5)  [clib]

前回から一年近くが過ぎてしまいましたので、過去の記事を最初に列挙しておきます。

スレッドライブラリ

独自に設計したメモリ管理ライブラリに基づいて設計したのが「スレッドライブラリ」でした。主には二つの機能が提供されていました。
  • CThreadIRunnableCSynchronizerCReaderWriterLockというスレッド生成および同期用のクラス
  • スレッドセーフなコレクションクラス(リスト、セットなど)
私の記憶が曖昧になっており、コレクションクラスとして何を準備したのかは記憶が定かではありません。スレッドライブラリのAPI設計では、いくつかの間違いをしました。それらの間違いは、初期のclibでは修正されていません。間違いを修正したのは、2003年から従事したLinuxベースでのソフトウェア開発でです。代表的な間違いは、fail-fast iteratorを仕様に盛り込まなかったことです。そのため、誤ったイテレータの使い方が行われていても気付かない実装になっていました。

今日なら

1993年から2000年にかけてC++を用いたデジタル複合機のコントローラソフトウェア開発で経験したメモリ関連の苦労を反映したのがclibでした。かなりの量のソフトウェアを新規開発しながらも、富士ゼロックス社のデジタル複合機を2000年後半から2年弱で開発できたことに貢献したと思っています。

しかし、今日作り直すとしたら、C/C++ではなくGoで開発すると思います。実際開発しましたし、将来機会があればやはりGoだと思います。
小さなスタックで動作する軽量なゴルーチン、CSP による通信、ガベージコレクタとメモリ保護、構造体によるコンパクトなメモリ設計、オブジェクト指向プログラミング、および、ウェブのクライアントやサーバの機能を含めたさまざまな機能を提供する標準パッケージと、道具は揃っています。したがって、オペレーティングシステムにLinux を採用している組み込みシステムでは、C やC++で開発するのではなく、Go で開発するのが適切だと思っていますし、将来Go で開発されるのが普通になる日がくるのではないかと期待しています。実際、ある組み込みシステムの制御ソフトウェアをGo を使った完全なテスト駆動開発で行うプロジェクトを2 年間にわたり率いて、Go での組み込みシステムの開発が可能なことを実証できました。

『プログラミング言語Go』の「訳者あとがき」より
残念ながら、clibを設計してから10年後にGoは登場しました。

コメント(0) 

clibと呼ばれるライブラリー開発の思い出(4) [clib]

(前回はこちら

メモリ管理ライブラリ

1990年代に開発したFuji Xerox DocuStation IM200 / AS200は、オペレーティングシステムがSolaris 2.3であったため、プロセスが存在していました。つまり、ユーザのメモリ空間に関しては、プロセスの壁があった訳です。それでも、プロセス内でのメモリ破壊のデバッグには苦労しました。

メモリ管理ライブラリのAPI設計にあたっては、メモリ空間がシステム全体で一つというVxWorks上で、どうやって効率よくメモリ関連の問題を早期に発見するかという視点で設計しました。細かな技術的な詳細は説明しませんが、メモリ管理ライブラリは以下の機能を持つように設計しました。
  • メモリをヒープゾーン(Heap Zone)と呼ばれる大きな単位で扱い、すべてのメモリ獲得は指定された特定のヒープゾーンから行う。用語「Heap Zone」は、初期のMachintoshのAPIから拝借したものです。
  • ヒープゾーンから獲得されたメモリブロックをdeleteオペレータで解放する際に、そのメモリブロックの範囲を超えて書き込みをして破壊していないかを検査する仕組み。破壊を検出した場合には、破壊を報告すると同時に、そのメモリブロックをnewオペレータで割り当てたソースコード名と行番号を表示する。
  • newオペレータで獲得されていないメモリブロック(もしくは、誤ったアドレス)をdeleteオペレータで解放しようとしていないかを検査する仕組み。
  • メモリブロックが獲得されたが初期化されていない、もしくは、解放されたことをデバッガーで調べている時に分かるようにする仕組み(初期化忘れやダングリングポインターに容易に気づく仕組み)。
  • ヒープゾーン内の現在のメモリ使用量と最大使用量を知る仕組み。
  • メモリリークを容易に検出できるようにし、リークしているメモリ ブロックがどのソースコードの何行目で獲得されたかを報告する仕組み。
今日では、このようなことを気にしないといけないような言語で開発することは少なくなっていると思います。しかし、2000年の頃、C++による大規模な組み込みシステムの開発では、これらの機能を事前に用意していないと、メモリ関連の障害によるデバッグは大変な時代でした。今から、やり直せるとしたら、C++ではなく、Go言語で作り直すと思います(訳者あとがき『プログラミング言語Go』)。

メモリ管理ライブラリはもちろんスレッドセーフに作成されており、このライブラリに基づいて、共通ライブラリとしてコレクション機能も含む「スレッドライブラリ」を設計しました。「スレッドライブラリ」は、Javaとそのライブラリに強く影響を受けたC++用ライブラリでした。

次回は、「スレッドライブラリ」について簡単に説明します。

コメント(0) 

clibと呼ばれるライブラリー開発の思い出(3) [clib]

(前回はこちら

記憶が定かではないですが、2000年の後半からカラーデジタル複合機の新たなコントローラソフトウェアの開発が始まりました。一枚のコントローラボードですべてのサービスを動作させるということで、ほとんどのサービスは新規に開発するというプロジェクトでした。

開発に先立ち、オブジェクト指向で開発を行うということで、当時FXISにいた私、荒井礼子さん、皆川さんの三人で富士ゼロックスのソフトウェア技術者(200名以上)に以下のことを行いことになりました。
  • C++教育を含むオブジェクト指向に関する基礎教育(一週間?)
  • 実際の開発成果物のレビューを含むコンサルティング
ほとんどのソフトウェアエンジニアは、デジタル複合機に関するドメイン知識はありましたが、オブジェクト指向言語(C++)やオブジェクト指向開発は初めてということで、基礎教育をFXISへ発注してもらって実施しました。

教育を受けただけでは、正しく実践できる保証はないため、これもFXISへ発注してもらい、希望する開発グループに対してコンサルティングをすることになりました。実際、私自身は、この頃から、2002年8月に米国へ赴任するまで、多くのレビューを行っていました。特に2001年には、週に3日とか4日は、朝から一日中レビューをしていました。

同時に開発に先立って私が書いたのが、『Code Review Guide』と『C++ Coding Standard』です。

『Code Review Guide』では、コードレビューのやり方を書いてあるのですが、特徴的なのは、開発者が単体テストを実施する前に、コードレビューを実施するということです。開発者がテストしてしまった後にコードレビューを実施して読みやすさに関する指摘をしても、コードを書き直してもう一度テストするというのは心理的な抵抗が強くなってしまうので、テストする前にレビューするように規定していました。当時は、自動テストではなく、手作業によるテストの時代でした。

『C++ Coding Standard』は、C++での様々な命名規則、クラスやインタフェースの宣言方法、定数の宣言方法、実装の隠蔽方法、禁止事項などを書いたものです。これは、IM200開発でのC++の経験に加えて、1996年から独学で学んだJavaの知識の多くをC++へ盛り込んだものとなっていました。

おそらく、2000年の夏頃からclibのAPI設計を始めたのだと思います。まず、書いたのは、『メモリ管理ライブラリ』のAPI仕様書です。次に、『メモリ管理ライブラリ』に基づく『スレッドライブラリ』のAPI仕様書を書きました。『スレッドライブラリ』には、単なるマルチスレッドプログラミング用のAPIだけではなく、コレクションライブラリも含めました。『メモリ管理ライブラリ』と『スレッドライブラリ』を総称して「clib」と呼んでいました。

clibは、製品で使われるOSであるVxWorks版が実装されただけでなく、さらにWindows版とSolaris版が実装されて開発者に提供されました。clibだけで実装されるライブラリであれば、Windows上でもSolaris上でも実装・デバッグできるようになっていました(最終的な商品では、VxWorks用にソースコードがクロスコンパイルされます)。

一方で、私自身がいくつかの開発グループのコンサルティング(主にクラス図やコードのレビュー)を行っていましたので、共通に必要と考えられる機能は、順次『スレッドライブラリ』へ追加していきました。追加した機能で最も実装が複雑だったのは、ReaderWriterLockでした。

clibはC++用のライブラリでしたが、Javaの影響を色濃く反映したものになっていました。特に、「インタフェース」概念の導入、「インタフェースの実装」を除く「クラスの多重継承の禁止」、staticファクトリーメソッドの推奨などです。

次回は、『メモリ管理ライブラリ』について書きます。

コメント(0) 

clibと呼ばれるライブラリー開発の思い出(2) [clib]

(前回はこちら

アーキテクチャ概要

Fuji Xerox DocuStation IM200 / AS200は、OSがSolaris 2.3であり、その上に、スキャナー、プリンタ、ファック装置、パネルなどのデバイスを抽象化してAPIを提供するプロセス群、それらのプロセス群と通信するvoyagerと呼ばれるプロセスがありました。一つのプロセスであるvoyager上でコピーやファックスなどのサービスが動作するようになっていました。

voyagerの中は、最下位層にMAE(Multifuction Application Environment)と呼ばれるレイヤがあり、MAEがサービスの起動から、割り込み処理、デバイスの排他制御などの基本機能を提供していました。各サービスは、ユーザからの指示があると、MAE上でスレッドとして都度起動されるように設計されていました。サービスを実装しているスレッドは、さらに子スレッドを生成することができ、それらをまとめてMAE上で管理していました。

コピーなどのサービスが完了すると、生成されたスレッド群はすべて終了します。MAEの実装は、ほぼすべてを私一人で設計・実装しました。このような動作環境でしたので、新たに実装したnew演算子とdelete演算子によるメモリ管理は、次のような機能を持っていました。
  • スレッド単位の管理:スレッド単位で、割り当てられたメモリを管理する
  • メモリ破壊検出:new演算子で返した領域の前後のメモリ領域を破壊した場合には、delete演算子がその破壊を検出する。
  • 割り当てられたメモリへのマーク付け:割り当てられて解法されていないメモリ領域にマークを付ける機能
  • スレッドごとのメモリ割り当て順序管理:スレッドごとに、個々の割り当てられたメモリがそのスレッドでの何番目の割り当てなのかの管理
システムが定常状態のとき(つまり、何もサービスが動作していないとき)に、「割り当てられたメモリへのマーク付けの機能」を使ってすべてのメモリにマークを付けます。その後に割り当てられたメモリにはマークが付けられていません。

システムの定常状態で、割り当てられているすべてのメモリにマークを付けて、その後にコピーなどのサービスを実行します。そして、実行後にマークが付けられていないメモリの一覧を表示することができました。つまり、サービスの処理の中で割り当てられて、サービスが終了したときに解放されていないメモリの一覧が表示されます。しかし、その一覧は、メモリのアドレスと、それがどのスレッドで何番目に割り当てられたかしか分からない実装になっていました。それでも、全く情報が無いよりはよかったのですが、リークしているメモリがどこでnewされたメモリなのかを調べるのが面倒でした。この面倒さを解消する仕組みは、当時私は思い付かなかったようです(clibでの改善1)。

もう一つ、当時苦労したのはダングリングポインターです。つまり、すでに解放済みのメモリを指すポインターです。解放されたメモリを参照していることをデバッガで調べるのが容易ではありませんでした。なぜなら、解放はされているが、メモリの内容(オブジェクト内容)がそのままなので、動作したりすることがあるからです。この問題の解決を容易にする仕組みも、当時私は思い付かなかったようです(clibでの改善2)。

IM200はペーパーユーザインタフェースを持つ新たなデジタル複合機でしたが、そのペーパーユーザインタフェースのソフトウェアの代わりに、別のソフトウェアを搭載したのがAS200(自治体窓口証明発行システム)でした(基本的なデジタル複合機の機能は同じソフトウェアが動作)。AS200固有の機能はすべて富士ゼロックス情報システムが開発していたのですが、そこでもメモリリークに悩まされていて、私が作った新たなメモリ管理でリンクし直したら、容易にリークが分かるようになって安定させることができました。

こうして、Fuji Xerox DocuStation IM200と AS200は、1995年の暮れから1996年にかけて完成して製品化されました。新たなサービス(課金)モデルを目指して開発されたIM 200は、残念ながらあまり売れませんでした。その代わり、AS200は対象が市役所向けでしたが、売れて、リコーへOEMまでしていました。AS200は、当時(1996年)の製品としてはめずらしく大型のタッチパネルを持ち、競合機種と比較してもダントツの商品に仕上がっていました。市役所向けとは言え、市場を席巻されてしまっては、AS200の導入と同時に普通のデジタル複合機も置き換えられてしまうのと、同じ機能を後追いで開発することは無理だと、当時のリコーの誰かが判断したのではないかと思います
※ リコーに転職してから、このOEMの件を開発部門のさまざまな人に聞いても、誰も知らないとう返事でした。おそらく開発部門を巻き込まずにOEMで仕入れて販売したのだと思います。
IM200に関しては、後継機の開発を行わないことが1996年に決まり、私は、その年の8月に富士ゼロックスを退職して、日本オラクルへ転職しました。その後、ジャストシステムを経て、富士ゼロックスの子会社である富士ゼロックス情報システムへ入社したのが1998年5月でした。

そして、1999年には、新たなカラーのデジタル複合機開発の検討が富士ゼロックスで始まろうとしていました。その頃に提案されたソフトウェアアーキテクチャに私が異議を唱えたのが1999年の暮れでした。私からの異議がきっかけで、二つのソフトウェアアーキテクチャの検討が2000年の前半の半年間に、多くのマネージャやエンジニアを巻き込んで行われました。そして、最終的には、私が提案したソフトウェアアーキテクチャは却下されて、今の富士ゼロックスのデジタル複合機に搭載されているソフトウェアアーキテクチャが採用されました。

ここから、clibの開発が始まります。それは、2000年夏です。

つづく

clibと呼ばれるライブラリー開発の思い出(1) [clib]

2000年にclibと呼ばれるC++用のライブラリーを開発しました。それは、今でも富士ゼロックスのデジタル複合機で1000万行を超えるコントローラソフトウェア(*)と呼ばれる制御ソフトウェアで使われているライブラリです。メモリ管理機構とマルチスレッドプログラミング機構を統合したようなC++のライブラリです。
(*)http://www.atmarkit.co.jp/ait/articles/1507/06/news009.html

clibは、私がそのAPI仕様をすべて設計し、実装は他の優秀なエンジニア2名に行ってもらいました。API仕様には、防御的プログラミングも含めて、メモリ管理機構での細かな動作仕様も書いていました。

このライブラリに関して、忘れないうちに一連のブログ記事として書きたいと思います。しかし、記憶は嘘をつきます。したがって、事実とは異なった私の記憶の嘘が含まれているかもしれません。

Fuji Xerox DocuStation IM 200/AS 200の開発

時代は、1991年1月17日まで遡ります。その日、私はXerox社のPARC(Palo Alto Research Center)にいました。米国に駐在していましたが米国内で転勤することになり、新たに従事するPARCのプロジェクトの話を聞くためにLAから出張していました。その日は、湾岸戦争が勃発し、PARCの廊下で戦争が始まったと伝えられたことを鮮明に覚えています。

そのプロジェクトは、PARCではPageMillプロジェクトと呼ばれていました。富士ゼロックス社からの駐在員は私一人でした。2年間、米国でそのプロジェクトに従事したあと、製品化のために1993年5月に日本に帰国しました。4年半の米国駐在を終えて富士ゼロックスへ戻り、PageMillプロジェクトの技術の製品化を行うDS企画開発室に配属になりました。

当時、富士ゼロックスはワークステーション事業から撤退を決めており、その開発室にはそれまでワークステーションを開発していた優秀なソフトウェアエンジニアが集められていました。それでもほとんどが私と同じ歳か一つ下ぐらいでしたので、30代前半のソフトウェアエンジニアが集められていました。

この製品化プロジェクトで特徴的だったのは、以下の通りです。
  • 集められたソフトウェアエンジニアは、私も含めて誰一人としてデジタル複合機の開発に従事したことがありませんでした。
  • CPUにはSPARC、オペレーティングシステムはSolaris 2.3という構成でした。
  • 私を含めて全員が、初めてのマルチスレッドプログラミングでした。
  • 誰もほとんど使ったことがないC++で開発することが決まっていました。
この開発では、さまざまな問題に直面し、都度解決していきました。

C++での開発

C++で開発することが決まっていたのですが、当時、C++を学べば学ぶほど困ったなと思っていました。それは、APIを設計するときに、ヘッダーファイルに実装の詳細を記述しない方法がないように思えたからです。実際には、ある書き方を考え出しました。その書き方の詳細については、「API設計の基礎」に書いていますので、参考にしてください。

ビッグバンインテグレーション

継続的インテグレーションという言葉がない時代でしたので、ビッグバンインテグレーションを行っていました。しかし、2週間ごとのインテグレーションは必ず失敗するという状況でした。この問題に対処するために導入したのは、夜間ビルドです。つまり、毎晩自動ですべてをビルドするということです。毎晩ビルドが成功するようになるまでは、数週間を要しました。

当時、ビルドはかなり時間を要していました。そして、夜間ビルドが毎晩成功するわけではありませんでした。毎朝7時過ぎに出社後は、夜間ビルドが失敗していたら、原因を修正して、ビルドをやり直すということを行っていました。また、複数のワークステーションを駆使して行う分散ビルドも自分で開発したツールで行っていました。

メモリ破壊とメモリリーク

開発がある程度進んで、最も大きな問題となったのは、「メモリ破壊」と「メモリリーク」です。コピーができるようになっても、1ページのコピー動作ごとにメモリが2MBもリークしていたのです。

当時、purifyが登場していましたが、ほとんど使えませんでした。マルチスレッドでは正しく動作しなかったの、purifyを使用すると動作が恐ろしく遅くなるため、普段の開発では使えませんでした。

この時、問題を解決するには、メモリ管理を自分で行う必要があると私は判断しました。そして、C++のnew演算子とdelete演算子を独自に実装し、メモリ破壊やメモリリークを検出する機構を入れ、問題を解決することにしました。

つづく