プログラマのための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
- sql securityの値
- を適切に設定することで迂回できるようです。
- ビューだけではなく規定テーブルへのアクセス権限の設定も必要。
oracleにはscottというデモ用のアカウントがいる
- なんだこの裏技みたいなアカウント!?(知りませんでした)
- https://ja.wikipedia.org/wiki/Oracle_Database#.E3.80.8CSCOTT.2FTIGER.E3.80.8D.E3.81.AE.E7.94.B1.E6.9D.A5
Oracle Databaseに付属するdemobld.sql(Oracle Database 10g以降ではutlsampl.sql)を実行すると「EMP」「DEPT」というふたつのテーブルと「SCOTT/TIGER」というスキーマよりなる伝統的なデモ環境が構築される。「SCOTT」とはオラクルの前身であるSDLに在籍していたBruce Scottを指し、「Tiger」は彼の愛猫の名前に由来する。Scottは優秀な開発者であり最初期のSQL*Plusも彼の手によるものとされている。Scottはすでにオラクルを後にしているが、この伝統は変わる様子がない。
権限は最強っぽいが詳細は不明。
mysqlではデフォルトで権限が最強の無名ユーザーがいる
- ユーザー名:なし,パスワードなし
- http://lovee7.blog.fc2.com/blog-entry-25.html
- https://cloudpack.media/1316
- それっぽいやつがいる
- 自分の環境にはいませんでした(消したかも?)
1対他の結合を行うクエリやスキーマをスター型のクエリ,スタースキーマというらしい。
- ER図がスター型になるので
mysqlのmerge table
- https://dev.mysql.com/doc/refman/5.6/ja/merge-table-advantages.html
- viewが導入される前に代替案で使われていた。
- もう古いので使わないこと。
ビューの循環参照(実験してみる)
- 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がある!
- MySQLにはない。
誤植
- P.466のSQL,'Sales'のSが小文字sになっている
▼P.466 SELECT * FROM Departments WHERE (dept_name = 'sales') --ココ!'sales'ではなく'Sales'!! AND (city_name = 'New York');
- 多分原著から間違っているのでは?(未確認ですが)とのこと。
次は26.7から!
- 作者: ジョー・セルコ,Joe Celko,ミック
- 出版社/メーカー: 翔泳社
- 発売日: 2013/05/24
- メディア: 大型本
- この商品を含むブログ (16件) を見る
30日OS自作本27日目
27日目の記事になります。というよりほとんどLDTの解説になります。
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自作本は結構古い本なので、こういう最近の低レイヤー情報はありがたい。
- 作者: Igor Zhirkov,吉川邦夫
- 出版社/メーカー: 翔泳社
- 発売日: 2018/01/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (3件) を見る
参考リンク
私が知りたいことが全て書いてあった。 http://softwaretechnique.jp/OS_Development/kernel_loader2.html
毎度お世話になってる486本
32ビットコンピュータをやさしく語る はじめて読む486 (アスキー書籍)
- 作者: 蒲地輝尚
- 出版社/メーカー: 角川アスキー総合研究所
- 発売日: 2014/10/21
- メディア: Kindle版
- この商品を含むブログを見る
感想
LDTは新要素なので486本を読み直したりググりまくって時間をかけたがあまり難しい概念ではない様に思います。ただ最近使われていないというのはちょと驚き。
OS自作本,486本をそろそろ読み終えるが、次に読みたい本の候補が決まってきた
うーん1冊消化するたびに積ん読が何冊も増えるような?w 本棚が爆発するんじゃないでしょうか。
Effective Javaも読みたいし、Deep Learningもかじりたい(超欲張り)。 秋に情報安全支援士受けるんでネットワークやセキュリティの総復習もしたい。
- Effective java 3rd
- ゼロから作るDeep Learning(続編もきましたね!)
- コンピューターネットワーク
- ソースコードで体感するネットワークの仕組み
- 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版
- ルーター自作でわかるパケットの流れ
うーむ時間は有限なので効率的に生きたいですね。 平日の時間投入はこれ以上は難しそうなので休日頑張るしかないな。
30日OS自作本24日以降のXボタンの影がおかしい
OS自作本24日目以降、Xボタンの色がおかしいことに気がつきました。
24日目(harib21e)に追加したウインドウの非アクティブ・アクティブを切り替えるchange_wtitle8
ですが
- 非アクティブ時のウインドウのバーの濃い灰色(
COL8_848484
)をアクティブ時に濃い青色(COL8_000084
)に上書き - 非アクティブ時のウインドウとXボタンの影が同じ色(濃い灰色=
COL8_848484
) - この処理は色でしか判断しておらずXボタンの影が青く塗られてしまう。
という問題がありXボタンの影が24日目以降変です。
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') {
結果
hariboteOSをベースに自作OSを作成されている方は一度見直してみることをお勧めします。
30日OS自作本26日目
26日目の内容になります。
GUI描画の最適化とコンソールの昨日の強化、コマンドを3つ作ります。
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); //今回追加する配列分
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色はありません。
何も設定せず表示したところ、233色以降、変な色?が出たのでgraphic.cを改造して赤色で埋めて見ました。
一行目左端から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の再起動現象が再現しませんでした。一応証拠動画(エビデンス(笑))みたいなの撮って見た。
感想
この章はさらなるGUI機能強化のためのリファクタリング + バグ回収という趣旨が強いと思います。 最初から綺麗に設計していないこともあり、ソースを弄るたびになんらかのバグが発生しますが、 あまり手がかりのない状態で的確にバグの原因を見つけて行っている印象があります。
うまくいかなくなった原因は書き足したり削除した部分のどこかにあると考えて、元に戻して見たりして探すのである。
だそうですがprint文デバッグであったり,Eclipseのデバッガのようなものがないと私には難しいです。 一般保護例外の表示を拡張しましたが、そもそもそれが出ずにOS自体がシャットダウンしてしまうこともよく起こります。
30日OS自作本24日目
24日目の内容になります。
ウインドウ操作
- タイマー
- ウインドウのドラッグ移動
- Xボタンの有効化
- アクティブなウインドウの切り替え
この辺は動画で見た方が面白いと思います。
ホストOS側からQEMUにF11キーが送れない
F11キーがQEMU側で反応するより前に、mac側のディスクトップを表示する機能が発動してしまうのでとりあえず無効化しておきました。 Mac使っている人は参考にして見てください。
タイマー終了時に発生する謎の文字
タイマーを利用しているアプリケーションを強制終了した際に、タイマーの通知がFIFOを通じてコンソールのキー入力の箇所に届いてしまう問題がありました。
このデータは何由来のものか、本書では解説されていなかったので調べて見ました。
hankaku.txtに登録されている
char 0x80 ........ ..***... .*...*.. *.....*. *....... *....... *....... *....... *....... *....... *....... *.....*. .*...*.. ..***... ...*.... ..*.....
これですね。(0x80=128)asciiは7bit符号化?らしいので127(0b1111111)までしか定義されていないらしいが、一部の拡張文字セット?(extend-ascii-code)というもので128にこれが登録されているものがあった。ちなみにこれはセディーユという記号らしい。
GUI雑感
この章でMain関数が非常に複雑になってしまいます。こういうのを見るとGUIはモデリングの格好の対象だなと思います。
学生の頃はワークフローとか在庫管理とか面白くなさそう、と思っていましたが、 オブジェクト指向の題材としてそういうものが取り上げられていることが多く、熱心に勉強していませんでした。
が、社会人になってから気付きましたが、複雑な業務ルールをクラス化・テーブル化したりするのは意外と楽しいものです。
OSをオブジェクト指向で作ろうとするとドメイン(=システム化の対象領域)も技術的なものなので技術がやりたい人(ビジネス的な部分を考えたくない人)にとってはオブジェクト指向を学ぶ題材としてすごく向いてると思います。
30日OS自作本23日目
23日目の内容です。
アプリケーションのメモリ管理の導入とGUI周りのAPIの強化をやります。
アプリケーションもメモリ管理(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=色番号
画面の任意の座標にドットを書くAPIです。VRAMの番地を直接指定するのではなく、シート(ウインドウ)上の1ドットを変えています。
星座
この乱数は結局は計算で作っているものであって、PCの中にサイコロがあるわけではないので、もう一度アプリを起動しても結果はいつも同じになります。
rand()
の有名な特性ですね。大学の頃のアルゴリズムの授業で、ソートプログラム用のデータを作ったら幾つ作っても同じデータになるんだけど???みたいな学生が大量に発生した思い出があります。
さて今回もシードを設定していないので毎度同じ位置にドットがきてしまいます。書籍中の画像と全く同じ配置になっていますね。
もう一度アプリを起動しても、結果はいつも同じになります。だから、筆者が「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)を実行して著者と違う並びで星座を表示してみました。
他GUI
- 線を引くプログラム
- RPG風の画面キー入力で動く
*
のウインドウ - 強制終了じに画面が消えない事象の修正
などを行いました。 この辺はAPI追加したりドット表示の延長上の話なのであまりOSが関係ないので流し読み。
感想
- 例のアルゴリズムの授業ですが、最終テストが筆記だったので
srand(time(null))
は暗記して臨んだ思い出があります。 - Xボタンを押してウインドウを消したい、みたいな流れにならないのがちょっとモヤモヤする。