osdev-jpでは、OS開発に有用な情報を収集し公開しています
Takym > QA
このページでは低レイヤに関する質問とその回答をまとめています。 下記に該当する質問が無い場合は Slack の #初心者質問相談 チャンネルに訪れてみてください。
例えば x86 では下記の様な命令があります。
命令 | 説明 (概略) | 参考 |
---|---|---|
CMPXCHG8B |
レジスタとメモリ上の値を比較し等しければ交換します。 | https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b |
CRC32 |
巡回冗長検査します。 | https://www.felixcloutier.com/x86/crc32 |
INT3 |
デバッグ時に使用されます。 | https://x86.puri.sm/html/file_module_x86_id_142.html |
UD2 |
無効命令例外を発生させます。 | https://mudongliang.github.io/x86/html/file_module_x86_id_318.html |
"a"(arg0)
や "Nd"(arg1)
は何を意味しますか?a
は EAX レジスタを表します。d
は EDX レジスタを表します。N
は8ビット即値の命令エンコーディングを優先する事を表します。
IN
命令と OUT
命令で使用されます。Nd
は N
と d
の両方を指定しています。これは GCC における仕様です。 Microsoft Visual C のインラインアセンブラの仕様とは異なります。
x86 での区画記述子は下記の書式になります。
31...24 | 23 | 22 | 21 | 20 | 19...16 | 15 | 14...13 | 12 | 11...8 | 7...0 |
---|---|---|---|---|---|---|---|---|---|---|
Base 31:24 | G | D/B | L | AVL | Seg. Limit 19:16 | P | DPL | S | Type | Base 23:16 |
31...16 | 15...0 |
---|---|
Base Address 15:00 | Segment Limit 15:00 |
G
(Granularity) - 粒度。0
の場合は 1 B 単位で設定し、1
の場合は 4 KB 単位で設定する。D/B
(Default operation size) - 既定の制御ビット数。0
の場合は16ビット、1
の場合は32ビット。L
(64-bit code segment) - 1
の場合は64ビット。(IA-32e のみ)AVL
(Available for use by system software)P
(Segment present) - セグメントが存在する場合は 1
、存在しない場合は 0
。DPL
(Descriptor privilege level) - 特権レベルを0~3で指定する。S
(Descriptor type) - 0
の場合はシステム、1
の場合はコードまたはデータ。出典:https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html、ページ:Vol. 3A 3-9
下部から Vol. 3A 3-12
上部、参照日:2021/07/11
__attribute__((packed))
を構造体に付与します。#pragma pack(1)
を指定します。 typedef struct _STRUCT_A_ {
unsigned int id;
unsigned char *name;
} __attribute__((packed)) StructA;
#pragma pack
を使って記述できます。
#pragma pack(1)
typedef struct _STRUCT_A_ {
unsigned int id;
unsigned char *name;
} StructA;
#pragma pack()
データ構造アライメントはメモリ上のデータを効率的に配置する為に利用されています。
INT 0xXX
命令で呼び出されるコードを読む方法を教えてください。INT 0xXX
命令にブレークポイントを貼り、デバッガを使ってコードを覗く事ができます。
実機でデバッグを行う場合は専用の機器が必要になる可能性があります。
仮想環境でデバッグを行う場合は QEMU に GDB を接続すれば良いでしょう。
もしくはオープンソースの実装を読む事もできます。
INT 0x10
命令) を使わずに描画処理を行う方法はありますか?BIOS (INT 0x10
命令) を使わずに描画処理を行うには下記の方法が考えられます。
BIOS ROM に保存されているコードの解析は利用規約や著作権に抵触する可能性があります。
U+3042
ですが、「あ」と書かれたファイルには 30 42
という値は見つかりません。Unicode では必ずコードポイントと同じ値がファイルに保存される訳では無く、通常は UTF-8、UTF-16、UTF-32 のいずれかに符号化してから保存されます。 「あ」(U+3042)という文字の場合は文字コード毎に下記の様に変換されます。
E3 81 82
42 30
30 42
42 30 00 00
00 00 30 42
C言語の型 wchar_t
はワイド文字型であり、どの文字コードを使うかは規定されておらず、環境依存になります。コンパイラやOSなどにより異なります。
上記で挙げた文字コードではない可能性もあります。
どの文字コードが使われるかは適宜確認する必要があります。
scanf
や cin
を使って整数値を読み取る時に、a
などの文字列を入力するとどうなりますか?scanf
の場合異常な値が入力された場合、標準入力ストリームにその値を残したままにしてしまいます。
例えば、下記のコードを実行し「123a
」と入力した場合、(2)
で入力は行われず、(3)
は「123
」を出力します。勿論、(1)
は「123
」を出力します。
#include <stdio.h>
int main(int argc, char *argv[])
{
int a;
scanf("%d", &a);
printf("%d\r\n", a); // (1)
scanf("%d", &a); // (2)
printf("%d\r\n", a); // (3)
return 0;
}
Wikipedia の「Scanf#異常な入力が行われた時の処理」が参考になります。
scanf
の内部実装は下記のページが参考になります。
cin
の場合異常な値が入力された場合、正しい値は返却されません。 下記の様に正しい整数値が入力されたかどうか検証する事ができます。
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int a;
cin >> a;
if (cin.fail()) {
cout << "Not a number" << endl;
} else {
cout << a << endl;
}
return 0;
}
下記のページが参考になります。
scanf
及び cin
の動作実験は、こちらのページをご参照ください。(書きかけ...)
「はりぼてOS」や「MikanOS」等を参考に開発した OS であれば強制終了しても問題は無いでしょう。 UI 上から電源を切る機能(シャットダウン)がない場合(UI が破損している場合を除く)は殆どの場合は問題無いと考えても良いと思います。 強制終了して PC が壊れる原因としては下記の様な原因が挙げられます:
因みに、筆者(@Takym)は Windows 10 で強制終了を行った事がありますが、壊れた事はありません。 強制終了しても壊れない OS の開発に挑戦してみるのも良いと思います。