「はりぼてOS」ファミリー(互換OS)用のアプリを仲良く共存させよう

  • (by K, 2006.09.27)
  • 発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです)

まえがき

  • advance/osselectによって、一つのディスクの中にいくつも「はりぼてOS」系のOSを収めて、メニューなどで選べるようになりましたが、こうなっているとadvance/osselectのこめんと欄のひよひよさんの意見のように
    でも、アプリは各OSで共通になるんですよね・・・。
    改変した場合オリジナルとファイル名を変えないと整合が取れなくなっちゃいますね・・・。
     -- ひよひよ 2006-09-27 (水) 00:40:42
    みたいな心配が生じます。
  • ということでこのページではアプリ共存に関して心配なことを提案したり相談したりするページです(みなさんはこめんと欄を使って意見を書き込んでください)。

独自API拡張による問題

  • たとえばHiyOSの場合、「RDMSR/WRMSR用API」というものを追加しています。そしてこのAPIを使ってAthlon系CPUで倍率変更を行うアプリも発表されています。このアプリは「RDMSR/WRMSR用API」がないと動作しないものですが、もしこれを別の「はりぼてOS」系OSで起動させてしまったら、存在しないAPI番号なので無視されるか、もしくはそのOS独自の拡張API番号とたまたま一致して、全く別の動作(=誤動作)をするかもしれません。
  • こういうことが簡単に起きると困ります。というか気軽にアプリを起動できませんよね。

アプリの拡張子を変える

  • 拡張子を.hrbからたとえば.hysに変更するというものです。こうすれば見た目で違いが分かります。そしてHiyOSではこの拡張子を認識できるようすればいいというわけです。もちろんHiyOSは普通の「はりぼてOS」のアプリも実行できるのですから、.hrbも受け付けられるようにしましょう。
  • そうするとこんな感じでしょうか(以下は例です)。
  • console.cより
    int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
    {
        (中略)
        /* ファイルを探す */
        finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
        if (finfo == 0) {
            /* 見つからなかったので後ろに".HYS"をつけてもう一度探してみる */
            name[i    ] = '.';
            name[i + 1] = 'H';
            name[i + 2] = 'Y';
            name[i + 3] = 'S';
            name[i + 4] = 0;
            finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
        }
        if (finfo == 0) {
            /* 見つからなかったので後ろに".HRB"をつけてもう一度探してみる */
            name[i    ] = '.';
            name[i + 1] = 'H';
            name[i + 2] = 'R';
            name[i + 3] = 'B';
            name[i + 4] = 0;
            finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
        }
        (中略)
    }
    
    • この例の場合、同名のアプリがあった場合は、.hysのほうが優先されます。
  • この方法は他の方法と併用することもできます。ファイル名で区別できるので見た目でわかりやすいという効果もあります。
  • しかし反面、OSの数だけアプリの拡張子が増えることにもなり、データの拡張子と区別がつかなくなってくるかもしれません。
  • 他の方法と比較していうと、これは「人間に区別してもらう」方法といえます。

アプリシグネチャを変える

  • これは、.hrbの実行バイナリの中のシグネチャ'Hari'を何か別のものに変更してしまおうというものです。
  • もちろん従来どおりの'Hari'もOKということにはします。
  • そうするとこんな感じでしょうか(以下は例です)。
  • console.cより
    int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
    {
        (中略)
        if (finfo != 0) {
            /* ファイルが見つかった場合 */
            appsiz = finfo->size;
            p = file_loadfile2(finfo->clustno, &appsiz, fat);
            if (appsiz >= 36 && (strncmp(p + 4, "Hari", 4) == 0 || strncmp(p + 4, "HyOS", 4) == 0) && *p == 0x00) {
                (中略)
            } else {
                (中略)
            }
            (中略)
    	}
        (中略)
    }
    
  • この方法だと、間違って他OSのアプリを実行しようとしても、誤動作せずに、アプリのファイルフォーマットエラーが出てくれます。なかなか嬉しいかもしれません。
  • 拡張子も.hrbに統一しておけば、アプリということもすぐに見分けられそうです。
    • しかし、アプリだと分かっても実行しようとするとどのOSでやってもエラーばかりで、「おい、じゃあ、いったいどのOSでなら動くんだよ!」とツッコミたくなる場面があるかもしれません(苦笑)。それはちょっとまずいですね。そんな場合のために、
    • appsig.c
      void HariMain(void)
      {
          char s[32], *p;
          int i;
      
          api_cmdline(s, 30);
          for (p = s; *p > ' '; p++) { } /* スペースが来るまで読み飛ばす */
          for (; *p == ' '; p++) { }     /* スペースを読み飛ばす */
          i = api_fopen(p);
          if (i == 0) {
              api_putstr0("file open error!\n");
          } else {
              if (api_fread(s, 8, i) < 8) {
                  api_putstr0("file too small!\n");
              } else {
                 api_putstr1(s + 4, 4); /* 4バイト目から4文字を表示 */
                 api_putchar('\n');
              }
          }
          api_end();
      }
      
    • というアプリを作っておくといいかもしれません。 >appsig app.hrb と実行すると、app.hrbファイルのシグネチャ部分の4バイトが表示されます。HyOSと出れば、おお、これはHiyOS用のアプリなのか!と気付けるというわけです。
  • この方法の最大の問題点は、bim2hrb等をそのまま使うだけは、アプリシグネチャが'Hari'のアプリしか生成できないので、後でバイナリエディタで書き換えるか、もしくは専用のツールを用意しなければいけないということです。
  • 他の方法と比べていうと、これは「OSに区別してもらう」方法といえそうです。

バージョン取得APIを用意する

  • これは、OSにバージョン取得APIを作り、アプリはそのAPIを使ってOSを区別しようというものです。たとえば仮にHiyOSのOS識別番号が1234なら、
    if (api_getosid() != 1234) {
        api_putstr0("このアプリケーションはHiyOS専用です\n");
        api_end();
    }
    という処理をアプリの前のほうに書いておけばいいのです。この方法だと複数OSにも対応できて、
    if (api_getosid() != 1234 && api_getosid() != 2345) {
        api_putstr0("このアプリケーションは\n");
        api_putstr0("HiyOSもしくはrapuOSでのみ実行できます\n");
        api_end();
    }
    なんていうことも可能です(rapuOSの識別番号が2345の場合)。
  • さらには、対応OSであればフル機能を、そうでないOSでも簡易機能を提供する、みたいな作り方もできるでしょう。
  • ついでにapi_getosver()というAPIもあったらいいと思いませんか?これはそのOSのバージョンを返すAPIです。このアプリはHiyOSのバージョン0.0.9以降か、もしくはrapuOSのバージョン0.1.2以降でのみ動作する、なんていう場合は、
    if (api_getosid() == 1234 && api_getosver() >=  9) goto ok;
    if (api_getosid() == 2345 && api_getosver() >= 12) goto ok;
    api_putstr0("このアプリケーションは...
    api_end();
    ok:
    処理本体...
    みたいに書けるわけです。
  • さて問題はこのAPIを何番にするかです。きっとみなさんすでにAPIを増やしていると思うので (増やしていない人もいるかもしれませんが)、適当に番号を決めるとぶつかってしまいます。でもこのAPIだけは番号共通でないと意味がありません。ということで、すでに元祖「はりぼてOS」で使われている番号を流用するという作戦に出ました。API番号はEDX=27です。
  • api027.nasにちょっと書き足します。
    _api_getosid:   ; int api_getosid(void);
            MOV     EDX,27
            MOV     ECX,0
            INT     0x40
            MOV     EAX,ECX
            RET
    
    _api_getosver:   ; int api_getosver(void);
            MOV     EDX,27
            INT     0x40
            MOV     EAX,EDX
            RET
    
  • こんな感じです。つまり、EDX=27では、ECXやEDXにも値を返すことにしようというわけです。ECXがOSのID、EDXがOSのバージョンです。元祖「はりぼてOS」では、EDX=27で INT 0x40 しても、EAX以外のレジスタは変更されません。それを利用することで、api_getosid()では0が、そしてapi_getosver()では27が返ってきます(元祖「はりぼてOS」のOS認識コードは勝手ながら0番とさせていただきました)。
  • それで、元祖以外の「はりぼてOS」ファミリーOSでは、API処理ルーチンを少し書き換えます。
        (中略)
        } else if (edx == 27) {
            reg[7] = task->langmode;
            reg[6] = 1234; /* OS識別コード */
            reg[5] = 8; /* バージョン */
        } else if (edx == 追加したAPI) {
        (中略)
  • というわけで、この方法は上記2つよりも良さそうに思えますが、問題が2つほどあります。
  • 一つ目はOS識別コードの管理です。○○番は○○OSを表す、みたいな管理をきちんとしないと、複数のOSが同じ識別コードになっていたりして混乱することになるでしょう。この管理をどうするかという問題があります。
  • 二つ目はアプリが識別分だけ大きくなったりややこしくなる傾向があるということです。まあでもみなさんはそれほど気にならないでしょうね。
  • 他の方法と比べていうと、これは「アプリに区別してもらう」方法といえそうです。

こめんと欄

  • 簡単な修正で済むと思いますが、”appsig.c”の確認表示で、Hariとかそのまま表示していいのでしょうか?どうせなら、HariならHariboteOS専用のアプリです。とか書いたほうがよいのでは? -- 名無しさん 2006-09-27 (水) 18:09:17
  • ご提案ありがとうございます。おっしゃるとおりです。そのへんの細かい実装ついてはみなさんにお任せします。僕としてはとりあえず基本的なアイデアを提示したかっただけなので。 -- K 2006-09-27 (水) 18:27:07
  • あと、INT 0x40 をはりぼてOS純正API専用にして、はりぼて友の会の会員に1つずつ番号を割り当てるという方法もありますね。0x40+会員番号とか。って、この方法だとそのINT番号に対応してないと一般保護例外が発動しちゃうわけですが・・・。なんにせよ、成果を共有しやすくするための仕組みは重要でしょう。次のOSCでは、Kさん以外(すごく残念です)全員集合の予定なので成果共有の仕組みについて議論したいと思います。 -- ひよひよ 2006-09-27 (水) 23:00:40
  • INT番号を別々にする方法だと、一般保護例外になってしまうことのほかに、会員数が192人を越えたら割り当てる番号がなくなってしまうという問題もあります。・・・まあそれはともかくとして、「はりぼて友の会」として推奨される方法を提唱できたらいいですね。最低限これこれのAPI群を追加してあれば「友の会準拠」と名乗ることを許すみたいにしたら、その拡張されたAPIを前提にしたアプリ開発も始まるかもしれません。 -- K 2006-09-30 (土) 12:37:45
  • 192人は無理かもしれませんが、OSCでいっぱいスカウトしたいですねぇ。一応、展示+ミニセミナー+ライトニングトーク+懇親会のゴールデンコンボで新会員をゲットしたいところ。広告塔(笑)のKさんがいらっしゃらないのは厳しいところですが。 -- ひよひよ 2006-10-01 (日) 00:36:00
  • 別な解決方法をちょっと個人ページの方に書いてみました。実行速度に関する問題は出ますが、このページで取り上げられているいくつかの問題を解決する方法だと思ってます。(簡潔に書くと、関数番号取得APIを実装するという内容です。) -- Source 2009-03-10 (火) 02:43:44
  • EBXの代わりにEDXを使うという方法はどうですか? -- skyblue 2012-09-02 (日) 14:07:20

コメントお名前NameLink

リロード   新規 編集 差分 添付   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2012-09-02 (日) 14:07:21 (3373d)