技術日誌

DB,Java,セキュリティ,機械学習など。興味のあることを雑多に学ぶ

分散処理本第37回に参加

Distributed Computing: Principles, Algorithms, and Systems

Distributed Computing: Principles, Algorithms, and Systems

本書の概要

CHAPTER 10 Deadlock detection in distributed systems

から

10.5  Knapp’s classification of distributed deadlock detection algorithms

までを読みました

  • 広く浅く学べる大学院の教科書的な本らしいです。
  • これを手引きに論文を引いて実装するのが良い。
  • 余談ですが、読書会の参加人数は対数線形で減っていくらしい

感想

「待ちグラフ」って名前はDBスペシャリスト試験などで何度も出てくるから知っていますが、 具体的にどのようなものか知りませんでした。DBの実装経験があるような方々もおられたため、実際に実装するならこういうアルゴリズムでやる、など会話をしているうちに、机上の理論だったものが実際のイメージに落ちていく感覚がとても面白かったです。

研究職やミドルウェアを作っておられるような本物の技術者のような方が中心なので、レベルの差を感じるものがありました。強い

勉強会ノート

雑でまとめきれていませんが。一応書いたのでブログに乗せます。

  • 分散システムではデットロックの検出は基本的にできない、という結論になりそう?

There is only one copy of each resource.

  • それぞれのコピーは一個しかない、的な慣用句

Wait-for graph

  • 待ちグラフの実装は面倒
  • グラフ処理は行列処理でやる。
  • 待ちグラフは効率が悪い

  • deadlockの対処

    • prevention:非効率
    • avoidance:(分散システムでは)実用的ではない
    • detection:これが良い(本書ではここを掘り下げる)

This is the main reason why many deadlock detection algorithms reported in the literature are incorrect.

  • とあるが、分散システム界隈では論文記載のデッドロック検出アルゴリズムが不完全というのは一般的な認識なのか?
  • 論文が不完全というのは常識ではない。
  • 想定が甘い、というのはある。

  • 10.4.2のANDモデルの例が適切ではない?

    • デッドロックを起こしているものがAndモデルの例として適切でない(複数のリソースを要求しておらずどのプロセスも一つの要求しか出していない)
  • Orモデルの場合のデッドロックの検出条件

    • 1.Each of the processes in the set S is blocked.
    • 2.The dependent set for each process in S is a subset of S.
    • 3.No grant message is in transit between any two processes in set S.
  • knotの定義がわからない?

    • 分散システムでも一般的な概念ではない
    • サイクルとの違いがわからない
      • サイクルよりも強い
        • サイクルとの違いは

In the OR model, the presence of a knot indicates a deadlock [19]. In a WFG, a vertex v is in a knot if for all u :: u is reachable from v: v is reachable from u. No paths originating from a knot shall have dead ends.

  • 参加者の方が論拠の論文をサクッと読んで定義を説明まとめてくれました(すごい)

  • この例だと

    • {c,d}はknot
    • {a,b},{c,d}はcycle
  • こちらのサイトがわかりやすい

http://cse.csusb.edu/tongyu/courses/cs660/notes/deadlock.php

  • そのノード以外に到達できないような集合がknotでは?

  • ノットにしか到達できないグラフはORモデルのデッドロック

  • 現段階ではデータベースは全てシングルリソースべき

    • ORモデル ANDモデルはない?
  • マルチマスターでのデッドロックの検知はそんなに優しくない

  • 従来の検知手法が使えない
  • knot以外へのリソース要求がないことを証明しないといけない。
  • タイムアウトするしかないのでは?
  • AWSはマルチマスターをやっているらしい(無謀?超絶技術?)
  • pqモデル?
    • 定義だけで証明はまだ示されていないが最強?
  • knotはグラフ理論では既約多項式とか言った気がする

既約多項式 - Wikipedia

  • 最後にknotの定義を調べてやろう、ということで神林さんが別室から鈍器のようなグラフ理論の本を持ってきた。

Java読書会「現場で役立つシステム設計の原則」を読む会 第1回に参加

本の概要

投票の結果こちらの本になりました。 ペース的に4回くらいになりそうです。 前回の投票でもKotlinの本に続き2位だったそうです。かなり人気な本だと思います。

正誤表:

サポートページ:現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法:|技術評論社

賛否

賛否のある本だと思いますが、思っていたより批判が激しい感じでした。

というのも「現場で役立つシステム設計の原則」というタイトルですが、現場として次のものが暗黙的に想定されていると思います

  • WEBシステムである
  • モダンなJavaでSpringのようなフレームワークが使える
  • バックエンドはRDBを使う
  • 国内向けのシステムで内製ができる(アジャイルが可能な現場)
    • 超大規模ではなく、国際化対応などはしなくて良い

著者が想定しきれていないほど多様な「現場」の方が参加されているので、業界が違う技術者に「原則」として著者の経験や考えが読み上げられてしまうと異論が湧き出るだろうなという印象です。

全体的にこのアマゾンレビューのスタンスな方が多い印象です。

この業界に溢れた、「オレのやり方がナンバーワンだ」の本の域からは出ていないように思います。「変更が容易」の判断基準や根拠が極めて定性的なまま、著者の考えにそぐわない既存の設計手法を「誤り」と断じる論調にはだんだん首をかしげてしまいました。 著者は『ドメインモデルのコードの中に、すべてのビジネスルールを凝集させてしまいたい』派なのだと思います。オブジェクトを第一に考えている、という点で確かにオブジェクト指向には違いないです。が、DBやUIとのミスマッチをどう埋めるか?という話に関しては論理に雑な印象を受けました。ストアドやリッチクライアントなど、ビジネスロジックを散在させる要素はそこかしこにあるわけで、「あくまでドメインモデルのコード内に閉じ込めるのが正しく、そうできないのはやりかたが悪いから」なスタンスは、現実から目をそらした排他主義に見えます。著者が「正しい」とする方法が、果たして本当にどこまで経済的にメリットのあるやり方なのかは疑問に感じました。 本書は「こういう考え方もあるのか」の視点で読める人には良書と思います。しかしながら、初心者が最初に読むには全くお勧めしません。著者の考えに強く賛同する人であったとしても、この本に「正しい」と書かれていることを妄信すべきではない、と思いました。

https://www.amazon.co.jp/gp/customer-reviews/R1SZREL1NO1RXE/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&ASIN=B073GSDBGT

一方的に批判がでる箇所もあり、主催の高橋さんがバランスを取って何度かフォローされていますが、DDD界隈の方が参加されていい感じの反論が聞けたら面白いのになと思っています。

感想

オブジェクト指向Javaを極めるとDDDに行き着くと思っていましたが、そうでも無いようです。実装が好きな人、モデルが好きな人、ドメインが好きな人、という感じに(もっと多いかも)枝分かれするようです。

ベテランの方が多いので、全員「ドメイン駆動設計」のファンだと思い込んで参加したのですが、そういう方がいなかったので衝撃的でした。*1

私自身はDDDの「プログラミングの対象領域を大事にする」という考え方は結構好きです。設計はあまりわかっていないので、勉強になるところも多かったです。

以下読書会ノートです。

オブジェクト指向とは何か

設計とプログラムどっちのことを言っているのかわからない

モデルとプログラムは一致するか?

  • 最近のシステム開発は紙ベースの業務を置き換えるだけではない
  • まだ世の中にないビジネス。どうやってITで作る?

文字数制限の話

文字数たくさん使ってわかりやすい単語で作れ的な本だが、 長い命名を使うことにも問題がある

ShipingCostの設計について

class ShippingCost {
    static final int minimumForFree = 3000;
    static final int cost = 500;
    int basePrice ;

    //メソッドの実装略
    ShippingCost(int basePrice);
    int amount();
}

int shippingCost(int basePrice) {
    ShippingCost cost = new ShippingCost(basePrice);
    return cost.amount() ;
}
  • 重複をなくすだけならstatic methodにくくり出すだけで良い
  • このクラス設計はいけてない。最近だとロジックとしてラムダ式とかを渡す?

計算の対象になる値が異なれば、別のオブジェクトに分けるのがオブジェクト指向らしい設計です

  • この意見は局面による
  • 直接newしたものを使うべきではない。(interfaceを使う例が後に出てくる)
    • 結果を知りたい側(amountが欲しい呼び出し側)がbasePriceを知らない場合は、こんな設計もあり
  • ライフサイクルが長いから送料クラスが意味を持つ
  • 好意的に解釈するとプライムな変数ではなく、独自定義型を使うというのはOK
  • intは使うな
    • int型の最大値は21億だが、その規模の額を扱わないシステムでもカードなどで端数の扱いの違いがあるのでintは危険
  • 国境を超えるシステムでは送料計算はめちゃくちゃ大変。
  • エリックエヴァンスの本に貨物を輸送する例が出てくる
    • DDD界隈の人達は貨物を輸送する例を好む傾向がある
  • 著者はDDD界隈の人
    • このへんからドメイン駆動設計への誘いを感じる。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

業務構造とプログラムを一致させる

業務の構造とプログラムの構造が一致していれば

int itemPrice = (basePrice + shippingCost) * taxRate();
  • 例えば上記は軽減税率などが入ると、税金計算の際に、料金だけでなく、明細が必要になってくる
    • そうなると破綻する
  • 一致させるのは大変難しい

DBとタイムゾーン

ロケールを明記していないと、タイムゾーン情報が消える(必ずUTCになるわけでない) この辺はサマータイム対応が難しい、ということになる。

可視性について

class Customers {
    List<Customer> customers;

    ...

    List<Customer> getList() {
        return customers;
    }
}
  • List<Customer> customers;はprivateスコープにすべきでは?とはという話が出た
  • DDDをする上で可視性などは些細な問題なのでは?

Listへの参照をそのまま外部に渡すと、要素の追加や削除がCustomersクラスの外部からできてしまいます。`

  • だとしてもこのような前提があるならば、可視性はこだわってサンプルコードを作成するべき
  • 可能な限りpackageスコープにする。という記述がある。その方針に沿っているのでは?

“関連性の強いクラスは同じパッケージに集めます。そして、クラスやメソッドのスコープ(参照範囲)は、可能な限りパッケージスコープにします。”

  • デフォルトで可視性をpublicにする風潮は如何なものかと思う*2
  • Java9で入ったモジュールは難しい
    • それだけを扱っている本が3冊くらい出ている

この著者は防衛プログラミング反対派

まだこの章に到達していませんが、懇親会などで結構話題に上がりました。

Javaの互換性

コレクションオブジェクト

class Customers {
    List<Customer> customers;

    ...

    Customers add(Customer customer) {
        List<Customer> result = new ArrayList<>(customers);
        result.add(customer);
        return new Customers(result);
        //return new Customers(result.add(customer));👈誤植間違い

    }
}
  • コピー先のCustomersでも参照先のcustomerオブジェクトは変更できるがいいのか?(シャローコピー、ディープコピーの話)
  • ここまでの文脈的にCustomerはimmutableなオブジェクトなのでは?
  • 少し後ろに説明が書いてある

“unmodifiableなListでも、個々の要素のオブジェクトの内容は変更できます。コレクションの要素を値オブジェクトにすればこの変更も防げます。”

  • コンストラクタの説明がない
  • list行間の下の...に書かれているのでは

Listオブジェクトのファクトリメソッドについて

  • List.of() 変更不可能なクラスを返す。可変長引数のみ。コレクションを引数を取ることはできない
  • Arrays.aslist() 固定サイズのlistを返す
  • Collections.unmodifiableList 変更不可能なクラスを返す
  • Collectors.toList() ストリームとかで使う?

技術者の人種

  • モデル屋さん

    • モデルを作るのが目標
    • 自動生成とかやりたがる
    • 別の設計関係の本の読書会で意見が合わないことがあった
  • ドメイン屋さん

  • 実装屋さん
    • この読書会の大半の人?

https://ja.wikipedia.org/wiki/モデル駆動型アーキテクチャ

  • プログラマ不要説は度々流行るが、結局流行らない
    • if文やfor文を違う表現で出してるだけ。
    • caseツール

※モデル屋さんとドメイン屋さんが来ておらず、偏った議論になっているのでは?と感じた。

インスタンスを返すメソッドの意味は?

以下のコード。なんの意味があるのか?という質問が出た

class Customers {
    List<Customer> customers;

    ...
    //👇これ
    Customers add(Customer customer) {
        List<Customer> result = new ArrayList<>(customers);
        return new Customers(result.add(customer));
    }
}
  • メソッドチェーンができる
  • 標準ライブラリのimmutableなオブジェクトも大体この仕様

サブストリングにおけるStringPoolの扱い

最近はやってない

Strng.Classのソースを確認したところ、確かに

  • substring(int beginIndex, int endIndex)
  • StringLatin1.newString(value, beginIndex, subLen)
    • new String(Arrays.copyOfRange(val, index, index + len), LATIN1)

こんな感じで新たなインスタンスが作られるようになっていました。

public String substring(int beginIndex, int endIndex) {
    int length = length();
    checkBoundsBeginEnd(beginIndex, endIndex, length);
    int subLen = endIndex - beginIndex;
    if (beginIndex == 0 && endIndex == length) {
        return this;
    }
    return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
                      : StringUTF16.newString(value, beginIndex, subLen);
}
  • サブストリング側の生で親がGCされずに残る負荷が多い。
    • 親が消えても子供が残っているから親が消えない。(GC対象にならない)

なぜラッパークラスを使っているのか

  • Booleanではなくbooleanを使うべき
private Boolean isChild() {
    return customerType.equals("child");
}
  • Object.equalsはboolean型を返すが?
    • 無駄なBooleanクラスを使ってる
  • Booleanを使っている意図がわからない
  • 執筆にワードを使って書いてる説
  • customerTypeの値はメソッド内に持たなくて良いのか?
    • ちょっと変えてストラテジーパターンに落ち着くいつものパターンでは?

Reservationクラスは不変クラスにしなくていいのか

  • addFeeでオブジェクトの状態を変えられるけどそれはいいのか?
class Reservation {
    List<Fee> fees; //大人と子供の内訳は不明

    Reservation() {
        fees = new ArrayList<Fee>()
    }

    void addFee(Fee fee) { //大人でも子供でも追加できる
        fees.add(fee);
    }

    Yen feeTotal() {
        Yen total = new Yen() // 合計ゼロ円
        for( Fee each : fees ) {
            total.add( each.yen() );
        }
        return total;
    }
}
  • 現実的には徹底的にやる必要はない。どこかで線引きが必要
  • 徹底的にやると目指せhaskellになってしまう。

enum

  • enumは積極的に進めるべき
  • enumにメソッドを定義できるのはJavaの良い点
  • enumのメンバは大文字にすべきだが?
    • 小文字でMapにアクセスしたいので発想はわかる
enum FeeType {
    adult( new AdultFee() ),
    child( new ChildFee() ),
    senior( new SeniorFee() );

    private  Fee fee;
    // Feeインターフェースを実装したどれかのクラスのオブジェクト

    private FeeType(Fee fee) {
        this.fee = fee; // 料金区分ごとのオブジェクトを設定する
    }

    Yen yen() {
        return fee.yen();
    }

    String label() {
        return fee.label();
    }
}
  • enumをDBで管理したいという要望があると、このような設計はできない

型引数

Set nextStates = EnumSet.of(承認済,差し戻し中);
                                 // 状態のグルーピング
  • Setの宣言時に型引数を使うべき

日本語の定義名

  • Enumの定義名が日本語でわかりやすくて良い
enum State {
    審査中,
    承認済,
    実施中,
    終了,
    差し戻し中,
    中断中
}
  • Javaだとファイルにした時に問題が怒る
  • 「が」とかはmacOSでへんなバグりかたする
  • クラス名には使わない方が良い。
    • 日本語を使うのはメソッド名にまでに止めること

EnumMapを使うべきか?

class StateTransitions {
    Map<State, Set<State>> allowed;

    {
        allowed = new HashMap<>();

        allowed.put(審査中, EnumSet.of(承認済, 差し戻し中));
        allowed.put(差し戻し中, EnumSet.of(審査中, 終了));
        allowed.put(承認済, EnumSet.of(実施中, 終了));
        allowed.put(実施中, EnumSet.of(中断中, 終了));
        allowed.put(中断中, EnumSet.of(実施中, 終了));
    }

    boolean canTransit(State from, State to) {
        Set<State> allowedStates = allowed.get(from);
        return allowedStates.contains(to);
    }
}
  • HashMapではなく、EnumMapを使うべき
  • 「終了」をセットしていないので、終了で呼ぶとぬるぽで落ちる
    • バグでは?
    • EnumMapを使った場合もぬるぽが発生する
  • EnumMapを使うメリットは?
    • コンパイル時にKey側の要素数がわかるので、メモリ効率が良い
    • (パフォーマンス以外の)機能的な利点は特になさそう?

データクラスと機能クラス

UtilとかCommonな便利クラスを作るなという恒例のアレ

  • 悪い共通クラス
    • RXJavaが共通ライブラリの最たる例。
    • メソッドが多すぎる。それでも使うけど。

Javaのルーツ(ちょっとざわついた)

Javaは、C言語からの移行しやすさを重視して設計されました。

オブジェクト指向設計の基本

ロジックの置き場所やクラス名/メソッド名の改善を続け、より良い設計を見つけていくのが、オブジェクト指向設計の基本です。

凝集度

“このように、関連性の強いデータとロジックだけを集めたクラスを凝集度が高いと言います。”

  • モジュール設計のことを書いて凝集度を説明するのは、少し説明不足な気がする
    • そもそも凝集度なんかは本を読んでもよくわからない。
      • 実際に手を動かして作ってみるべき。

データと処理を同じクラスに置く(分離させない)

“データとロジックを一体にして業務ロジックを整理する”

  • この話はWEBアプリが流行り始めた頃からかなり議論されている
  • lombokhibernateのが許容できない感じの思想
    • hibernateのデータクラスもロジックをぶら下げることは可能

時間の流れ?

時間軸に沿った業務の基本の流れを軸に、業務ロジックの関係を整理します。

  • この手法は初めて聞いた
    • 注文と出荷は非同期。同期する必要はない(溜め込めばいい)

パッケージ間の参照

図3-3

出荷する時に顧客は見ないのか?

  • has a関係のようなものでは?
  • クラス単位の参照関係のようなものでは?
  • パッケージの関連では?

  • モジュラー屋さんはよくこのようなUMLを書く

    • 矢印がないからと言って、本当に参照しないという意味ではないのではないか。

技術選定などのフェーズが抜けている

  • Spring,Javaありきで技術選定などのフェーズが抜けている

    • グランドデザインができてしまっている
    • どうしてもWEBアプリケーションで成り立つ理論になる
      • 大企業の調達システムなどでは通用しない考え方
  • 動き(設計)を想定しながら分析する

    • 最初に習ったオブジェクト思考設計は確かにこんな感じ
  • 分析と設計と実装を一人の人間がやるのは小規模でないと難しい

  • この本は実装者に結構寄り添っている

  • モジュラー屋さんほどコードから離れていない
    • モジュラー屋さんはプログラミング実現性みたいなものを全く考えない。
  • ものごとは綺麗なツリーにはならないので、多重継承*3の形で分析することもある

次回Chapter 4の“なぜドメインモデルだと複雑な業務ロジックを整理しやすいのか”から

*1:いたらすみません

*2:この本の話では無い

*3:Javaではプログラミングで表現できない

C言語で美咲フォントを扱う3

あまり進捗良くありませんが、とりあえずバイナリデータのどこにフォントがあるのか確認できるようになりました。

$ echo "犬" |nkf -s|hexdump 
0000000 8c a2 0a                        

まあこんな感じでSjisのコードを調べた後、そのデータの配列を作成し、

  char test_letter[2] = {0x8c,0xa2};
  int block_index= block_potition(header.block,test_letter);
  //文字が存在しているブロックの先頭まで、何文字あったか確認する
  int base = count_letters_untill_block(block_size,block_index);

  //ブロックの先頭から何番目めに文字があるのか確認する
  int offset = char_2B_hex_minus(test_letter,header.block[block_index].start_code);

  int  tmp =HEADER_SIZE + tablesize * (sizeof(bloack_table_entry))
              + (base+offset) * (sizeof(font_data));

  printf("tmp is %d\n",tmp);

こんな感じのコードを書くとFONTX2形式ファイル中の文字データの位置がわかります。 それをバイナリエディタで抜き出した後、適当に置換して見ました。*1

// 憂憂    憂
// 憂憂  憂憂憂
// 憂  憂憂憂憂憂
// 憂憂  憂  憂
// 憂憂憂    憂憂
// 憂      憂
// 憂  憂憂憂憂憂


//   鬱  鬱  鬱
// 鬱鬱鬱  鬱鬱鬱
//   鬱鬱鬱鬱鬱
// 鬱鬱鬱鬱鬱  鬱
// 鬱鬱鬱鬱  鬱
//   鬱        鬱
//   鬱鬱鬱  鬱


//       犬  犬    
//       犬        
// 犬犬犬犬犬犬犬  
//       犬        
//       犬        
//     犬  犬      
// 犬犬      犬犬  
// 

憂鬱と犬のデータ場所を探して見たけど、憂はなんか違う感じがする?

もしかして憂ではなく優を出している? と思い優を確認して見たところ、こっちの方が優っぽい。

    優優優優優  
  優  優優優    
優優  優優優    
  優優優優優優  
  優優優  優優  
  優    優      
  優優優  優優  

bmpファイルにしたりすると、わかりやすいのかなぁ。

*1:もちろんいまは手作業でやっているがいずれこの部分もプログラムにするつもり

パタヘネのMIPS学習環境をMacOSのVagrant上に作成する2

目的のMIPSをクロスコンパイル、アセンブル、逆アセンブルする環境が整いました。

おまけに実行環境まで揃いました。

手順は例の如く、この動画の通り進めます。

www.youtube.com

ロスコンパイル

まずはHelloWorldプログラムをコンパイルして見ます

#include<stdio.h>

int main(){
    puts("hello world\n");
    return 0;
}

以下のコマンドでx86/MIPSなどのCPUターゲット向けにビルドしています

gcc helloworld.c -o hellox86
mips-linux-gnu-gcc helloworld.c -o hellomips

それぞれ作成したhellox86とhellomipsファイルの情報を表示して見ます

$ file hellomips
hellomips: ELF 32-bit MSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 3.2.0, BuildID[sha1]=99476e03a7fd711a9db72e3d26aed2eedfc4b978, not stripped

$ file hellox86
hellox86: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=02c9c175d055b19d03b26fa37f9651d967c85611, not stripped

MIPSのソースを動かす

もちろんゲストOSのdebianx86で動いている想定なので、直接mips用のバイナリを動かすことはできません

$ ./hellox86
hello world

$ ./hellomips
-bash: ./hellomips: cannot execute binary file: Exec format error

qemuを入れてそちらで動かします

sudo apt-get install qemu-user

動かして見たところ、動的リンクに失敗しているようです

$ qemu-mips hellomips
/lib/ld.so.1: No such file or directory

静的リンクに切り替えてコンパイルして実行して見ます

$mips-linux-gnu-gcc -static helloworld.c -o hellomips_static

$ file hellomips
hellomips: ELF 32-bit MSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 3.2.0, BuildID[sha1]=99476e03a7fd711a9db72e3d26aed2eedfc4b978, not stripped

$ file hellomips_static
hellomips_static: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=4f0a58ae0f277369b2cc5d635e9226f7e30ed0e7, not stripped

statically linked

-staticオプションを利用してコンパイルしたもののファイル情報が変化しました。

静的リンクをしているのでやはり桁違いにバイナリサイズが大きいです

$ls -alt hellomips hellomips_static
-rwxr-xr-x 1 vagrant vagrant 601504 Aug 13 21:41 hellomips_static
-rwxr-xr-x 1 vagrant vagrant   6384 Aug 13 21:11 hellomips

では気を取り直して実行して見ます

$ qemu-mips hellomips_static
hello world

gccアセンブルせずにコンパイルだけする方法

gccでは-Sオプションをつけることでオブジェクトコードに変換せずアセンブリまででストップすることができます

$mips-linux-gnu-gcc -S helloworld.c -o helloworld_mips.s
$ cat helloworld_mips.s
    .file   1 "helloworld.c"
    .section .mdebug.abi32
    .previous
    .nan    legacy
    .module fp=xx
    .module nooddspreg
    .abicalls
    .text
    .rdata
    .align  2
$LC0:
    .ascii  "hello world\012\000"
    .text
    .align  2
    .globl  main
    .set    nomips16
    .set    nomicromips
    .ent    main
    .type   main, @function
main:
    .frame  $fp,32,$31      # vars= 0, regs= 2/0, args= 16, gp= 8
    .mask   0xc0000000,-4
    .fmask  0x00000000,0
    .set    noreorder
    .cpload $25
    .set    nomacro
    addiu   $sp,$sp,-32
    sw  $31,28($sp)
    sw  $fp,24($sp)
    move    $fp,$sp
    .cprestore  16
    lw  $2,%got($LC0)($28)
    addiu   $4,$2,%lo($LC0)
    lw  $2,%call16(puts)($28)
    move    $25,$2
    .reloc  1f,R_MIPS_JALR,puts
1:  jalr    $25
    nop

    lw  $28,16($fp)
    move    $2,$0
    move    $sp,$fp
    lw  $31,28($sp)
    lw  $fp,24($sp)
    addiu   $sp,$sp,32
    jr  $31
    nop

    .set    macro
    .set    reorder
    .end    main
    .size   main, .-main
    .ident  "GCC: (Debian 8.1.0-12) 8.1.0"

これでMIPSアセンブリが作成できました。

アセンブル/逆アセンブル

では次にこちらをアセンブルしてELFファイルを作って逆アセンブルしてみます

$ mips-linux-gnu-gcc -static helloworld_mips.s -o hellomips_static2

ファイルサイズを表示して見たところ全く同じだったので、diffをかけて見たところ全く同じ内容だった(当たり前といえば当たり前) GCCが自動的にコンパイル->アセンブルしている流れを手作業で段階的に同じステップを踏めるのは勉強するときに便利だと思う。

$ ls -alt hellomips_*
-rwxr-xr-x 1 vagrant vagrant 601504 Aug 13 22:27 hellomips_static2
-rwxr-xr-x 1 vagrant vagrant 601504 Aug 13 21:41 hellomips_static
$ diff hellomips_static hellomips_static2
(出力なし)

では逆アセンブルして見ます

$ mips-linux-gnu-objdump -d hellomips

結果(でかすぎるのでmain関数のところのみ) とりあえずstackを確保しているあたりくらいはアセンブリとほぼ同じですね。(正直アセンブリまだよくわかりません)

000007d0 <main>:
 7d0:   3c1c0002    lui gp,0x2
 7d4:   279c8240    addiu   gp,gp,-32192
 7d8:   0399e021    addu    gp,gp,t9
 7dc:   27bdffe0    addiu   sp,sp,-32
 7e0:   afbf001c    sw  ra,28(sp)
 7e4:   afbe0018    sw  s8,24(sp)
 7e8:   03a0f025    move    s8,sp
 7ec:   afbc0010    sw  gp,16(sp)
 7f0:   8f828034    lw  v0,-32716(gp)
 7f4:   244409d0    addiu   a0,v0,2512
 7f8:   8f82805c    lw  v0,-32676(gp)
 7fc:   0040c825    move    t9,v0
 800:   0320f809    jalr    t9
 804:   00000000    nop
 808:   8fdc0010    lw  gp,16(s8)
 80c:   00001025    move    v0,zero
 810:   03c0e825    move    sp,s8
 814:   8fbf001c    lw  ra,28(sp)
 818:   8fbe0018    lw  s8,24(sp)
 81c:   27bd0020    addiu   sp,sp,32
 820:   03e00008    jr  ra
 824:   00000000    nop

これでパタヘネの学習内容の環境の整備に関してはやりたいことが終わりました。 このvagrantのboxファイルどっかで配布しようかな

パタヘネのMIPS学習環境をMacOSのVagrant上に作成する1

とりあえずこのビデオの通りに進めます

www.youtube.com

まずは64bitのdebianを入れる

vagrant init debian/jessie64

sudo apt-getの入手先にMIPSのクロスコンパイルに対応したGCCがない

/etc/apt/sources.listを書き換える必要がある

最終行に

deb http://httpredir.debian.org/debian unstable main

を追加

mipsのクロスコンパイラがインストールできるようになったので、レッツインストール

sudo apt-get istall gcc-mips-linux-gnu

しばらくした後、linux karnelのバージョンを上げろ、的なメッセージがでた。

You have mail in /var/mail/vagrant
vagrant@jessie:~$ cat /var/mail/
cat: /var/mail/: Is a directory
vagrant@jessie:~$ cat /var/mail/vagrant
From root@jessie.vagrantup.com Mon Aug 13 19:24:54 2018
Return-path: <root@jessie.vagrantup.com>
Envelope-to: root@jessie.vagrantup.com
Delivery-date: Mon, 13 Aug 2018 19:24:54 +0000
Received: from root by jessie.vagrantup.com with local (Exim 4.84_2)
    (envelope-from <root@jessie.vagrantup.com>)
    id 1fpISU-0000Ub-A6
    for root@jessie.vagrantup.com; Mon, 13 Aug 2018 19:24:54 +0000
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Auto-Submitted: auto-generated
Subject: apt-listchanges: news for jessie
To: root@jessie.vagrantup.com
Message-Id: <E1fpISU-0000Ub-A6@jessie.vagrantup.com>
From: root <root@jessie.vagrantup.com>
Date: Mon, 13 Aug 2018 19:24:54 +0000

glibc (2.26-5) unstable; urgency=medium

  Starting with version 2.26-1, the glibc requires a 3.2 or later Linux
  kernel.  If you use an older kernel, please upgrade it *before*
  installing this glibc version. Failing to do so will end-up with the
  following failure:

    Preparing to unpack .../libc6_2.26-5_amd64.deb ...
    ERROR: This version of the GNU libc requires kernel version
    3.2 or later.  Please upgrade your kernel before installing
    glibc.

  The decision to not support older kernels is a GNU libc upstream
  decision.

  Note: This obviously does not apply to non-Linux kernels.

 -- Aurelien Jarno <aurel32@debian.org>  Tue, 23 Jan 2018 22:03:12 +0100

現在使っているバージョンが3.16のようなので、バージョンアップが必要なようだ

vagrant@jessie:~$ uname -v
#1 SMP Debian 3.16.56-1+deb8u1 (2018-05-08)

さーてインストール可能なlinuxは?

vagrant@jessie:~$ apt-cache search linux-image |grep "x86"
linux-image-4.16.0-2-cloud-amd64 - Linux 4.16 for x86-64 cloud
linux-image-4.17.0-1-cloud-amd64 - Linux 4.17 for x86-64 cloud
linux-image-cloud-amd64 - Linux for x86-64 cloud (meta-package)

よくわからんからとりあえず最新版にしとくか

sudo aptitude install linux-image-4.17.0-1-cloud-amd64

インストール完了

$ mips-linux-gnu-
mips-linux-gnu-addr2line     mips-linux-gnu-gcc-nm        mips-linux-gnu-ld.bfd
mips-linux-gnu-ar            mips-linux-gnu-gcc-nm-8      mips-linux-gnu-ld.gold
mips-linux-gnu-as            mips-linux-gnu-gcc-ranlib    mips-linux-gnu-nm
mips-linux-gnu-c++filt       mips-linux-gnu-gcc-ranlib-8  mips-linux-gnu-objcopy
mips-linux-gnu-cpp           mips-linux-gnu-gcov          mips-linux-gnu-objdump
mips-linux-gnu-cpp-8         mips-linux-gnu-gcov-8        mips-linux-gnu-ranlib
mips-linux-gnu-dwp           mips-linux-gnu-gcov-dump     mips-linux-gnu-readelf
mips-linux-gnu-elfedit       mips-linux-gnu-gcov-dump-8   mips-linux-gnu-size
mips-linux-gnu-gcc           mips-linux-gnu-gcov-tool     mips-linux-gnu-strings
mips-linux-gnu-gcc-8         mips-linux-gnu-gcov-tool-8   mips-linux-gnu-strip
mips-linux-gnu-gcc-ar        mips-linux-gnu-gprof         
mips-linux-gnu-gcc-ar-8      mips-linux-gnu-ld            

使いそうなもの

使い方よくわからんので、次回は使い方編のビデオを見たいと思います。

https://www.youtube.com/watch?v=iSiYGifDltI

かなり良い情報なんだけどgoodと再生数少ないね...

https://www.linuxquestions.org/questions/linux-software-2/how-to-upgrade-libc6-i-am-not-able-to-install-730618/

vagrantを導入

RubyDSL(VagrantFile)でVirtualBox,VMWareなどをお手軽にいじれちゃうツール的な印象です。

www.vagrantup.com

仮想マシンの設定

  • テンプレートの追加vagrant box add

公式に方に以下の通りにあったのでまずはhashicorp/precise64を試して見た

For example: vagrant box add hashicorp/precise64. You can also quickly initialize a Vagrant environment with vagrant init hashicorp/precise64.

仮想マシン名はlistで出てきたものを選択

仮想マシンの状態管理

接続

仮想マシンにはvagrant sshで接続する。 切断はゲスト側でexit

共有ファイル

ホストOSで仮想マシンを作成した作成したフォルダは、 ゲストOSのログインパス/vagrantと共有フォルダになっている

->そこを利用すれば普段通りの環境でC言語などを作成してVM側でコンパイルできる

Provision(自動初期設定)

vagrant initしたときにできるファイル(Rubyスクリプト) 内にvagrant upしたときに自動で実行する設定(Provision)をかける。 chefなども動かせるが、hello worldを書いた

  config.vm.provision :shell, :inline => "echo hello world"

が実行されず

==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

再度vagrant up--provisionのオプション付きで実行するとうまくいった

もしくはvagrant provisionコマンドでprovisionを単体で実行することも可能

provitionは以下の内容でシェルスクリプトも実行できる

config.vm.provision :shell, :path => "provision.sh"

provision.shをゲストOSのVagrantFileがあるフォルダに作成して 起動時に行う内容を記述する

自分でboxを作る方法

仮想マシンを動かしているディレクトリでvagrant packageを実行 これを vagrant box add <追加後にテンプレートとして扱う名前> <作成したパッケージ名>

vagrantに追加できる。

テンプレートに追加後は、~/.vagrant.d/boxesにboxがファイルとして追加されている

ls ~/.vagrant.d/boxes

こんな感じで一人が環境設定したboxを簡単に配ることができる?(配布の方法は今の所知りません)

pluginのインストール

vagrant plugin install <plugin名>で可能

vagrant plugin install sahara

vagrant plugin listでインストールしたpluginを確認可能

プラグインのsaharaについて

VMの状態のバージョン管理的なことが行える(す、すげ〜🙄)

vagrant sandbox on
vagrant sandbox commit
vagrant sandbox rollback
vagrant sandbox status

commit後の変更はrollbackで消すことができる。

ただし、statusはgit statusほど強力ではない。(sandboxの有効/無効状態くらいしかわからない)

気になったこと

VM上のubuntsでvimが使えませんでした。(入ってないANDインストールできない)

sudo apt-get install vim
vim                vim-doc            vim-lesstif        vim-scripts
vim-addon-manager  vim-gnome          vim-migemo         vim-syntax-go
vim-athena         vim-gtk            vim-nox            vim-syntax-gtk
vim-common         vim-gui-common     vim-puppet         vim-tiny
vim-conque         vimhelp-de         vim-rails          vim-vimoutliner
vim-dbg            vim-latexsuite     vim-runtime        
vagrant@precise64:~$ sudo apt-get install vim-
vim-addon-manager  vim-gnome          vim-nox            vim-syntax-gtk
vim-athena         vim-gtk            vim-puppet         vim-tiny
vim-common         vim-gui-common     vim-rails          vim-vimoutliner
vim-conque         vim-latexsuite     vim-runtime        
vim-dbg            vim-lesstif        vim-scripts        
vim-doc            vim-migemo         vim-syntax-go      
vagrant@precise64:~$ sudo apt-get install vim-tiny 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  vim-common
Suggested packages:
  indent
The following packages will be upgraded:
  vim-common vim-tiny
2 upgraded, 0 newly installed, 0 to remove and 64 not upgraded.
Need to get 497 kB of archives.
After this operation, 0 B of additional disk space will be used.
Do you want to continue [Y/n]? y
Err http://us.archive.ubuntu.com/ubuntu/ precise-updates/main vim-tiny amd64 2:7.3.429-2ubuntu2.1
  404  Not Found [IP: 91.189.91.26 80]
Err http://us.archive.ubuntu.com/ubuntu/ precise-updates/main vim-common amd64 2:7.3.429-2ubuntu2.1
  404  Not Found [IP: 91.189.91.26 80]
Failed to fetch http://us.archive.ubuntu.com/ubuntu/pool/main/v/vim/vim-tiny_7.3.429-2ubuntu2.1_amd64.deb  404  Not Found [IP: 91.189.91.26 80]
Failed to fetch http://us.archive.ubuntu.com/ubuntu/pool/main/v/vim/vim-common_7.3.429-2ubuntu2.1_amd64.deb  404  Not Found [IP: 91.189.91.26 80]
E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

なんでやねん💢

まぁIPアドレスとかでググると英語のトラブルシュート情報がたくさん出てくるので 必要になればトラブルシュートしましょう。

参考 https://dotinstall.com/lessons/basic_vagrant

C言語で美咲フォントを扱う2

次回はコンソールに美咲フォントを出力して見たい

お約束の品です?

00111100
01000010
00000010
00001100
00010000
00000000
00010000
00000000

0を抜くとなんの文字かわかりやすいかもしれません

  1111
 1    1
      1
    11
   1

   1

前回記事のヘッダの解説が若干不足しているのでそこの補足と データ格納部の説明をしたいと思います。

$ ./font
hdader size is 18
filesignature is FONTX2
fontname is MISAKI
width is 8
hight is 8
char code flg is 1
blocks is 5c
  0 blocks start code is 0x4081
  0 blocks end   code is 0x7e81
  1 blocks start code is 0x8081
  1 blocks end   code is 0xac81
  2 blocks start code is 0xb881
  2 blocks end   code is 0xbf81
  3 blocks start code is 0xc881
  3 blocks end   code is 0xce81
・・・略
 91 blocks start code is 0x80ea
 91 blocks end   code is 0xa4ea

こんな感じでヘッダが18バイト続いた後、 blockテーブル(文字コードの範囲で一まとまりにしたblockの情報をまとめたテーブル) が18バイトめの値(美咲フォントの場0x5C = 十進数で92)分だけ続く

だからフォントのデータはファイル先頭から

18+92*4=386=0x182バイト目から始まります。

で、Sjis0x8140から順番に収められているが、 そこからヘッダ分の0x182を引いた適当なオフセットで計算すればいい。。。 というそんな単純なものではありません。

例えばSjisには文字が入っていないエリアがあります。

http://charset.7jp.net/sjis.html

例えば0x9f94〜0xe040です ここはブロックが変わったときに飛ばされています。

69 blocks end   code is 0xfc9f
70 blocks start code is 0x40e0

という訳で、

  • 文字のSJISのコードを得る
  • Sjisのコードから配置ブロックを探る
  • ブロックの中でのインデックスを求める

という手間が必要な訳です。 次回はこの辺の処理をうまく完成させます。

今日の疑問

前から結構思っていますが、16進数リテラルを表現する方法は色々あるけど

0x16
16H

10進数リテラルを表現する方法はマイナーな気がする

10D

一応後ろにDとつけることで10進数という意味になるらしいが、あまり見ない。 基本的に数値に何もプリフィックス、サフィックスがついていない場合は10進数と皆しても問題ないと思うが、 個人的にこの値は16進数じゃないよ~ということを明示したい時もある*1

余談:美咲フォントに登録されている最終文字は「熙」

91ブロックめの最後です。

91 blocks end   code is 0xa4ea
1111 11
1 1  11
11 1 11
1 1  1
1111 11

1 1 1 1

読めなくないですか。ビットマップにすればいけるのかな?うーん

*1:文字セットとして10進数は16進数の完全なサブセットなため紛らわしい