Yabu.log

ITなどの雑記

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

22章,23章あたりを読みました。

IN述語の中には列名を書ける

これは結構以外に思われる方が多いのではないでしょうか?

例えばテストの点数をscore1~score5でもつEcaminationテーブルについて、 score1~score5のどれか一つでも100点を取っている生徒、という条件抽出を試してみます。

PostgreSQLで例示します。 SELECT文発行対象のテーブル*1

DROP TABLE Examination;
CREATE TABLE Examination(
  student_id char(4) NOT NULL PRIMARY KEY,
  score1 integer,
  score2 integer,
  score3 integer,
  score4 integer,
  score5 integer
);

insert into Examination(student_id,score1,score2,score3,score4,score5)
  values('0001',90,90,60,53,0);
insert into Examination(student_id,score1,score2,score3,score4,score5)
  values('0002',40,20,70,51,30);
insert into Examination(student_id,score1,score2,score3,score4,score5)
  values('0003',60,90,10,43,100);  

実行

SELECT * FROM Examination
WHERE 100 in(score1,score2,score3,score4,score5);

結果

postgres=# SELECT * FROM Examination
postgres-# WHERE 100 in(score1,score2,score3,score4,score5);
 student_id | score1 | score2 | score3 | score4 | score5 
------------+--------+--------+--------+--------+--------
 0003       |     60 |     90 |     10 |     43 |    100
(1 row)

NOT INのサブクエリにNULLが含まれる場合、常に結果は無しになる。

以前このテーマを題材にQiitaに記事を書きました(懐かしい)

qiita.com

  • みなさんNOT INはあまり使わないようです。

MySQLではcheck制約の代わりにenum,setなどが使われている

  • 後述しますが、MySQLにはcheck制約のようなものはないようです。
  • 方言がある。使用実績は少ないのが実情。
  • トリガーで値をチェックすることもある。
  • ググるMySQLでcheck制約を頑張って実現しようとする投稿が多く引っかかる。

VALUES句はINSERT句以外にも使える

これはちょっと衝撃的でした。 ちょっとした検証用の一時表を作るときは

  • 1.スカラ式のみのSELECT句を作る
  • 2.UNIONで繋げる
  • 3.WITHで囲って一時表とする

このテクニックはこのQiitaの投稿で利用しています。

qiita.com

WITH students as(
SELECT 1 AS sid, '高橋太郎'   AS sname FROM DUAL
UNION ALL
SELECT 2 AS sid, '山田幸助'   AS sname FROM DUAL
UNION ALL
SELECT 3 AS sid, '鈴木美恵子' AS sname FROM DUAL
UNION ALL
SELECT 4 AS sid, '織田信長'   AS sname FROM DUAL
)
SELECT * FROM students ORDER BY sid

このやり方を使わずともVALUES句でUNIONを使わずに一時表が作れたようです。

SELECT * FROM
(VALUES(1,'高橋太郎'),(2,'山田幸助'),(3,'鈴木美恵子'),(4,'織田信長'))
AS students(sid,sname);

PostgreSQLでの実行結果

postgres=# select * from
postgres-# (values(1,'高橋太郎'),(2,'山田幸助'),(3,'鈴木美恵子'),(4,'織田信長')) as students(sid,sname);
 sid |   sname    
-----+------------
   1 | 高橋太郎
   2 | 山田幸助
   3 | 鈴木美恵子
   4 | 織田信長
(4 rows)

Mysqlではできない。

SQL ServerのINCLUDE

  • インデックスのリーフにINCLUDEで値を入れられる。
  • INCLUDEで指定した値のみの問い合わせで条件でインデックスが使われる場合、インデックスオンリースキャンになる。
  • 同等の仕組みがDB2にもある。

MySQLのセカンダリーインデックス

MySQLのセカンダリーインデックス(PK以外のインデックス)は必ず主キーを保持しているので 主キーの一部のみの問い合わせを、セカンダリーインデックスで行なった場合、 インデックスオンリースキャンにできる。

量化比較述語 ALL,ANY

初めて見ました。 http://jutememo.blogspot.jp/2010/11/sql-3-all-any.html 初出現が22章ですが、23章で説明が入ります。(さすが難しい本。厳しい)

Aの上下逆さ記号とEの左右反対記号

離散数学とか論理学の話でよく出てくる記号ですが

  •  \forall:ALLのA
  •  \exists:EXISTSのE

が元らしいです。こういうのとりあえず暗記させられてたからルーツとかちょっと意外でした。

EXISTSをCHECK句 vs REFERENCE句

制約の対象となる親レコード

CREATE TABLE ZipCodes
(state_code CHAR(2) NOT NULL PRIMARY KEY);

--データ投入
insert into ZipCodes (state_code) values('JP');
insert into ZipCodes (state_code) values('US');

REFERENCE句

DROP TABLE Addresses;
CREATE TABLE Addresses(
  addresses_name CHAR(25) NOT NULL PRIMARY KEY,
  street_addr CHAR(25) NOT NULL,
  city_name CHAR(20) NOT NULL,
  state_code CHAR(2) NOT NULL REFERENCES ZipCodes(state_code)
);

INSERT INTO Addresses (addresses_name,street_addr,city_name,state_code) values('俺の家','1','1','JP');
INSERT INTO Addresses (addresses_name,street_addr,city_name,state_code) values('トランプの家','1','1','US');
INSERT INTO Addresses (addresses_name,street_addr,city_name,state_code) values('金正恩の家','1','1','KP');
--三行目でエラー(親側のテーブルにKPが登録されていないため)
--ERROR:  insert or update on table "addresses" violates foreign key constraint "addresses_state_code_fkey"
--DETAIL:  Key (state_code)=(KP) is not present in table "zipcodes".

CHECK制約

DROP TABLE Addresses;
CREATE TABLE Addresses(
  addresses_name CHAR(25) NOT NULL PRIMARY KEY,
  street_addr    CHAR(25) NOT NULL,
  city_name      CHAR(20) NOT NULL,
  state_code     CHAR(2) NOT NULL,
  CONSTRAINT valid_state_code
    CHECK(
      EXISTS(
        SELECT *
        FROM ZipCodes AS Z1
        WHERE Z1.state_code = Addresses.state_code
      ))
);
--上記のDDLはpostgresqlでは実行できない。
--ERROR:  cannot use subquery in check constraint
  • REFERENCE句の方がカスケードを使えるというメリットがある。
  • このCHECK句が使える実装(RDBMS)がない?
    • MySQLではcheck制約verのDDLを実行できたが、意図した制約動作をしていない。

オマケ:MySQLのReference句はINSERTに対して制約を書けない?

帰宅後、自宅で検証してちょっと不思議な現象が発生したのでメモです。

mysql> CREATE TABLE Addresses(
    ->   addresses_name CHAR(25) NOT NULL PRIMARY KEY,
    ->   street_addr CHAR(25) NOT NULL,
    ->   city_name CHAR(20) NOT NULL,
    ->   state_code CHAR(2) NOT NULL REFERENCES ZipCodes(state_code)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql>
mysql> INSERT INTO Addresses (addresses_name,street_addr,city_name,state_code) values('俺の家','1','1','JP');
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO Addresses (addresses_name,street_addr,city_name,state_code) values('トランプの家','1','1','US');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO Addresses (addresses_name,street_addr,city_name,state_code) values('金正恩の家','1','1','KP');
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> select * from Addresses;
+--------------------+-------------+-----------+------------+
| addresses_name     | street_addr | city_name | state_code |
+--------------------+-------------+-----------+------------+
| トランプの家       | 1           | 1         | US         |
| 俺の家             | 1           | 1         | JP         |
| 金正恩の家         | 1           | 1         | KP         |
+--------------------+-------------+-----------+------------+
3 rows in set (0.00 sec)

ZipCodeに登録していない北朝鮮の住所が登録できてしまいました。

少し調べると衝撃的なことが書いてありました。

2 つのテーブルを結合するだけの場合は、外部キー制約は必要ありません。InnoDB 以外のストレージエンジンの場合、カラムを定義するときに REFERENCES tbl_name(col_name) 句を使用できます。これは実際の効果はありませんが、現在定義しようとしているカラムが別のテーブルのカラムを参照する予定であるという自分のメモまたはコメントとして役立ちます。この構文を使用するときは、次の点を理解しておくことが非常に重要です。

MySQL は、col_name が実際に tbl_name に存在するか (また、その tbl_name 自体が存在するか) を確認するためのどのような CHECK も実行しません。

MySQL は、tbl_name に対してどのようなアクションも実行しません。たとえば、定義しようとしているテーブルの行に実行されたアクションに対応して行を削除することなどはありません。つまり、この構文にはどのような ON DELETE 動作や ON UPDATE 動作もありません。(REFERENCES 句の一部として ON DELETE 句や ON UPDATE 句を記述することはできますが、これらも無視されます。)

この構文はカラムを作成します。どのようなインデックスやキーも作成しません。

https://dev.mysql.com/doc/refman/5.6/ja/example-foreign-keys.html

😱

IS NOT FALSEとIS TRUE

postgres=# select true IS TRUE;
 ?column?
----------
 t
(1 row)

postgres=# select null IS NOT FALSE;
 ?column?
----------
 t
(1 row)
  • 初めて知りました。
  • 普通は使わないらしい

23.1にある1歳年上のSQL上での表現

INTERVAL 365 DAY

は1歳年上としては不適切なのでは?

  • 閏年
  • 365日先に生まれたから1歳上と言えるのか?

ドメインルールによると思いますが、教育関係の場合は学年での差分?をとるといいと思うのですが、 例えば

  • 1991/03/11 (1990/4/2~1991/4/1のグループ)
  • 1991/04/11 (1991/4/2~1992/4/1のグループ)

この2人の場合は1歳(1学年)差があるという扱いをする気がしますね ちなみに学年グループが4月2日からはじまる理由を知らない人はこちらを参照*2

あんまりSQL関係ありませんね。

量化子SOME

ANYと同じ意味

この本の人物録を作るのは極めて困難

しかし、アレックス・ドーフマンは1つのサブクエリで解く方法を見つけた。

アレックス・ドーフマンって誰?検索してもこの本しか出てこないんだけど…*3

この本はニュースグループというのでしょうか?ネット上の草の根SQLコミュニティの投稿なども多数紹介されており、このドーフマンさんのように出典が一切書かれていないものも多くあります。主催の方は人名録的の作成を諦められたようです。

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

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

*1:ちなみにこのDDLSQLアンチパターン7章のマルチカラムアトリビュートです。本番でテーブル設計する人は従属テーブルを作りましょう

*2:確か祖母がこれを食らってた気がします

*3:あなたがこの文章を読む頃にはこのブログも検索結果に含まれていることでしょう。

30日OS自作本8日目

8日目の内容になります。 前回マウスを動かすための割り込み処理を完成させたので、 受信したデータを使ってマウスカーソルを動かす内容になります。

祝マウス初動作。灰色のバーはマウスが通った後再描画されないのでグチャグチャ

動画

動画を撮ってみました

www.youtube.com

マウスの受信データの解説

マウス入力は3バイトをワンセットとして送られてくるようです。意味のある入力として読み取るためにmdec.phaseという変数でこの3バイトの受信を同期させるようなことをやっています。

 if (mdec->phase == 0) {
        /* マウスの0xfaを待っている段階 */
        if (dat == 0xfa) {
            mdec->phase = 1;
        }
        return 0;
    }
    if (mdec->phase == 1) {
        /* マウスの1バイト目を待っている段階 */
        if ((dat & 0xc8) == 0x08) {
            /* 正しい1バイト目だった */
            mdec->buf[0] = dat;
            mdec->phase = 2;
        }
        return 0;
    }
    if (mdec->phase == 2) {
        /* マウスの2バイト目を待っている段階 */
        mdec->buf[1] = dat;
        mdec->phase = 3;
        return 0;
    }
    if (mdec->phase == 3) {
        /* マウスの3バイト目を待っている段階 */
        mdec->buf[2] = dat;
    //以下バッファの内容を加工してmdecにデータを詰めるコードが続く
    --(省略)--
        
        return 1;
    }

バッファから構造体へ変換

mdec->btn = mdec->buf[0] & 0x07;
mdec->x = mdec->buf[1];
mdec->y = mdec->buf[2];
 sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
 if ((mdec.btn & 0x01) != 0) {
   s[1] = 'L';
 }
 if ((mdec.btn & 0x02) != 0) {
   s[3] = 'R';
 }
 if ((mdec.btn & 0x04) != 0) {
   s[2] = 'C';
 }

受け取った3バイトのデータは以下のように対応しています。

  • 1バイト目:下位3bitがクリックに関する情報
  • 2バイト目:マウスカーソルの水平方向の動き
  • 3バイト目:マウスカーソルの縦方向の動き

マウスから送られてくるデータを四角括弧内に表示

[]の中身ですが左から1バイト目、2バイト目、3バイト目となります。

確かにこの3バイトをただ画面に表示しているharib05bなどを見ると、 限りなく縦にマウスを動かした場合は3バイト目しか反応せず、限りなく横だけにマウスを動かせば2バイト目が変化しているように見えます。

しかし、なぜかカーソルを動かしている際に1バイト目が反応することがあるようです

調べましたがこの辺の参考情報はWikiには乗っていないようです。*1

マウスはどのように制御するのですか? -- 名無しさん 2005-03-08 (火) 19:42:26
ご質問ありがとうございます。マウス関係はそのうちページを作りたいと思います。 -- K 2005-03-10 (木) 13:15:21

http://oswiki.osask.jp/?cmd=read&page=%28AT%29keyboard&word=keyboard

ちょっと割り込みの知識が整理しきれていないので、別途整理して日記に書こうと思います。

ちなみにMacbook Proトラックパッド単体で右クリックすることはできないようです。 macOSで右クリックに相当するダブルタップもLcRとして認識されませんでした。 commandキーを押しながらクリックすることで右クリックとして認識されました。 ただしこの動作はqemu内のhariborteOSのみで右クリックとして扱われているようで、macOS上でやっても特に右クリックメニューとかは出ません。

プロテクトモード

day3に書いてある内容、セグメントレジスタESとBXを使わない方法。 GDTを代わりに使うようです。 保護(プロテクト)モードにも種類があって16bit,32bitがあるそうです。 今回利用するのは32bitの保護モードです。

感想

後半の32ビットモードへの道に書いてあることは省略(お預け)されていることも多く、よくわからなかった。 とにかく、このOSかBIOSか何かはデフォルトで16ビットの非保護モードで起動してしまうので、起動直後に32ビットモードに切り替えてGDT,IDTを作り直すということをやっているのだと思う。

*1:Kさんは著者の川合さん

30日OS自作本7日目

7日目は6日目で作った割り込みを少しカスタマイズしました。 マウスからの入力が感知できるようになりましたが、まだ動かす部分は作っていません。

7日目終了時の画面。表示されているデータの左側がキーボード、右側がマウス入力のもの

割り込み処理の中身を小さく

  • ハンドラの関数内部にVRAMにピクセルを書き込む処理(キー入力の文字表示)
    • ハンドラの中で行う処理としては重すぎる
      • 改善案1
        • ハンドラの中では割り込みが発生したフラグを立てる
        • かつ入力されたキーの情報も変数で保持
        • 同時に(ctrl)2バイトが送られてるキーだとうまくいかない。
          • ctrl押下時に(E0 1D),離した時に(E0 9D)を受信する
          • 改善案1適応時にE0が表示されない(読み捨てられる)
          • ちなみにこの挙動は自分の環境では再現しなかった。(harib04aでも0Eは表示されず((まぁあくまで私の肉眼で確認できなかっただけですが。)))
      • 改善案2
        • バッファを利用して同時に複数バイトが送信されるケースも対応する
        • 配列でFIFOバッファ(キュー)をつくる
        • 最終的にリングバッファを採用する

マウス操作にも使えるバッファに

struct FIFO8 {
    unsigned char *buf;
    int p, q, size, free, flags;
};
  • FIFO8という名前の構造体に。
    • 構造体の定義はbootpack.hに
    • 処理の定義はfifo.cに
  • リングバッファの制御用の変数
    • p:読み出しの位置
    • q:書き込みの位置
  • 配列の長さ->無制限にunsigned char *buf;
    • sizeで長さを外部から設定できるようにする
  • 空き容量を保存するfree
  • バッファ溢れの情報を保持するflags

マウスを使うための設定

  • マウス制御回路の有効化
  • マウス自体の有効化

http://oswiki.osask.jp/?cmd=read&page=%28AT%29keyboard&word=keyboard

#define PORT_KEYDAT                0x0060
#define PORT_KEYSTA                0x0064
#define PORT_KEYCMD                0x0064
#define KEYCMD_WRITE_MODE  0x60
#define KBC_MODE                 0x47


void init_keyboard(void)
{
    /* キーボードコントローラの初期化 */
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, KBC_MODE);
    return;
}

#define KEYCMD_SENDTO_MOUSE        0xd4
#define MOUSECMD_ENABLE            0xf4

void enable_mouse(void)
{
    /* マウス有効 */
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
    return; /* うまくいくとACK(0xfa)が送信されてくる */
}

マウスの設定値の根拠みたいなの、ググりまくってもよくわからない。まぁ見つかってもこの位置のbitはXXXを有効かするフラグです、 みたいなのが見つかるだけだと思うので、ちょっとここをググるのはやめよう。

この辺が情報元なのかな?

データ受け取り

マウス制御回路はキーボード制御回路の中にあるらしく、データの受け取り箇所はキーボードと全く同じらしい。 ので割り込み番号で判別して扱う。

if (fifo8_status(&keyfifo) != 0) {
  i = fifo8_get(&keyfifo);
  io_sti();
  sprintf(s, "%02X", i);
  boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);
  putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
} else if (fifo8_status(&mousefifo) != 0) {
  i = fifo8_get(&mousefifo);
  io_sti();
  sprintf(s, "%02X", i);
  boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 47, 31);
  putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}

感想

ネットに転がっている感想を読み漁って気になるものがありました。

まあ,この本を読んでいくうちにわかってくるが, PC/AT アーキテクチャ自体が,資料無し,の山のようだから.

http://www.makisima.org/wiki/wiki.cgi?HariboteOS

そうなの?OSを作るような人は資料の無い一つ一つのハードウェアにオシロスコープとか使ってリバースエンジニアリングでもして仕様を掘り当てるのかな?大変そう(小並感)。

30日OS自作本6日目

6日目は割り込みハンドラの実装&呼び出しを行った。

キーボード入力に反応.マウスはまだ動かない.

GDTの仕組み

GDTRという48ビットのレジスタにGDTを配置した番地(開始地点)とリミット(開始地点からどこまでGDTか)を書き込む

  • GDTRの最初の2バイトはリミットを表す
  • 残りの4バイトでGDTが置いてある番地を表す
  • 0xFFFF000027000x270000~0x27ffffまでGDTとして扱うという意味なる。
    • GDT自体のサイズは8バイトであるからこの場合GDTは8192個設定可能となる。

構造体の解説

struct SEGMENT_DESCRIPTOR {
    short limit_low, base_low;
    char base_mid, access_right;
    char limit_high, base_high;
};

上記の構造体の説明。文章だけだとこんがらがったので画像に起こして理解した。画像とコードの対応がちょっとおかしいが、C言語は4bit長の変数がないらしく、limit_highの上位4bitにセグメント属性を書き込むようになっている。最終的にGDTに書き込む長さとしてはlimit_highは実質4bit,access_rightは実質12bitとなっている。

構造体上での表現


63       55       47       39       31       23       15       7        0
+--------+--------+--------+--------+--------+--------+--------+--------+
|base_hi |limit_hi|   ar   |base_mid|     base_low    |    limit_low    |
+--------+--------+--------+--------+--------+--------+--------+--------+



CPUの仕様上のデスクリプタ
※limit_highの上位4bitはアクセス属性の続きになります。だから実際はCPUは上の構造体を以下の形式で扱います。

63       55       47       39       31       23       15       7        0
+--------+----+----+--------+--------+--------+--------+--------+--------+
|base_hi |ar  |l_hi|   ar   |base_mid|     base_low    |    limit_low    |
+--------+----+----+--------+--------+--------+--------+--------+--------+


※入りきらない変数名は省略しています

アクセス権の設定値

arの中身です。

設定値 hex 意味
00000000 0x00 GDTが未使用であることを示す
10010010 0x92 システム専用readのみ。実行不可
10011010 0x9a システム専用read可能&実行可能
11110010 0xf2 アプリケーション用readのみ。実行不可
11111010 0xfa アプリケーション用read可能&実行可能

セキュリティ

CPUにはアプリケーションモードとシステムモードがある。

  • アプリケーションモードの時にLGDTの実行を制限(監視)する
  • アプリケーションモードの時にシステム専用のセグメントの読み書きを制限(監視)する

実行中のプログラムが0x9a,0xfaどちらのセグメントに配置されているかでモードの判別を行う。 アプリケーションモード時にLGDTの実行を制限しているのはGDTの再設定ができてしまうから。*1

割り込み

割り込みにはGDTとIDTの初期化に加えて、PICの初期化が必要

PICとは?

programmable intrrupt controllerの略

CPUは単独では一つしか割り込みを扱えない。 複数(8個)の割り込みを一つの割り込み信号にまとめる装置がPIC

割り込み信号をIRQといい0~8の数字が振られPICの各ピンに対応している

本中のPICは2連結(マスター:スレーブ)になっているらしく、15個のIRQが扱える*2

  • マウス:IRQ12
  • キーボード:IRQ1

はここに記載があるものと一緒。何かの標準規格?

http://d.hatena.ne.jp/wocota/20090302/1235985661

ハードウェア的にはIntel8259と呼ばれるものと同一?

PICの接続をマスターの2番ピンにさしてるのはIBMのおじさんが決めた、的な下りがあるけど、 IBMのおじさんが決めた規格があってInterl人もそれに添って決めているということなのかな。

PICのレジスタ

全て8bit

  • IMR:innterrupt mask register
    • 1が立っている位置に対応しているIRQの割り込みを無視する。
  • ICW:1~4の合計4バイトがあるが何に使っているかよくわからない。プログラムの設定値以外は設定できないらしい。
    • ICW3:スレーブが刺さってるマスタのピンを登録。つまり0b00000100で固定。
      • 下位ビットからpin 0,pin 1,pin 2,...という対応だと思う。
      • 他の値は設定できない。
    • ICW2:IRQ0~15をINTのどれで受け取るかを指定。以下に設定例を示す
io_out8(PIC0_ICW2, 0x20  ); /* IRQ0-7は、INT20-27で受ける */
io_out8(PIC1_ICW2, 0x28  ); /* IRQ8-15は、INT28-2fで受ける */

割り込みの設定

キーボード(IRQ1)の例

  • IDTに0x21bの割り込みの祭に呼び出す関数_asm_inthandler21(アセンブリ)を設定
    • Cから呼び出せないIRETD命令が必要なためアセンブリで書く必要がある。
    • 短い。レジスタの退避、Cの関数呼び出し、IRETD命令程度しかやっていない。
  • _asm_inthandler21からC言語の関数inthandler21を呼び出す
    • キーボードが押された際に画面に出力

感想

  • 正直前章まではプログラムで絵を書いてる要素が強かったが今回から結構OSっぽくなってきたと思う。
  • ググりながら読みましたが英語と配線地獄のデータシートとにらめっこの日々を思い出した。*3

*1:いい例えが思いつかないが刑務所のセキュリティがどんだけ厳しくても、受刑者が自分の刑期を自由に設定されちゃ意味ないよね。的な感じ。

*2:1個はPICを連結しているピンで塞がるため16-1で15個

*3:まぁ楽しかったのですが。

第14回 セキュリティ共有勉強会に参加

5月10日開催。テーマは認証

新しい知見や感想を箇条書き

  • パスワードマネージャー

    • 流行ってない
    • 課金しての業務利用を推奨している
    • あまりセキュリティの専門家がパスワードマネージャーを進めてる印象はないが・・・
      • 強いパスワードは運用コストが高いが*1、実際現場できちんと運用するにはどうすれば、というところはあまり語られない気がする。
        • パスワードマネージャは良い提案
    • ちょっと調べたが、トレンドマイクロがパスワードマネージャーを提供しているらしい*2
      • これならお堅い会社も導入しやすい気がする
    • 主催者の方はAppleのkeychainはバグの対応などが遅い印象があるので使わないらしい。
  • Slackのエンタープライズ版の課題

    • SAMLのidPが1つしか使えないらしい。 
      • 複数のワークスペースの認証情報を管理することができないという問題がある。
    • keycloakというOSSを仲介に使う。
    • あまり関係ないがApple Watchでの2段階認証かっこいい。
      • PCのWEB画面でパスワード入力後、Apple Watchに通知が来る
    • Slackの社内利用を推したいけど、こういう課題があるのを知れたのは良かった。*3
  • FIDO認証がすごい。

    • RSA認証っぽい(秘密鍵と公開鍵)
    • 始めて知った
    • 端末・リテラシに依存しない
    • 各ベンダが作り始めてる
    • TCPだけでなくBluetoothでも動く。
    • 認証情報を送らないから生体情報をパスワードに使えるかも?*4
      • ググってみたがすでにそういう取り組みもある
  • CAPTCHA(開発会社の方がLTをされました)

    • 開発会社の人は苦情が気になるらしい。
    • パズルをはめるようなCAPTCHAはマウスカーソルの軌跡を分析するらしい
    • BOT側に難読なCAPTCHA機械学習で突破する研究とかあるけど、その辺のイタチごっこが気になる。
  • パスワード定期変更の是非

    • 例の総務省のポリシーが変わったやつ
    • 定期変更はPマークやISMSの必須要件となっている
      • それがボトルネックになり社内運用を変えられないケースが多いらしい。
    • まだ定期変更が有効なケースもある
      • パスワードが漏洩した場合
      • パスワードを共有するような運用をしている場合に人事異動が起きた場合
  • インフラ勉強会

    • リモートでやってるやつ
      • 羽目をはずすとログに残る
    • 地方の人は都内の勉強会とか羨ましい感じらしい
      • 地方転勤が辛いらしい
    • アメリカ人の好きな食べ物はだいたいピザらしい
      • 秘密の質問「好きな食べ物は?」が…
    • 認可と認証を混同してはいけない。
    • 自衛隊の誰何は3回聞いて返事が帰ってこなかったら撃っていいらしい。

    返事がなく、3度誰何しても答えない場合は「捕獲するか、刺・射殺」します。

    http://kamiya-masanari.com/JSDF_Word/index.html
    • 現実的な覚えやすく強いパスワードを決める際はここで推奨されている方法がいいらしい。
      • こちらで高木氏が勧めているものも同じ

*1:人間が覚えるという意味での運用コスト

*2:https://www.trendmicro.com/ja_jp/forHome/products/pwmgr.html

*3:多分1社でほそぼそと運用するには問題にならないだろうけど。

*4:生体情報は個人情報なのでサーバー側で管理するのはリスクらしい

30日OS自作本5日目

5日目は文字とマウスカーソルの表示をやりました。

VRAMにマウスカーソルとフォントを書き込んだ。マウスカーソルはまだ動かない

文字をどう扱うか

8 × 16のピクセルでAを表現することを考えます。 1行を1バイトの

□ □ □ □ □ □ □ □
□ □ □ ■ ■ □ □ □
□ □ □ ■ ■ □ □ □
□ □ □ ■ ■ □ □ □
□ □ □ ■ ■ □ □ □
□ □ ■ □ □ ■ □ □
□ □ ■ □ □ ■ □ □
□ □ ■ □ □ ■ □ □
□ □ ■ □ □ ■ □ □
□ ■ ■ ■ ■ ■ ■ □
□ ■ □ □ □ □ ■ □
□ ■ □ □ □ □ ■ □
□ ■ □ □ □ □ ■ □
■ ■ ■ □ □ ■ ■ ■
□ □ □ □ □ □ □ □
□ □ □ □ □ □ □ □
//16バイトで1文字を表現。
//※char型=1バイト
static char font_A[16] = {
  0x00,// 00000000
  0x18,// 00011000
  0x18,// 00011000
  0x18,// 00011000
  0x18,// 00011000
  0x24,// 00100100
  0x24,// 00100100
  0x24,// 00100100
  0x24,// 00100100
  0x7e,// 01111110
  0x42,// 01000010
  0x42,// 01000010
  0x42,// 01000010
  0xe7,// 11100111
  0x00,// 00000000
  0x00 // 00000000
};

Aを表示した画面

もちろん全ての文字に対してC言語でいちいち配列を作ってられないから、DB命令でアルファベットなど主要な文字の群を実行バイナリに直接書き込み、Cからアドレス経由で指定する。

文字を配列のインデックスに利用する

//ASCIIと同じ並びで1文字あたり16バイト格納。16バイト*265文字=4096バイトぶんのcharが必要。
//※char型=1バイト
extern char hankaku[4096];

putfont8(binfo->vram, binfo->scrnx,  8, 8, COL8_FFFFFF, hankaku + 'A' * 16);

putfont8の最後の引数hankaku + 'A' * 16がミソ。文字データを格納している配列hankakuの先頭アドレスから0x41 * 16 バイト先に16バイト分の'A'を表す文字のデータが格納されている。配列 + 文字のポイント演算をデータ参照のインデックスに利用しているというのが高等なテクニックに感じた。*1

//定義
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
  //for分の中は一行ごとの処理。i<16からわかる通り、16行分行う。
  for (i = 0; i < 16; i++) {
        d = font[i];
      ・・・(略)

      //VRAMに位置ピクセルごとに書き込み * 8回行う
        if ((d & 0x80) != 0) { p[0] = c; }
        if ((d & 0x40) != 0) { p[1] = c; }
      ・・・(略)
        if ((d & 0x02) != 0) { p[6] = c; }
        if ((d & 0x01) != 0) { p[7] = c; }
    }
    return;

}

マウスカーソルを動かすためにはセグメンテーションと割り込みが必要。

マウスカーソルを動かすにはGDTとIDTの説明が必要らしい。 5日目にしてはちょっと難しくなるよ、と書かれている。

セグメンテーション

  • ORG命令で読み込む番地を指定する
  • だが競合が起きると大変。
  • メモリを切り分けて好きなブロックの最初の番地を0番地にする。

以下の設定が必要

  • セグメントのサイズ
  • セグメンの開始番地
  • セグメントの管理用属性

セグメントでは0~8191番号が使える(8192個のセグメントが使える)

8192個のセグメントが定義できるわけで、だから設定にはその8倍の65536バイト(=64KB)が必要になります

この8倍っての1つのセグメントの設定に必要なサイズだと思うのだけれど、どこから出てきたのだろう?*2

とにかくこの64KBをGTD:global (segment) descriptor tableという。 使い方としては並んだセグメントの設定の先頭と有効設定個数をCPUのGDTRというレジスタに登録すれば完了。

割り込み

  • IDTはinterrupt descriptor tableの略
  • ポーリングと割り込みの比較。が入っている。コンピュータは監視対象が多すぎるのでポーリングでOSを作るのは難しい。
  • IDTでは割り込み番号0~255を設定可能(つまり割り込みは256種類までしか登録出来ない?)
    • 番号と関数を対応付けた表のようになっているらしい。

セグメントと割り込みのコード

データ構造は以下のようになる

//GDTの8バイトの中身
struct SEGMENT_DESCRIPTOR {
    short limit_low, base_low;
    char base_mid, access_right;
    char limit_high, base_high;
};
//IDTの8バイトの中身
struct GATE_DESCRIPTOR {
    short offset_low, selector;
    char dw_count, access_right;
    short offset_high;
};

GDTとIDTのメモリ割り当てはコード上ではこんな感じ。

void init_gdtidt(void)
{
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;
    struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;

関係ないけど上のコードをatomで書いたらmarkdownシンタックスハイライトがバグった。(Markdown上の**強調表現**C言語シンタックスハイライトが衝突してる?)

コードより後ろのテキストが全て強調表現の扱いになっている

以降のコードの詳細。(GDTとIDTに実際に値を割り当てて割り込みとセグメンテーションを設定している箇所)は説明が放棄されている?(明日に廻す)ためか中途半端になっているようですのでday6の記事に書きます

 思い出

  • 昔割り込みハンドラに登録して云々?ということをやった記憶がある。
  • ISRとかいうのを使った覚えがある。

わからないこと

hariboteの開発環境では2進数リテラルが使えない?

char test = 0b01100100; この行をbootpack.cの66行に入れてmake runすると以下のようなエラーが入る。

bootpack.c:66: invalid suffix on integer constant

うーん以下のプログラムはgccコンパイルして実行もできるけどhariboteの開発環境のコンパイラは特殊?

#include <stdio.h>

int main(void){
        char test = 0b01100100;
        printf("%c\n",test); //dが出力される
}

前回失敗したuchanさんの「OS自作入門*3

http://yuyubu.hatenablog.com

起動ディスクの選択画面まではいけたが、その先が真っ黒で特にhelloworldしなかった。 打ち間違いがあったのかな?時間が無くなったので今日はこの辺でおしまい。

修正していただいたようなので、再度チャレンジしたものの昨日と状況は変わらず、起動ディスク選択画面まで行くが画面が表示されない。 USBを何本か変えてみたり、FATの種類?*4を変えてみたりしたが上手くいかなかった。 ちょっと特殊な環境でやっているため*5何が原因なのかよくわからない。別のPCを使える機会があれば再度試してみようと思う。

感想

大学時代にAVRマイコンを扱う授業の自由課題として8*8のマトリックスLEDに美咲フォントを表示する要素を作った作品を作ろうと試したことがあった。*6*7今回の内容はC言語ピクセル形式のデータをVRAMで扱うためのノウハウが詰まっていた1章であり、件の実験でやりたかったことに近い内容だったように思う。

当時は美咲フォントのデータを上手く扱うことができず、また時間がなかったため諦めたが、事前にこの本を読んでいれば出来ていた気がする。

まぁ件に限らず、技術書を読んで引き出し増やすのは重要だと思う。

*1:Cをやってる人には当たり前のテクニックなのかもしれないが。

*2:CPUの資料に書いてあるらしい

*3:http://uchan.hateblo.jp/entry/2018/05/07/233338

*4:FAT16/32

*5:USB3であったり、変換ケーブルとかが絡んでいるのが

*6:RSSかなんかに反応してニュースをスクロールで流すようなものを目標に考えていた

*7:イメージとしてはこんな感じ:https://www.youtube.com/watch?v=hcBgcG7ezLY

SQLのCOUNT,MAX関数はソートを発生させるのか PostgreSQL編

この記事の続きてです 今回も同じようなデータを使います。

id age country
0001 18 JP
0002 23 US
0003 56 SK
0004 99 SK
0005 11 US
0006 34 JP
create table people(
  id char(4) not null primary key,
  age integer not null,
  country char(2) not null
);

-- people
INSERT INTO people(id, age, country) VALUES('0001', '18', 'JP');
INSERT INTO people(id, age, country) VALUES('0002', '23', 'US');
INSERT INTO people(id, age, country) VALUES('0003', '56', 'SK');
INSERT INTO people(id, age, country) VALUES('0004', '99', 'SK');
INSERT INTO people(id, age, country) VALUES('0005', '11', 'US');
INSERT INTO people(id, age, country) VALUES('0006', '34', 'JP');

PostgreSQLでのソート

PostgreSQLではソートが入るとSort演算子が実行計画に表示される

postgres=# explain select * from people order by age;                                                      QUERY PLAN                            
-----------------------------------------------------------------
 Sort  (cost=88.17..91.35 rows=1270 width=36)
   Sort Key: age
   ->  Seq Scan on people  (cost=0.00..22.70 rows=1270 width=36)
(3 rows)

count関数

postgres=# select country, count(*) from people group by country;
 country | count 
---------+-------
 US      |     2
 JP      |     2
 SK      |     2
(3 rows)

postgres=# explain select country, count(*) from people group by country ;
                           QUERY PLAN                            
-----------------------------------------------------------------
 HashAggregate  (cost=29.05..31.05 rows=200 width=20)
   Group Key: country
   ->  Seq Scan on people  (cost=0.00..22.70 rows=1270 width=12)
(3 rows)
  • Sort確認できず

max関数

postgres=# select country, max(age) from people group by country;
 country | max 
---------+-----
 US      |  23
 JP      |  34
 SK      |  99
(3 rows)

postgres=# explain select country, max(age) from people group by country ;
                           QUERY PLAN                            
-----------------------------------------------------------------
 HashAggregate  (cost=29.05..31.05 rows=200 width=16)
   Group Key: country
   ->  Seq Scan on people  (cost=0.00..22.70 rows=1270 width=16)
(3 rows)
  • sort確認できず

集約なしmax関数

postgres=# explain select max(age) from people;
                           QUERY PLAN                           
----------------------------------------------------------------
Aggregate  (cost=25.88..25.89 rows=1 width=4)
->  Seq Scan on people  (cost=0.00..22.70 rows=1270 width=4)
(2 rows)
  • sort確認できず

結論

実行計画を見たところsortが発生している箇所はなさそうです。

おうちで学べるデータベースの基本はMySQLを念頭に置いているので、

本書では、実際にいくつかの章(特に後半)で「MySQL」というデータベースを操作することで、「データベースとは何か?」ということをわかりやすく解説しています。

MySQLの動作としてMax,Countを集約関数と同時に使うと ソートが発生するという記載は適当なのだと思います。

おうちで学べるデータベースのきほん

おうちで学べるデータベースのきほん