Yabu.log

ITなどの雑記

プログラマのためのSQL 読書会(21)に参加

26章の初めから26.7まで読みました。

ビューがメインテーマです。

  • ビューの更新を知る人は少ない。

    • 情報技術者試験でよく出る
    • https://www.ap-siken.com/kakomon/23_aki/q30.html
      • このサイトに更新可能ビューの条件があるが一部違っている。本書の更新可能ビューの条件を引用します。
        • 1.1つだけのテーブルから作られていること
        • 2.GROUP BY句を使っていないこと
        • 3.HAVING句を使っていないこと
        • 4.集約関数を使っていないこと
        • 5.計算列を使っていないこと
        • 6.UNION,INTERSECT,EXCEPTを使っていないこと
        • 7.SELECT DISTINCT句を使っていないこと
        • 8.ビューに含まれていない規定テーブルの全ての列はNULLを許可するかデフォルト値が指定されていること。*1
  • IBMではViewではなく「視点」というらしい。

  • 確かにIBMの汎用機周りは謎翻訳が多い

    • そういえばindicator*2のことを標識とよく言っている
  • クエリーのリライトプラグインというものがある

    • 特定の効率の悪いクエリを書き換える機構が存在するらしい
  • マルチテナントシステムだとテナントごとにビューを切ったりする。

  • ビューの権限

  • アクセス権限の設定はビューだけではない?

    • ビューだけではなく規定テーブルへのアクセス権限の設定も必要。
      • そういう作業を最近した人がいる
      • 詳しい人曰く
        • sql securityの値
          • definer
          • invocar
      • を適切に設定することで迂回できるようです。
  • oracleにはscottというデモ用のアカウントがいる

    Oracle Databaseに付属するdemobld.sqlOracle Database 10g以降ではutlsampl.sql)を実行すると「EMP」「DEPT」というふたつのテーブルと「SCOTT/TIGER」というスキーマよりなる伝統的なデモ環境が構築される。「SCOTT」とはオラクルの前身であるSDLに在籍していたBruce Scottを指し、「Tiger」は彼の愛猫の名前に由来する。Scottは優秀な開発者であり最初期のSQL*Plusも彼の手によるものとされている。Scottはすでにオラクルを後にしているが、この伝統は変わる様子がない。

  • 1対他の結合を行うクエリやスキーマをスター型のクエリ,スタースキーマというらしい。

    • ER図がスター型になるので
  • MyISAM

    • ストレージエンジン。以前はISAMと呼ばれるものだった。
      • ISAM -> C,COBOLなどの汎用機の人が詳しいかも。
    • MySQLが使い始めてMyISAMというものを改良して使っている。
  • MyISAMは古い。最近はinnoDBを使っている

  • mysqlのmerge table

  • ビューの循環参照(実験してみる)

    • ALTER VIEWで循環参照になるビューを作ればどうなるか、という話題になった。
    • tbl1←v1←v2という参照関係のテーブルビューを作る
--定義
create table tbl1(id int);
insert into tbl1 values(1);
create view v1 as select id from tbl1;
create view v2 as select * from v1;

--確認
select * from tbl1;
select * from v1;
select * from v2;

/*もちろん結果は全て

+------+
| id   |
+------+
|    1 |
+------+

になる。
*/
  • ここでv1をalter viewで変更して v1 <-> v2という循環参照を作る
    • が、この循環参照を作るalter文は実行できなかった。
alter view v1 as select * from v2;
--ERROR 1146 (42S02): Table 'test.v1' doesn't exist

--もちろん構文は間違っていない。v2をtbl1にすれば実行できる。
--alter view v1 as select * from tbl1;
--結果:Query OK, 0 rows affected (0.00 sec)
  • mysql "循環参照"でググっても関連しそうな情報はなかった。

  • SQL Serverにはindex viewがある!

  • 誤植

    • P.466のSQL,'Sales'のSが小文字sになっている
▼P.466

SELECT *
  FROM Departments
 WHERE (dept_name = 'sales') --ココ!'sales'ではなく'Sales'!!
   AND (city_name = 'New York');
  • 多分原著から間違っているのでは?(未確認ですが)とのこと。

次は26.7から!

プログラマのためのSQL 第4版

プログラマのためのSQL 第4版

*1:これは規定テーブルに行を挿入する際、値を一位に決めるためである

*2:システムグローバルなフラグ集,CPUのレジスタで言う所のEFLAGSのようなもの

30日OS自作本27日目

27日目の記事になります。というよりほとんどLDTの解説になります。

www.youtube.com

LDT

  • OSのセグメントにアプリケーションからアクセスすることはできない。(21,22日目のセキュリティ対応)
  • ただしアプリは別のアプリのセグメントにアクセスできてしまう。(コード例:crack7)
  • CPUはこの問題に対処するためにLDT(Local Descriptor Table)という仕組みを持っている
  • LDTを使うことでタスクローカルなセグメントを作ることができる。

LDTのアドレス指定

タスク切り替えは以下のtask state segmentをGDTに登録してタスク切り替えのたびにレジスタ等に値をロードします。

struct TSS32 {
    int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
    int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
    int es, cs, ss, ds, fs, gs;
    int ldtr, iomap;
};

このTSSのldtrにLDTの開始アドレスを登録するとタスク切り替え時にLDTの場所をCPUが指定することになります。

hariboteOSでmtask.cでLDTを設定しています。

#define MAX_TASKS       1000    /* 最大タスク数 */
#define TASK_GDT0       3       /* TSSをGDTの何番から割り当てるのか */

//こいつらは権限。一応書いていますがとりあえず今は関係ないので無視でOK
#define AR_TSS32       0x0089
#define AR_LDT         0x0082

set_segmdesc(gdt + TASK_GDT0 + i            , 103, (int) &taskctl->tasks0[i].tss, AR_TSS32); /*タスクのGDTの設定*/
set_segmdesc(gdt + TASK_GDT0 + MAX_TASKS + i, 15 , (int) taskctl->tasks0[i].ldt , AR_LDT  ); /*LDTの設定*/

LDTを使う前後の比較

アプリのセグメントの設定

LDTを使う前のcmd_app()ではGDT上のタスクのTSSの配置位置(task->sel)から相対的にコードセグメント、データセグメントを計算しています。

set_segmdesc(gdt + task->sel / 8 + 1000, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60); //コードセグメント
set_segmdesc(gdt + task->sel / 8 + 2000, segsiz - 1,      (int) q, AR_DATA32_RW + 0x60); //データセグメント

セグメントをセットするコードに関して整理します。

  • gdt + task->sel / 8 + 1000

これはコードセグメントですが、各項は以下の意味を持っています。

意味
gdt GDTのベースアドレス
task->sel/8 GDTを参照する時のセレクタ
1000 GDTの1000~2000をアプリケーションのコードセグメントとするための加算アドレス

task->selをなぜ8で割るかというと、GDTのセレクタ値は8の倍数で指定されているからです。

index sel(dec) sel(binary)
0 0 0b00000
1 8 0b01000
2 16 0b10000
3 24 0b11000
4 32 0b20000

これはGDT場では8飛びでデータが配置されているという意味ではなく、あくまでGDTを指定するためのインデックスの仕様です。 詳しくは後述するのでそちらを参照してください。

このようにLDT導入前ではTSSの配置indexからアプリケーションのコードセグメント、データセグメントを相対的に求めてGDT上に確保しています。

例:TSSがGDTの5の位置のアプリの場合,

  • index = 5
  • セレクタ値(sel) = 5*8
  • コードセグメント = sel/8 + 1000 = 1005
  • データセグメント = sel/8 + 2000 = 2005

LDT導入前のGDTのマッピングは以下のようになります。

index content
1003 task_a用(アプリはないので使っていない)
1004 idle用(アプリはないので使っていない)
1005 1個目に確保されるアプリのコードセグメント
1006 2個目に確保されるアプリのコードセグメント
... 省略
2005 1個目に確保されるアプリのデータセグメント
2006 2個目に確保されるアプリのデータセグメント

次にLDTを使う場合です。

set_segmdesc(task->ldt + 0, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
set_segmdesc(task->ldt + 1, segsiz - 1,      (int) q, AR_DATA32_RW + 0x60);

アプリケーションのセグメントはLDTを使ってこのように確保していきます。 セレクタ値を計算で使って相対的に決めている前バージョンよりコードはわかりやすくなっています。

LDTは結構上の方に書いていますが、gdt + TASK_GDT0 + MAX_TASKS + iの位置に配置されます。

例:TSSがGDTの3の位置のアプリの場合,

  • index = 5
  • セレクタ値(sel) = 5 * 8
  • ldtの位置 = TASK_GDT0 + MAX_TASKS = 1005
  • コードセグメント = ldtの0番目の位置
  • データセグメント = ldtの1番目の位置

LDT導入後のGDT内容は以下のようになります。。

index content
1003 taska用(アプリはないので使っていない)
1004 idle用(アプリはないので使っていない)
1005 1個目に確保されるアプリのLDTへの参照
1006 2個目に確保されるプリのLDTへの参照
... 省略

ちなみにLDTです。これはタスクごとに作成されます。

index content
0 アプリのコードセグメント
1 アプリのデータセグメント

アプリケーションの起動

void start_app(int eip, int cs, int esp, int ds, int *tss_esp0);
  • LDT導入前:start_app(0x1b, task->sel + 1000 * 8, esp, task->sel + 2000 * 8, &(task->tss.esp0));
  • LDT導入後:start_app(0x1b, 0 * 8 + 4 , esp, 1 * 8 + 4 , &(task->tss.esp0));

LDT導入後はどのアプリケーションもセレクタ値は

  • cs:0 * 8 + 4
  • ds:1 * 8 + 4

となります。導入前と比べてどうでしょうか。導入前はセレクタ値を加工してタスクごとに指定するセグメントが変わるようになっていますが、LDT導入後は固定値になっています。 これは衝突しないのか?と疑問に思うかもしれませんが、このセグメントレジスタの指す先はGDTではなくタスクごとに用意されるLDTなので衝突することはありません。

なぜGDTやLDTのセレクタ値は8倍したり4を加算したりするのか

そもそもなぜ8をかけているかというと、この本には解説されていませんが、

8倍の理由はセグメント番号の下位3bitは別の意味があって、ここでは0にしておかないと行けないからです。

p135(day7)より

一旦ここでセグメントの下位3bitを踏まえたセレクタ値について説明します。

セレクタ値の構造は以下の様になっています。

31       23       15       7        0
+--------+--------+--------+--------+
|             index            |T|RP|
+--------+--------+--------+--------+
図中の略称 正式名称 長さ(bit) 意味
index 13 DTの位置を示すindex
RP RPL:Requested Privilege Level 2 必要権限
T table indicator 1 0=GDT 1=LDT

具体例を書き起こしてみます。

GDTを指す場合:sel = index * 8

index sel(dec) sel(binary)
0 0 0b00000
1 8 0b01000
2 16 0b10000
3 24 0b11000
4 32 0b20000

LDTを指す場合:sel = index * 8 + 4

index sel(dec) sel(binary)
0 4 0b00100
1 12 0b01100
2 20 0b10100
3 28 0b11100
4 36 0b20100
  • 8を掛けることで下位3bitが全て0になる。
  • 8を掛けた後に4を足すことでtable indicatorの位置が1になる
    • 下位2bitは0のまま

表に起こすことで上記の3点の理解が深まると思います。

RPLの役目について

RPLは0が最強で3が最弱です。アプリケーションは通常3,OSは0のレベルで動作します。 この権限の設定はGDTにもあり、GDTのアクセス権の6,7bit目に当たります。(day6で解説しています)RPLの値がGDTの権限属性に満たない場合は一般保護例外が発生します。

アプリケーションからOSのセグメントに不正にアクセスすることはできませんが、 代理でOSに処理をさせる様な挙動、例えばコールゲート経由でOSに不正なアドレスを渡すと、OSの権限で動いてしまうので不正なアドレス操作をする恐れがあります。

RPLはこれを防ぐためにあるようです。

はじめての486の10章セキュリティによると、 RPLの設定はハードウェアが自動で行うのではなく、OSが責任をもつようです。

ところで、要求者特権レベルtのチェックは486が行うのですが、設定の作業はオペレーティングシステムが行わなければなりません。

ちなみに現時点でhariboteOSにはこのRPLの設定機構はありません

LDTは現在使われていない?

LDTは、ハードウェアに夜タスク切り替え機構のために提供されたが、OSメーカーが採用しなかった。現在のプログラムは仮想メモリによって隔離されており、LDTは使われていない。

低レベルプログラミングP52より

30日OS自作本は結構古い本なので、こういう最近の低レイヤー情報はありがたい。

低レベルプログラミング

低レベルプログラミング

参考リンク

私が知りたいことが全て書いてあった。 http://softwaretechnique.jp/OS_Development/kernel_loader2.html

毎度お世話になってる486本

感想

LDTは新要素なので486本を読み直したりググりまくって時間をかけたがあまり難しい概念ではない様に思います。ただ最近使われていないというのはちょと驚き。

OS自作本,486本をそろそろ読み終えるが、次に読みたい本の候補が決まってきた

  • Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識
  • はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ
  • 低レベルプログラミング
  • 詳解Linuxカーネル

うーん1冊消化するたびに積ん読が何冊も増えるような?w 本棚が爆発するんじゃないでしょうか。

Effective Javaも読みたいし、Deep Learningもかじりたい(超欲張り)。 秋に情報安全支援士受けるんでネットワークやセキュリティの総復習もしたい。

  • Effective java 3rd
  • ゼロから作るDeep Learning(続編もきましたね!)
  • コンピューターネットワーク
  • ソースコードで体感するネットワークの仕組み
  • 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版
  • ルーター自作でわかるパケットの流れ

うーむ時間は有限なので効率的に生きたいですね。 平日の時間投入はこれ以上は難しそうなので休日頑張るしかないな。

30日OS自作本24日以降のXボタンの影がおかしい

OS自作本24日目以降、Xボタンの色がおかしいことに気がつきました。

f:id:yuyubu:20180624004128p:plain
ボタンの影の部分が濃い灰色ではなく濃い青色になっている

f:id:yuyubu:20180626182115p:plain
参考:23日目終了時点でのXボタン
24日目(harib21e)に追加したウインドウの非アクティブ・アクティブを切り替えるchange_wtitle8ですが

  • 非アクティブ時のウインドウのバーの濃い灰色(COL8_848484)をアクティブ時に濃い青色(COL8_000084)に上書き
  • 非アクティブ時のウインドウとXボタンの影が同じ色(濃い灰色=COL8_848484)
  • この処理は色でしか判断しておらずXボタンの影が青く塗られてしまう。

という問題がありXボタンの影が24日目以降変です。

f:id:yuyubu:20180624003957p:plain

void change_wtitle8(struct SHEET *sht, char act)
{
    int x, y, xsize = sht->bxsize;
    char c, tc_new, tbc_new, tc_old, tbc_old, *buf = sht->buf;
    if (act != 0) {
        tc_new  = COL8_FFFFFF;
        tbc_new = COL8_000084;
        tc_old  = COL8_C6C6C6;
        tbc_old = COL8_848484;
    } else {
        tc_new  = COL8_C6C6C6;
        tbc_new = COL8_848484;
        tc_old  = COL8_FFFFFF;
        tbc_old = COL8_000084;
    }
    for (y = 3; y <= 20; y++) {
        for (x = 3; x <= xsize - 4; x++) {
            c = buf[y * xsize + x];
            if (c == tc_old && x <= xsize - 22) {
                c = tc_new;
            } else if (c == tbc_old) {
                c = tbc_new;
            }
            buf[y * xsize + x] = c;
        }
    }
    sheet_refresh(sht, 3, 3, xsize, 21);
    return;
}

解決策をいくつか考えて見ました。

  • 1.SHEET構造体にtitleという属性を持たせてchange_wtitle8の代わりにtilte引数付きでmake_window8を呼ぶ
  • 2.232色の中からxボタンの影として濃い灰色COL8_000084と似た色を選ぶ
  • 3.233色目に濃い灰色をCOL8_000084と同じ色を登録してxボタンの影にし、else if(c == tbc_old)この文の処理対象とさせない

下に行くほど付け焼き刃対応になります。工数がかかりますが、本来は一番上の選択肢を取るべきでしょう。 メモリ破壊とかそういう危険なバグではないので、とりあえず一番下で直して見ました。

  • graphic.cのinit_palette()の末尾に追加
static unsigned char table_2[3] = {
    0x84, 0x84, 0x84 /* 15:暗い灰色 */
};
set_palette(233, 233, table_2);
  • window.cのmake_wtitle8()の一部を変更
if (c == '@') {
    c = COL8_000000;
} else if (c == '$') {
    //c = COL8_848484;//←消す
    c = 233;//←入れる
} else if (c == 'Q') {

結果

www.youtube.com

hariboteOSをベースに自作OSを作成されている方は一度見直してみることをお勧めします。

30日OS自作本26日目

26日目の内容になります。

GUI描画の最適化とコンソールの昨日の強化、コマンドを3つ作ります。

www.youtube.com

GUI描画の最適化

harib23a(透明色の判定)

透明色を判定しているif分が非効率なのでこれを改善します

for(/*シート分*/){
    for(/*y幅分*/){
        for(/*x座標分*/){
            if(){/*透明色用の処理*/} //←これ効率悪い!!!!!
        }

    }
}

透明色がないシートの場合でもy幅分 * x幅分 if 文をとってしまうのが無駄です。 harib23aでは透明色ありのシートとなしのシートでfor文を変えてしまうことで高速化を期待します。

for(/*シート分*/){
    if(/*透明色なしのシート*/){//この条件を追加!!!
        for(/*y幅分*/){
            for(/*x座標分*/){
                //透明色判定を省略したVRAM書き込み
            }
        }
    }else{
        for(/*y幅分*/){
            for(/*x座標分*/){
                if(){/*透明色用の処理*/}
            }
        }
    }
}

全てのシートが透明色をもつシートの場合は、通る余分なif文が一つ増えるので速度が落ちてしまいますが、 マウスぐらいしかないので 

harib23b(一度の書き込みを1Bから4Bへ)

VRAMへの書き込みは*char型のポインタを利用するので1Bずつですが、

//unsigned char *map;
map[vy * ctl -> xsize + vx] = sid;

こちらをint型(4B)にします。

p = (int *) &map[vy * ctl->xsize + vx];

//注:ループ上限のbx1はbxの役1/4になる様に設定されている
for (bx = 0; bx < bx1; bx++) {
    p[bx] = sid4;
}

これでループ回数を役1/4に減らすことができます。 かなり早くなりそうですが。。。

harib23c(sheet_refreshsubも4B書き込み対応に)

画面全体ではなく指定したシートのみを更新するsheet_refreshsub()も4B書き込みに対応させます。

harib23d

FIFOが空っぽになった時に描画するという変更を入れます。 こちらの変更で、手元でマウスを止めたがカーソルが移動し続けているという事象を回避できる様ですが、いまいち違いがわかりませんでした。

ウインドウ機能の強化

  • コマンドの追加
    • start:新規ウインドウを開く
    • exit:ウインドウを閉じる
    • ncst:マルチタスクでアプリを起動させる

ncstはbashで言う所のコマンド末尾に&をつける様な動作です。

感想

今回はちょっとしたリファクタリング中心であまり新しい技術要素が出て来なかった様に思います。特にスピードアップに興味がない方は描画の改善は飛ばしてもいい気がします。 次回はLDTを使います。これも「はじめて読む486」に書いてあって気になっていた内容なので楽しみです。

30日OS自作本25日目

25日目の内容になります。

この章でコンソールを増やします。

Beep

12日目にBeepを試そうとしていましたが、なんと本章に動くプログラムが書かれていました。 QEMUだと鳴らないようです(笑)

色数増加 16 → 232

graphic.cに16色のRGBと色番号の対応付けがしてある配列がハードコードされていますが、これを増やします。

static unsigned char table_rgb[16 * 3] = {
    0x00, 0x00, 0x00,    /*  0:黒 */
    0xff, 0x00, 0x00,    /*  1:明るい赤 */
    0x00, 0xff, 0x00,    /*  2:明るい緑 */
    ・・・(省略)・・・
    0x84, 0x84, 0x84 /* 15:暗い灰色 */
};

今回追加する色の配列を作成しているプログラムの一部を抜き出して色番号とRGBの対応を画面に出して見ました。

#include <stdio.h>
int main(void){
    unsigned char table2[216 * 3];
    int r, g, b;
    int i=0;
    for (b = 0; b < 6; b++) {
        for (g = 0; g < 6; g++) {
            for (r = 0; r < 6; r++) {
                table2[(r + g * 6 + b * 36) * 3 + 0] = r * 51;
                table2[(r + g * 6 + b * 36) * 3 + 1] = g * 51;
                table2[(r + g * 6 + b * 36) * 3 + 2] = b * 51;

                printf("%d\tr=%d\tg=%d\tb=%d\t:\t%d\t%d\t%d\n",i,r,g,b,r * 51,g * 51,b * 51);
                i++;

            }
        }
    }
}

結果

0   r=0 g=0 b=0 :   0   0   0
1   r=1 g=0 b=0 :   51  0   0
2   r=2 g=0 b=0 :   102 0   0
・・・(中略)・・・
121 r=1 g=2 b=3 :   51  102 153
・・・(中略)・・・
215 r=5 g=5 b=5 :   255 255 255

これをビデオDAコンバータのパレットに追加しています。 復習ですが色は特定のレジスタではなく、ハードウェア(ビデオDAコンバータ)に持っているんですね。 4日目に色の登録に使ったコードを再度利用します。

void set_palette(int start, int end, unsigned char *rgb)
{
    int i, eflags;
    eflags = io_load_eflags();  /* 割り込み許可フラグの値を記録する */
    io_cli();                   /* 許可フラグを0にして割り込み禁止にする */
    io_out8(0x03c8, start);
    for (i = start; i <= end; i++) {
        io_out8(0x03c9, rgb[0] / 4);
        io_out8(0x03c9, rgb[1] / 4);
        io_out8(0x03c9, rgb[2] / 4);
        rgb += 3;
    }
    io_store_eflags(eflags);    /* 割り込み許可フラグを元に戻す */
    return;
}
  • 0x3c8:パレット番号
  • 0x3c9:RGB(色のデータ)

ここにどかっと作った216色分のデータが入った配列を突っ込みます。

set_palette(0, 15, table_rgb); //今まで使っていた色のやつ

・・・(中略(table2を作る処理))・・・

set_palette(16, 231, table2);  //今回追加する配列分

f:id:yuyubu:20180623134643p:plain

36色しか出していないようでしたので、自分で改造して全色(232色)出して見ました。

void HariMain(void)
{
    char *buf;
    int win, x, y, r, g, b;
    api_initmalloc();

    int box_size  = 8;  //色のボックのサイズ(px)
    int box_amt_x = 32; //色のボックスを水平に何個置くか
    int box_amt_y = 8;  //色のボックスを縦に何個置くか


    int content_x = box_size * box_amt_x;
    int content_y = box_size * box_amt_y;


    int win_bar_height = 20;                  //バーの高さ
    int padding = 8;                          //コンテンツの余白
    int bar_space = win_bar_height + padding; //コンテンツの描画が始まる高さ


    int win_x_siz = 256 + padding *2;
    int win_y_siz = content_y + win_bar_height + padding * 2;

    buf = api_malloc(win_x_siz * win_y_siz);
    win = api_openwin(buf, win_x_siz, win_y_siz, -1, "color");

    for(y = 0; y < content_y; y++){
        for(x = 0; x < content_x; x++){
            buf[(x + padding) + (y + bar_space) * win_x_siz ] = (x / box_size) +(y / box_size) * box_amt_x;
        }
    }

    api_refreshwin(win, padding,bar_space ,
        content_x+padding,content_y+bar_space);
    api_getkey(1);
    api_end();
}

32列 * 8行=256色で出して見ました。 当然232色しか登録してないので、登録していない残りの24色はありません。

f:id:yuyubu:20180623134708p:plain

何も設定せず表示したところ、233色以降、変な色?が出たのでgraphic.cを改造して赤色で埋めて見ました。

f:id:yuyubu:20180623134721p:plain

一行目左端から16個分は昔に登録した色が出ています。

コンソールを増やす(2個にする)

コンソールを増やすというより、まず2個作ってみて問題のある箇所を探す、という感じでインクリメンタルに改修していきます。

diffツール

sdiffというコマンドでかなりわかりやすくソースの差分が見れます。 ちょっと色塗ってくれればwinmergeみたいに使えますね。

sdiffは-wオプションで表示幅を変更できるようですが、 ネストが深すぎて私のノートPCだと最高解像度でも収まりませんでした。

$ sdiff -l -w 240 -W ../harib22d/bootpack.c bootpack.c |nkf
  • 使ってるオプション
    • l:変更のない行は左ファイルのみ表示
    • -w <数字>:一行あたりの文字数
    • -W :空白の変更を無視

-lは変更有無がちょっと見にくいので抜いたほうがいいかもしれません。

他にもdiffツールを探して見たところvimdiffというものがあるようです(vimが使えるならこれが最強?)

再起動現象再現せず(harib22i)

harib22iの再起動現象が再現しませんでした。一応証拠動画(エビデンス(笑))みたいなの撮って見た。

www.youtube.com

感想

この章はさらなるGUI機能強化のためのリファクタリング + バグ回収という趣旨が強いと思います。 最初から綺麗に設計していないこともあり、ソースを弄るたびになんらかのバグが発生しますが、 あまり手がかりのない状態で的確にバグの原因を見つけて行っている印象があります。

うまくいかなくなった原因は書き足したり削除した部分のどこかにあると考えて、元に戻して見たりして探すのである。

だそうですがprint文デバッグであったり,Eclipseのデバッガのようなものがないと私には難しいです。 一般保護例外の表示を拡張しましたが、そもそもそれが出ずにOS自体がシャットダウンしてしまうこともよく起こります。

30日OS自作本24日目

24日目の内容になります。

ウインドウ操作

  • タイマー
  • ウインドウのドラッグ移動
  • Xボタンの有効化
  • アクティブなウインドウの切り替え

この辺は動画で見た方が面白いと思います。

www.youtube.com

ホストOS側からQEMUにF11キーが送れない

F11キーがQEMU側で反応するより前に、mac側のディスクトップを表示する機能が発動してしまうのでとりあえず無効化しておきました。 Mac使っている人は参考にして見てください。

タイマー終了時に発生する謎の文字

タイマーを利用しているアプリケーションを強制終了した際に、タイマーの通知がFIFOを通じてコンソールのキー入力の箇所に届いてしまう問題がありました。

f:id:yuyubu:20180622190935p:plain

このデータは何由来のものか、本書では解説されていなかったので調べて見ました。

hankaku.txtに登録されている

char 0x80
........
..***...
.*...*..
*.....*.
*.......
*.......
*.......
*.......
*.......
*.......
*.......
*.....*.
.*...*..
..***...
...*....
..*.....

これですね。(0x80=128)asciiは7bit符号化?らしいので127(0b1111111)までしか定義されていないらしいが、一部の拡張文字セット?(extend-ascii-code)というもので128にこれが登録されているものがあった。ちなみにこれはセディーユという記号らしい。

GUI雑感

この章でMain関数が非常に複雑になってしまいます。こういうのを見るとGUIモデリングの格好の対象だなと思います。

学生の頃はワークフローとか在庫管理とか面白くなさそう、と思っていましたが、 オブジェクト指向の題材としてそういうものが取り上げられていることが多く、熱心に勉強していませんでした。

が、社会人になってから気付きましたが、複雑な業務ルールをクラス化・テーブル化したりするのは意外と楽しいものです。

OSをオブジェクト指向で作ろうとするとドメイン(=システム化の対象領域)も技術的なものなので技術がやりたい人(ビジネス的な部分を考えたくない人)にとってはオブジェクト指向を学ぶ題材としてすごく向いてると思います。

30日OS自作本23日目

23日目の内容です。

アプリケーションのメモリ管理の導入とGUI周りのAPIの強化をやります。

f:id:yuyubu:20180621215939p:plain

アプリケーションもメモリ管理(malloc)をする

気になったことがあります。winhelo2.hrbが7.6KBもあることです。 (略)なんでかなーと思ってwinhelo2.hrbをバイナリエディタでのぞいてみたら00がいっぱいです。 (略)ソースに戻って調べてみたところ、原因はwinhelo2.cのchar buf[150*50]でした。 (略)アセンブラでいうとRESB 7500ってとこです。

とのことですので調べてみました。

49                                          [SECTION .data]
50 00000013 00 00 00 00 00 00 00 00 00 00       ALIGNB  16
     0000001D 00 00 00
51 00000020                                 _buf:
52 00000020 00 00 00 00 00 00 00 00 00 00       RESB    7500
     0000002A 00 00 00 00 00 00 00 00 00 00
...ずっと0なので略...
     00001D62 00 00 00 00 00 00 00 00 00 00

メモリ管理をしないとこんな風にデータセクションに値が確保されて7500KBも増えるということですね。 アプリケーションから利用できるメモリ確保APIを使って実行ファイルの容量を減らしています。

char *buf;
int win;

api_initmalloc();
buf = api_malloc(150 * 50); //新規

約7500KB程度減っています。

$ ls -alt winhelo*.hrb
-rw-r--r--  1   staff   387  6 20 13:26 winhelo3.hrb
-rw-r--r--  1   staff  7888  6 20 13:26 winhelo2.hrb

一般的に配列を宣言すれば、そのサイズ分実行ファイルに含まれるということでしょうか。

画像系のAPI

ウインドウに点を打つ
EDX=11
EBX=ウインドウの番号
ESI=表示位置のx座標
EDI=表示いちのy座標
EAX=色番号

f:id:yuyubu:20180621215005p:plain

画面の任意の座標にドットを書くAPIです。VRAMの番地を直接指定するのではなく、シート(ウインドウ)上の1ドットを変えています。

星座

この乱数は結局は計算で作っているものであって、PCの中にサイコロがあるわけではないので、もう一度アプリを起動しても結果はいつも同じになります。

rand()の有名な特性ですね。大学の頃のアルゴリズムの授業で、ソートプログラム用のデータを作ったら幾つ作っても同じデータになるんだけど???みたいな学生が大量に発生した思い出があります。

さて今回もシードを設定していないので毎度同じ位置にドットがきてしまいます。書籍中の画像と全く同じ配置になっていますね。

f:id:yuyubu:20180621215546p:plain

もう一度アプリを起動しても、結果はいつも同じになります。だから、筆者が「make run」して眺めた夜空と全く同じものを、読者の皆さんも眺めることになります。

srand((unsigned int) time(null));こんな感じで時間を与えて初期化できるのですがtime()の入っているtime.hがinclude出来ない。

../../z_tools/gocc1 -I../../z_tools/haribote/ -Os -Wall -quiet -o stars.gas stars.c
stars.c:2:18: time.h: No such file or directory

多分ztools配下のhariboteに追加すればいいんだろうけど。

$ls ../../z_tools/haribote/
errno.h     golibc.lib  harilibc.lib    math.h      stdarg.h    stdio.h     string.h
float.h     haribote.rul    limits.h    setjmp.h    stddef.h    stdlib.h

とりあえずsrand(20)を実行して著者と違う並びで星座を表示してみました。

f:id:yuyubu:20180621215739p:plain

GUI

  • 線を引くプログラム
  • RPG風の画面キー入力で動く*のウインドウ
  • 強制終了じに画面が消えない事象の修正

などを行いました。 この辺はAPI追加したりドット表示の延長上の話なのであまりOSが関係ないので流し読み。

感想

  • 例のアルゴリズムの授業ですが、最終テストが筆記だったのでsrand(time(null))は暗記して臨んだ思い出があります。
  • Xボタンを押してウインドウを消したい、みたいな流れにならないのがちょっとモヤモヤする。