技術日誌

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

Java読書会BOF「Kotlinイン・アクション」を読む会 第6回に参加

Kotlin in Action 第6回

ジェネリクスの後半(9章)とリフレクションの章(10章)とDSL(11章)を読みました。 10章は技術的にもちょっと面白い読み物になっていて、オープンソースjsonリアライザ/デシリアライザであるjKid*1の解説をしながらリフレクションを学ぶ章になっています。

ジェネリクスにつけるout,in

  • outをつけると共変
  • inをつけると反変
  • それ以外は不変(javaと同じ)

p322の表がおかしい

共変 反変 不変
Class<out T> Class<in T> MutableList<T>
クラスのサブタイピングは維持 サブタイピングは逆 サブタイピングなし
Tはoutポジションだけ Tはinポジションだけ Tはどこでも

inポジションだけにすべき箇所の文言がoutポジションになってしまっている。

::の意味

クラス参照の意味。p344に軽く説明。(なぜここ?)

  • String::ClassはJavaのString.classと同じ意味。
  • p146メンバ参照で解説されているものと同じ

久しぶりに読むと忘れちゃいますね。

関数インターフェイス?

p332ページのまとめ

・関数インターフェイスは、1つめの方パラメータについて反変、2つ目の方パラメータについて久代編として宣言されています。

とありますが、「関数インターフェイス」と書いてしまうと一般的な関数の意味になってしまうのでは?という違和感を示す方がいました。322ページにあるFunctionインターフェイスのことなので、Functionインターフェース、もしくはFunctionNインターフェースにすれば違和感がない気がする。

Deprecatedで推奨する関数を通知できる。

p335の内容です。

@Deprecated("Use removeAt(index_ instead.",ReplaceWith("removeAt(index)"))
fun remove(index int){...}

こんな感じで非推奨になったAPIに対して代替を示すことができます。この機能はJavaにはなかったため(多分)大変便利だと思います。

inteliJ IDEAではAlt+ ENterはかなり使う

p338

IntelliJ IDEAでは、コンパイラの警告状で[ALT] + [Enter]を押して表示されるオプションメニューから[Suppress]を選ぶことで、このアノテーションを挿入できます。

とありますが、コンパイラ抑制系のアノテーションの挿入の推奨をかなりしてくるとのことです。

これですね。

Java EE 8のJSONのライブラリが

などがかなり良いらしい

jackson,GSONの作者が言ってたらしい。

340文章がおかしい。

最後の塊の二行目。

data class Person(
  @jsonName("alias") val firstName: String,
  @JsonExclude val age: Int? = null  
  )

プロパティfirestNameを示すJSON内でのキーを変更するために、アノテーションをつけています。これは、プロパティageをシリアライゼーションやでシリアライゼーションから除外するためです。

「これは、プロパティageを~」の直前に@jsonExcludeの説明がないと変。何かコピペのし忘れ?のように思える。

p350 KFunctionNはどこに生成される?

KFunctionNはKFunction1,KFunction2,KFunction3...と無限に存在しているのか?と思ったがこれはコンパイラによって生成されるらしい。これがどこに生成されるのか?という話題になった。(宿題)

p377誤植

2こ目のコードブロック。

閉じのダブルクォート"が抜けている。

感想

この本の9章、10章は非常に難しいです。少し理解できない点があるので復習中です。 9章は4時間ほどかけてコードを動かしながら精読しました。 明日以降10章も復習したいと思います。

次回は11.2から!

おそらく午前中で終了する程度の分量です。午後は何かレクをするようです(卓球?)。

ちなみに高橋さんは利き手じゃなくても私に勝てるそうです。

Kotlinイン・アクション

Kotlinイン・アクション

ブルーボトルコーヒーの豆を買った

通勤経路にあるので覗いてみたところお土産コーナーだけ人がいなかったので買いました。行列並んでまで買うと負けな気がするので今まで飲む機会がありませんでした。

 

1500円/200gくらいしました。普段買ってる豆が1200円くらいなので少し高いです。挽き売りはやってないらしいです。コーヒーミルを自宅に持っていない人は多いと思うので挽き売りも普通にやって欲しい。

 

f:id:yuyubu:20180616202448j:image

 

パッケージ裏に焙煎日が書いているけど、なんと焙煎日がなんと1日前(6/6購入)。これは結構すごい。

 

f:id:yuyubu:20180616202709j:image

 

豆はこんな感じ。貝殻豆やちゃんと焼けてなさそうな色の豆が結構入ってたりして値段の割にはあまりいい印象はありません

 

f:id:yuyubu:20180616203352j:imagef:id:yuyubu:20180616203409j:image

 

適当に粗挽きにしてドリップして頂きました。焼きたてなだけあってブクブク膨らみますね。

 

f:id:yuyubu:20180616203452j:image

 

味はアフリカの豆を沢山混ぜたような味がしました。ブレンド名がスリーアフリカンだから当たり前か。

 

特にオチはありません。

30日OS自作本21日目

21日目の内容です。セグメントやメモリの復習が多めです。

hariboteOSのメモリマップ

メモリに直接データを書き込むなどの危険行為を行なっているので、念のためどこらへんを潰しているのか分かり易くするために復習です。p171から引用したデータを元に作成しました。

start end size content
0x00000000 0x000fffff 1MB boot
0x00100000 0x00267fff 1440KB floppy content
0x00268000 0x0024f7ff 30KB empty
0x0026f800 0x0026ffff 2KB IDT
0x00270000 0x0027ffff 64KB GDT
0x00280000 0x002fffff 512KB bootpack.hrb
0x00300000 0x003fffff 1MB stack or etc
0x00400000 - - empty

合計4048KB+です。

$ echo $((1000 + 1440 + 30 + 2 + 64 + 512 + 1000 ))
4048

本エントリ中に生のアドレスが出てきた際は参考にしてください。 

セグメントについておさらい。

  • プロテクトモード時はセグメントは固定長ではない
    • リアルモード時はセグメントは固定長である。
  • セグメントの設定はset_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)関数で行なっている*1

今まで登録したセグメントをまとめます。

セグメントはこんな感じで登録してきました。

  • set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, AR_DATA32_RW);
  • set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);
  • set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);
  • set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER);
  • set_segmdesc(gdt + 1004, 64 * 1024 - 1, (int) q, AR_DATA32_RW);

ざっくり表にまとめてみました。

position content base address limit ar
1 OS data 0x00000000 0xffffffff 0100000010010010
2 OS code 0x00280000 0x0007ffff 0100000010011010
3~1002 TSS alloc result 103B 0000000010001001
1003 app code alloc result file size 0100000010011010
1004 app data alloc result 64KB 0100000010010010
  • alloc resultというのはallocation系の関数の結果次第ということです。

範囲的にはbootpackで解放している

memman_free(memman, 0x00001000, 0x0009e000); /* 0x00001000 - 0x0009efff */
memman_free(memman, 0x00400000, memtotal - 0x00400000);
  • 0x00001000 - 0x0009efff
  • 0x00400000 - メモリの最後まで

この範囲のアドレスが使われます。

OS codeセグメントのベースアドレスが本エントリ冒頭に書かれているbootpack.hrbの開始地点0x00280000と一致していることが確認できます。

ちなみにTSSはこんな感じでセグメントの3~1002の範囲を割り当てています。

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

struct TASK *task_init(struct MEMMAN *memman)
{
  /* 中略 */
  for (i = 0; i < MAX_TASKS; i++) {
    taskctl->tasks0[i].flags = 0;
    taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;
    set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);
  }

セグメントのアクセス属性を設定する

arの下位8ビットはセグメント属性と呼ばれるものであり 下位6,7bit目のフラグを立てる(0x60をOR演算する)と、アプリ用のセグメントとなり、OS用のセグメントにデータが書けなくなります。

セグメント属性について改めて説明します。

content ar
0x60 01100000
app code 10011010
app data 10010010
label PDDSTTTA

一番下に説明のため追加したlabelの行がセグメント属性の各ビットの役割です。上位から順に

  • P:セグメントが存在する(1=存在,0=存在しない)
  • D:DPL(セグメントの特権レベル)
  • S:セグメントフラグ= セグメントである場合に1
  • T:TYPE(セグメントの種類)
  • A:セグメントがアクセスされた

PとSは説明しなくてもわかると思います。またAはメモリ管理用のフラグです。

ここで説明が必要なのがtypeとdplだと思います。

DPL

DPL(Descriptor Privilege Level)の2ビットで権限を表現します。

0~4の4種類をとり、小さいほど強い権限になります。

となり、今回はこの部分に0b11=4を登録しますのでアプリケーションのセグメントであることをマークしたことになります。

type:セグメントのread,write,executeなど

この章ではtypeはあまり関係ないけど一応説明。

type binary read write execute content
0 000 × × data segment
1 001 × data segment
2 010 × × stack segment
3 011 × stack segment
4 100 × × code segment
5 101 × code segment
6 110 × × conforming code segment
7 111 × conforming code segment
  • コンフォーミングコードセグメントはアプリケーションからOSの権限の命令(0)を直接呼ぶためのセグメントです。
  • コードセグメントはcsレジスタ、データセグメントはdsレジスタを使って参照します。
  • スタックセグメントはssレジスタを使います。

設定したアクセス属性により上がる例外をハンドルする

x86ではアクセス属性に違反した挙動があればINT0x0dの割り込みが発生する。この割り込みを例外という。 例外をサポーするにはこのINT0x0dに対応する割り込みハンドラを作成してIDTに登録すればOK。

本書によるとqemuのバグ?などがあるらしく例外割り込みなどはうまく再現できないとのこと。古い本なので最近のqemuでは実は治ってたりしてwと思いましたが、私が利用しているバージョンは2.11.0の段階でまだ治っていません。

$ qemu-system-i386 --version
QEMU emulator version 2.11.0
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers

crack2が破壊しているOSの部分。

crack2を実行するとdirコマンドの表示内容がなくなるとのことですが、 これがなぜ発生しているか本書に記載がなかったので調べてみました。

[INSTRSET "i486p"]
[BITS 32]
        MOV     EAX,1*8         ; OS用のセグメント番号
        MOV     DS,AX           ; これをDSにいれちゃう
        MOV     BYTE [0x102600],0
        MOV     EDX,4
        INT     0x40

冒頭にまとめた通り、セレクタ値1のセグメントはOSのデータ領域です。ここのベースアドレスはメモリ上の先頭(0x00000000)になります

position content base address limit ar
1 OS data 0x00000000 0xffffffff 0100000010010010

このセグメントをDSで指定した状態で,MOV BYTE [0x102600],0を実行しています。オフセットアドレス(0x102600)の位置に0を書き込むという意味です。今回はベースアドレスが0x00000000なのでオフセットもクソもありませんが。。。

そしてこの0x102600の位置を冒頭のメモリマップで確認しています。

start end size content
0x00000000 0x000fffff 1MB boot
0x00100000 0x00267fff 1440KB floppy content

どうやらフロッピーディスクの0x2600番地の位置のようです。 この位置のデータをバイナリエディタで確認すると次のようになっていました。

[:plane]

そしてこの位置は19日目に書いた通り、root directoryとなります。 crack2は

  • root directoryの先頭位置に0書き込みディレクトリの内容を消す、

という結果を引き起こすコードだったのです。

dirコマンドはファイル先頭位置のヌル文字(0x00)を読み取った時点で処理を中断していますので、これが理由でdirの出力結果がなくなった、ということになります。

void cmd_dir(struct CONSOLE *cons)
{
    struct FILEINFO *finfo = (struct FILEINFO *) (ADR_DISKIMG + 0x002600);
    int i, j;
    char s[30];
    for (i = 0; i < 224; i++) {
        if (finfo[i].name[0] == 0x00) {
            break;
        }
        /*dirの処理*/
        }
    }
    cons_newline(cons);
    return;
}

今回はそのような攻撃をセグメント属性と例外割り込みで守ろう、という趣旨の章でした。

はじめて読む486

参考にしています。 30日OS自作本がOSを30日で作るという目標のため省いている細かいCPUの機能についてかなり充実しています。あと図がかなりわかりやすい。

30日でできる! OS自作入門

30日でできる! OS自作入門

*1:arは権限です。acsess right

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

25.3.4の自己外部結合から25章最後まで読みました。

自己結合、自己外部結合などがテーマです。 SQLでパズル的なことをやろうとすると自己外部結合が役立ちそうです。 あとは障害調査くらいですかね。

正誤表と掲載SQL

今回読書会中に詰まることがあり必要になりました。どちらも翔泳社の公式サイトにあります。

www.shoeisha.co.jp

ただしSQLはプレーンテキストが置いてあるだけであり、またSQLを動かすためのinsert文がついていないものも多数あります。また正誤表で上がっているものの訂正もされていないようですので注意が必要です。

外部結合は順番が入れ替えられない

25.3.5の内容。外部結合は結合則が成り立たない

  • (A + B) + C
  • A + (B + C)

これが同じ結果にならない可能性がある。 どの順番で結合をするかで結果が変わってしまう。

サポートでも外部結合が入っているとお手上げらしい。

25.4のクエリの疑問

---生徒テーブル

CREATE TABLE Students
(student_nbr INTEGER NOT NULL PRIMARY KEY,
 student_type CHAR(1) NOT NULL DEFAULT 'D'
     CHECK (student_type IN ('D', 'F')));

---国内学生テーブル

CREATE TABLE DomesticStudents
(student_nbr INTEGER NOT NULL PRIMARY KEY
     REFERENCES Students(student_nbr));

---留学生テーブル

CREATE TABLE ForeignStudents
(student_nbr INTEGER NOT NULL PRIMARY KEY
     REFERENCES Students(student_nbr));

---データ投入

-- Students
INSERT INTO Students(student_nbr, student_type) VALUES('1', 'D');
INSERT INTO Students(student_nbr, student_type) VALUES('2', 'D');
INSERT INTO Students(student_nbr, student_type) VALUES('3', 'F');
INSERT INTO Students(student_nbr, student_type) VALUES('4', 'D');
INSERT INTO Students(student_nbr, student_type) VALUES('5', 'F');

-- DomesticStudents
INSERT INTO DomesticStudents(student_nbr) VALUES('1');
INSERT INTO DomesticStudents(student_nbr) VALUES('2');
INSERT INTO DomesticStudents(student_nbr) VALUES('4');


-- ForeignStudents
INSERT INTO ForeignStudents(student_nbr) VALUES('3');
INSERT INTO ForeignStudents(student_nbr) VALUES('5');




SELECT Students.*, DomesticStudents.*, ForeignStudents.*
  FROM Students
         LEFT OUTER JOIN
           DomesticStudents
    ON CASE Students.student_type
       WHEN 'D'
       THEN 1
       ELSE NULL END = 1
         LEFT OUTER JOIN
           ForeignStudents
             ON CASE Students.student_type
                WHEN 'F'
                THEN 1
                ELSE NULL END = 1
                --注意:正誤表を見ると以下が抜けていた。
                WHERE (   Students.student_nbr = DomesticStudents.student_nbr
                       OR Students.student_nbr = ForeignStudents.student_nbr);

結果

student_nbr | student_type | student_nbr | student_nbr
-------------+--------------+-------------+-------------
          1 | D            |           1 |            
          2 | D            |           2 |            
          3 | F            |             |           3
          4 | D            |           4 |            
          5 | F            |             |           5

なぜCASE式を使っているのか?という意見が出た。

  • Students.student_type = D
  • Students.student_type = F

の2つの結合で良いのではないかという意見が出た。

SELECT Students.*, DomesticStudents.*, ForeignStudents.*
  FROM Students
         LEFT OUTER JOIN
           DomesticStudents
    ON  Students.student_type = 'D'
         LEFT OUTER JOIN
           ForeignStudents
             ON  Students.student_type = 'F'
                WHERE (   Students.student_nbr = DomesticStudents.student_nbr
                       OR Students.student_nbr = ForeignStudents.student_nbr);

結果

student_nbr | student_type | student_nbr | student_nbr
-------------+--------------+-------------+-------------
          1 | D            |           1 |            
          2 | D            |           2 |            
          3 | F            |             |           3
          4 | D            |           4 |            
          5 | F            |             |           5

確かに結果は変わらないですね。

古いSQLの結合構文の有利な点

結合が宣言的にかけるというメリットがある。

-- JOINを使わない古い結合
SELECT x, y, z
  FROM Foo, Bar, Flub
 WHERE y BETWEEN x AND w;--結合条件が宣言的にかける

-- JOINを使う新しい結合
 SELECT x, y, z

   --Foo,BAR,Flubとインクリメンタルに結合する必要がる。
   --SQLで手続き的に書く必要が出てくる。
   FROM Foo INNER JOIN Bar
     ON y >= x
             INNER JOIN Flub
               ON y <= w;
  • シーター結合は普通はやらない。
  • 新しい結合句で問題ない。

新人研修や初学者がとくべき定番の問題

自己結合の章ではパズルのような問題を沢山ときます。そういえばSQLでは

などの他のプログラミング言語だと最初に解いて見る定番の演習問題が無いので 質問してみました。

スッキリわかる SQL 入門 ドリル215問付き! (スッキリシリーズ)

スッキリわかる SQL 入門 ドリル215問付き! (スッキリシリーズ)

改訂第3版 すらすらと手が動くようになる SQL書き方ドリル (WEB+DB PRESS plus)

改訂第3版 すらすらと手が動くようになる SQL書き方ドリル (WEB+DB PRESS plus)

複数冊ありますがどちらもおすすめとのこと。 後者はブラウザでSQLを試せる環境がついています。

  • キリン本。初学者が躓きやすいgroup byのサポートが充実。また入門書で集約関数まで扱っているものは少ない。
    • 私もSQLの学習はこの本からはじめました。*1

SQL 第2版 ゼロからはじめるデータベース操作 (プログラミング学習シリーズ)

SQL 第2版 ゼロからはじめるデータベース操作 (プログラミング学習シリーズ)

  • 会社がSQLのドリルを作ってる。
  • 林優子さんのSQLドリル(書籍? or 講習?)が良いらしい

どこかで話がそれてマニアックなデータベース本の話題になりました。

  • データベースリファクタリングという本がメチャクチャ良い。
    • 例のピアソンショックでプレミアがついてる
    • 積読している人は質屋に持っていくと金になるかも。。。

データベース・リファクタリング

データベース・リファクタリング

  • 作者: スコット W アンブラー,ピラモド・サダラージ,梅澤真史,越智典子,小黒直樹
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2008/03/26
  • メディア: 単行本
  • 購入: 10人 クリック: 211回
  • この商品を含むブログ (53件) を見る

  • 川崎の工業図書館?にSQL関連のレアな書籍が結構ある。

    横浜に県立図書館がすでに開館していることと、工業都市・川崎にあることから、開館当初から自然科学・工業・産業分野を重点とした資料収集・サービス方針を掲げている。

  • カーリル*2というサービスで全国の図書館を検索できる

  • 成熟している分野なので教材などは充実している感じですが、定番の問題みたいなのは特になさそうです。

人名

この本は質問サイト?のようなコミュニティの引用も多く、一般人の人名が多く登場します。 主催の木村さんは同名人物っぽい人をlinked inで見つけてメッセージを送ってみたそうですが、返事がなかったようです。

初版が古い本なので、昔から書かれている人とはもう連絡を取るのが難しいかもしれませんね。 最近の本だとtwitterIDを載せると思いますし、昔より連絡コストは大きく減りつつあると思います。

感想

パズル系のクエリは普段描かないような書方で描かれていることが多かったり、クエリそのものが長くなっている等で、SQLにコメントがついていないと理解が難しいです。本章は読書会で読むというより、家でじっくり試しながら読むほうがよさそうです。

30日OS自作本20日目

20日目は1文字を表示するAPIを作りました。これでOSに配備したアプリケーションからOSの機能を呼び出すことができます。

f:id:yuyubu:20180613053734p:plain

API

APIを作ります。プログラムからOSの機能を呼ぶ部分です。コマンドラインに書いたコマンドからOSの機能を呼び出します。

C言語で作ったput_charをアプリケーション(OSにコピーした実行ファイル)から呼びます

/**
 * コンソールに指定した文字を1文字書きます
 * @param cons 描画先のコンソール
 * @param chr  描画する文字コード
 * @param move 制御用フラグ 0:カーソル位置を進めない
 */
void cons_putchar(struct CONSOLE *cons, int chr, char move)
MOV      AL,'h'
CALL    2*8:0xbe8 ;
  • ALに文字を入れてセグメント2(OS領域)の0xbe8を呼ぶとconsoleにALに入れた文字がでる

そんなシンプルなAPIになります。

引数を扱う専用の処理を呼び出す

アセンブリからC言語の関数を引数付きで呼べないらしいのでcons_putcharを直接呼ばず、引数を扱う専用の処理(_asm_cons_putchar)を一旦呼びます。

ALに文字コードを入れた状態で、OSのセグメント0xbe8にロードされる_asm_cons_putcharを呼びます。

C言語で定義した関数に引数を渡すにはスタックに引数となる値を積む必要があります。それがC言語ではできないそうです。*1

MOV      AL,'h'
CALL    2*8:0xbe8 ;

引数専用の処理でスタックに引数をつむ

_asm_cons_putchar:
        PUSH    1               ; move:moveフラグ
        AND     EAX,0xff        ; AHやEAXの上位を0にして、EAXに文字コードが入った状態にする。
        PUSH    EAX             ; chr:表示対象の文字コード
        PUSH    DWORD [0x0fec]  ; cons:コンソールの値
        CALL    _cons_putchar
        ADD     ESP,12            ; スタックに積んだデータを捨てる
        RETF

スタックに積んでいる順がpopした際にcons_putcharの引数として適当な順番になっていることが確認できます

※push命令を順序を変えずに抜粋。

PUSH    1               ; move:moveフラグ
PUSH    EAX             ; chr:表示対象の文字コード
PUSH    DWORD [0x0fec]  ; cons:コンソールの値
void cons_putchar(struct CONSOLE *cons, int chr, char move)

_asm_cons_putcharの以下の部分は何をやっているのかよくわかりません。抜いても動作は変わらず、正しく文字表示ができました。

AND      EAX,0xff        ; AHやEAXの上位を0にして、EAXに文字コードが入った状態にする。

consの渡し方だけ意味不明なので別途解説します

void console_task(struct SHEET *sheet, unsigned int memtotal)
{
  //抜粋
    struct CONSOLE cons;
    cons.sht = sheet;
    cons.cur_x =  8;
    cons.cur_y = 28;
    cons.cur_c = -1;
    *((int *) 0x0fec) = (int) &cons;

書き込み対象のコンソールの情報consを0x0fecに書き込んでいます。ここをアセンブリで参照してスタックに積んでいます。

PUSH DWORD [0x0fec]  ; cons:コンソールの値

今のままだと、consの番地がOSとアプリケーションに決めうちになっています。

23bitレジスタ,16bitレジスタ,8bitレジスタ復習(EAX,AL等)

一応補足ですが、EAX,AX,AL,AHは全てEAXの一部分です。

EAX:32bitレジスタ AX:16bitレジスタ AL: 8bitレジスタ AH: 8bitレジスタ

ALはEAXのどこの部分か

レジスタがEAXのどの部分に当たるかを図にしました。

EAX:■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■  
AX :□□□□□□□□□□□□□□□□■■■■■■■■■■■■■■■■  
AL :□□□□□□□□□□□□□□□□□□□□□□□□■■■■■■■■  
AH :□□□□□□□□□□□□□□□□■■■■■■■■□□□□□□□□  

IDT経由で割り込みとしてシステムコールを呼び出す

MOV      AL,'h'
CALL    2*8:0xbe8 ;

この文字出力APIは番地が決めうちになってしまっています。OSの開発が進むにつれこの番地が変わった場合、 新しい番地に書き直す必要があります。このAPIを読んでいる箇所を全て書き直す必要があります。

これは問題なので関数をIDTに登録し、割り込み命令扱いとし、INT命令で呼び出すように変更します。

[BITS 32]
        MOV     AL,'h'
        INT     0x40
        MOV     AL,'e'
        INT     0x40
        MOV     AL,'l'
        INT     0x40
        MOV     AL,'l'
        INT     0x40
        MOV     AL,'o'
        INT     0x40
        RETF

APIを変更し、INT 0x40で呼び出せるようにしました。上記でhelloと表示されます。 また本書ではIDTテーブルに限りがあることから、INT 0x40の処理にswitch文的な機能を持たせてedxで処理を切り替えるように作ってあります。

プログラムのメモリのハードコードを割り込みテーブルを使って解決するというのは、ちょっと強引な気がしました。本来の使い方とはかけ離れていると思うのですが、一般的なやり方なのでしょうか。

追記:Linuxでは0x80で呼び出すそうです。

http://kzlog.picoaccel.com/post-104/

アプリケーションからOSの機能を呼び出すのは、ソフトウェア割り込みが一般的なんですかね。そういえばLinuxもint 0x80だし。その他、コールゲートというのがあるのは知っているけれど、他にはあるのかな。

感想

この本はなんかライブ感?がある感じで(毎回挨拶からスタートしてる)これもしかして草稿は30日で書き上げられたのかな?とふと思いました。

*1:あくまで本書に書いてあることの私の理解です。もしかしたら最近のアセンブリは出来るようになっている気もする。

30日OS自作本19日目

19章の内容です。typeコマンドでOSにコピーしたファイルの読み込みと、ファイルとしてコピーした機械語の実行を行いました。

f:id:yuyubu:20180612052308p:plain

FAT12の復習

1セクタ=512バイト

sector offset size(Byte) content
1 0x0000 0x0200 boot sector
9 0x0200 0x1200 fat area
9 0x1400 0x1200 fat area(sub)
14 0x2600 0x1c00 root directory
2849 0x4200 0x164200 file area

合計2882セクタ

FAT上でどのように物理ファイルを扱うか

MakeFile内で以下のようにOSに直接ファイルを書き込んでいます。

copy from:haribote.sys to:@: \
copy from:ipl10.nas to:@: \
copy from:make.bat to:@: \
NNNNNNNNNNNNNNNNEEEEEETTRRRRRRRRRRRRRRRRRRRRTTTTDDDDCCCCSSSSSSSS
48415249424F54455359530000000000000000000000549CCA4C0200486E0000
49504C31302020204E41530000000000000000000000927B924C3A00950B0000
4D414B45202020204241540000000000000000000000927B924C40002E000000

本書の流れに剃って バイナリダンプしたファイルをバイナリエディタ上で直接探します。 

$ hexdump -C haribote.sys
00000000  b8 00 90 8e c0 bf 00 00  b8 00 4f cd 10 3d 4f 00  |..........O..=O.|
00000010  75 52 26 8b 45 04 3d 00  02 72 49 b9 05 01 b8 01  |uR&.E.=..rI.....|
00000020  4f cd 10 3d 4f 00 75 3c  26 80 7d 19 08 75 35 26  |O..=O.u<&.}..u5&|
・・・(略)

先頭の16バイトの内容b800908ec0bf0000b8004fcd103d4f00でimgファイル内をググってみます

0x41FFが開始のようです。

同じようにして各ファイルの内容が配備されているアドレスを表に起こして見ました。

file name clustno ofset
haribote.sys 0x0002 41FF
ipl10.nas 0x003A B1FF
make.bat 0x0040 BDFF

本書によるとディスクイメージ内のアドレス = clustno * 512 + 0x003e00で決まるようです。

例えばharibote.sysの場合、0x0002 * 512 + 0x03e00 = 0x0400 + 0x03e00 = 0x4200 となるのですが、なんか私の調査結果だとさらに-1されてるように見えるのですが?????

typeコマンドは本書に掲載されているものをそのままでなぜか動きました。

初アプリ

アプリというか、静的な機械語の実行ファイルをOSに保持させてそれをプログラムから呼ぶだけ。 19章ではhlt命令を実行するだけのプログラムを実行した。

 $ cat hlt.nas 
[BITS 32]
fin:
    HLT
    JMP fin

上記をアセンブルした下記をhariboteOS内にファイルとしてコピして呼ぶ。

$ hexdump -C hlt.hrb 
00000000  f4 eb fd                                          |...|
00000003

手順としては見つかったファイル内容をセグメントに登録してそこにfar jumpしておしまい。

感想

  • コンソールにスクロール機能が付いていないので大きいファイルをtypeコマンドで表示した場合、先頭の方のデータがわからない。
  • typeコマンドで断片化したファイルの表示ができたら面白いと思う
    • ファイルを書き込んでも必ず断片化されるわけではないので、用意が大変そう。バイナリを直接いじるのが良さそう?
  • FAT12デフラグするプログラムも書くイメージが湧いた

    • ディスクのフォーマットに依存する
  • doxygen(c言語javadoc的なやつ)的なのが付いていないから、数週間ぶりに関数などを見ると使い方を忘れる。

30日OS自作本18日目

細かいコンソールの修正といくつかのコマンド(mem,cls,dir)を作る内容でした。

動画

www.youtube.com

文字列を比較する

string.hで定義されているstrcmpを使用します。

arr[0] == "t" && arr[0] == "s" && arr[2] == "t"

これが

strcmp(arr,"tst")==0

こんなにシンプルにかけます。

#include<stdio.h>
#include<string.h>

int main(void){
        char arr[3] = "tst";
        if(strcmp(arr,"tst")==0){
                printf("true");
        }
        return 0;
}

文字列の取り扱い、Cは普段使っているもの(Java,JS,VB)と結構違うので苦労します。

コマンド

  • mem:今まで画面に出してたmemory freeみたいな情報をコンソールにだすだけ
  • cls:コンソールに書かれている内容を削除するだけ。
  • dir:linuxでいうlsコマンド

dir

dirコマンドに利用するファイルはhariboteOSの中で作成するのではなく、windowsからコピーしてきます。 ですので、本書の内容ではファイルフォーマットはWindowsのものをそのまま拝借するようになっています。

struct FILEINFO {
    unsigned char name[8];  //8
    unsigned char ext[3];   //3
    unsigned char type;     //1
    char reserve[10];       //10
    unsigned short time;    //2
    unsigned short date;    //2
    unsigned short clustno; //2
    unsigned int size;      //4        
};
FILEINFO(見やすさのために書いてます)
NNNNNNNNNNNNNNNNEEEEEETTRRRRRRRRRRRRRRRRRRRRTTTTDDDDCCCCSSSSSSSS
FILE(私の実行結果)
48415249424F544553595300000000000000000000007A16C84C0200706C0000
49504C31302020204E41530000000000000000000000927B924C3900950B0000
4D414B45202020204241540000000000000000000000927B924C3F002E000000

あれ?これファイル内容は入ってなくないか?と思いましたが、19日目の内容を見るとこのclustnoに入っている数字の先にファイル内容が入っているようです。

N文字ごとに改行する置換

下記の正規表現で16進数で書かれたテキストを32バイトごとに改行します

置換前
(.{64})

置換後
$1\r\n

バイナリエディタからコピペで持ってきた時点ではもちろん一行の長い16進数でしかなかったので 上記で置換しました。

余談

昔、結合したセルが一行になるようなエクセルのシートに研修の感想を書かされたことがあります。 これ右端に行った時の改行を自分でやらないといけないんですよね(それじゃ紙に書いてるのと一緒じゃねーか) 固定幅フォント何文字で改行されるのか調べてsakura editorで同じような置換をしてそのまま貼り付けて提出しま した。

コマンド名の変更

dirコマンドなんてWindowsみたいで嫌いだ、Linuxみたいにlsコマンドの方がいいよーという人もいるかもしれません。そういう人は、もちろんlsコマンドにしてくれていいですよ。どこを直したらいいかなんて、ここまで読んできた皆さんには言わなくてもわかりますよね?

とのことですので他のも全部変えて見ました

  • mem:free
  • cls:clear
  • dir:ls

lsコマンド作った、というとなんかすごいことをやったように思えますが、今の所、リードしたデータをWindowのファイルフォーマットに合わせてデータを分解して画面に出してるだけですね〜 そういえばlsコマンドのソースを読む内容の本があったのを思い出しました。

lsを読まずにプログラマを名乗るな!

lsを読まずにプログラマを名乗るな!

いつか読もうと思っています。

感想

コマンドを作っているというより、コンソールを改造しているような感じです。実装もconsole_taskの中にベタ書きになっているので。でも多分今後の章で切り出す気がします。

そういえば普通のコンソール(bash,zsh,cmd)でも実行ファイルとして切り出されておらず、コンソールの機能として実装されているコマンドがあったりするのかしら?

30日でできる! OS自作入門

30日でできる! OS自作入門