2019年2月16日土曜日

MochiOS@仮想ファイルシステムの作成part3

MochiOS@仮想ファイルシステムの作成part3

前提知識無しで作るなんちゃって仮想ファイルシステムpart3。

開発日記です。独り言です。

これまでのあらすじ

part 内容
0 仮想ファイルシステムを作りたい宣言をする。
1 ざっくり仮想ファイルシステムの機能を決める。
が、機能呼出し先の仮想ファイルシステムのタスクIDをどう取得するかという問題に気付く。
2 カーネルにタスク名登録機能を実装した。
これでタスク名からタスクIDを取得する事が出来る様になるハズ、なので試したいが試す方法が無い事に気付き、ログ出力タスクの開発を宣言する。

%dとか使いたいじゃん?

part3では、ログ出力機能も実装して、試してみて、わはーうまく動いてる。って書きたかったんだけど、欲張りな私は、printfみたいに「%d」とかで書式指定して変数の内容をログ出力したいと考えたわけだ。
というわけで、C標準ライブラリのvsnprintf()を実装したい。
  • 何故vsnprintf()かというと、log( "へんすうのあたい=%d", value );みたいにログ出力関数の引数を可変長にしたいから。

vsnprintf()の仕様

wikipediaさんによるとこんな規則なんですよ。
%[引数順][フラグ][最小フィールド幅][.精度][長さ修飾子]変換指定子
仕様がよくわからんので、どっかから拾ってきた仕様を、英語できないけどがんばって訳してみる。

引数順

ISO/IEC9899:1999には無さそうだし、見なかったことにするよ。

フラグ(flag)

Zero or more flags (in any order) that modify the meaning of the conversion specification.
変換指定の意味を修飾する0個以上のフラグ(順不同)
フラグ 英語説明
適当翻訳
- The result of the conversion is left-justified within the field. (It is right-justified if this flag is not specified.)
フィールドの中で左寄せされるんだぜ。(これ指定されなかったら右寄せよ)
+ The result of a signed conversion always begins with a plus or minus sign. (It begins with a sign only when a negative value is converted if this flag is not specified.)
符号付き変換は常に+か-符号から始まるぜ。(これ指定されなかったら負の数の時だけ符号から始めちゃう。)
space If the first character of a signed conversion is not a sign, or if a signed conversion results in no characters, a space is prefixed to the result. If the space and + flags both appear, the space flag is ignored.
符号付き変換で最初の文字が符号じゃないか、符号付き変換が文字にならないんなら、空白が前につくぜ。空白と+フラグが両方あったら空白フラグは無視されちゃうんだな。
# The result is converted to an ‘‘alternative form’’. For o conversion, it increases the precision, if and only if necessary, to force the first digit of the result to be a zero (if the value and precision are both 0, a single 0 is printed). For x (or X) conversion, a nonzero result has 0x (or 0X) prefixed to it. For a, A, e, E, f, F, g, and G conversions, the result of converting a floating-point number always contains a decimal-point character, even if no digits follow it. (Normally, a decimal-point character appears in the result of these conversions only if a digit follows it.) For g and G conversions, trailing zeros are not removed from the result. For other conversions, the behavior is undefined.
"alternative form(選択方式、代替形態)"に変換されっぜ。o変換に対しては、必要な時に限って、最初の桁を0に強制するんで精度を拡張する(値と精度が両方0なら一つの0が出力される)。x(かX)変換に対しては、非0は0x(か0X)が前に付く。
(続きは、気が向いたら。。)
0 For d, i, o, u, x, X, a, A, e, E, f, F, g, and G conversions, leading zeros (following any indication of sign or base) are used to pad to the field width rather than performing space padding, except when converting an infinity or NaN. If the 0 and - flags both appear, the 0 flag is ignored. For d, i, o, u, x, and X conversions, if a precision is specified, the 0 flag is ignored. For other conversions, the behavior is undefined.
d,i,o,u,x,X,a,A,e,E,f,F,g,G変換に対して、無限大や非数に変換される時を除いて、最初に空白埋めが行われるんでなく(符号か基数表示の後に続いて)0がフィールド幅を埋める為に使われるぜ。0と-フラグ両方が使われると0フラグは無視されちゃうよ。d,i,o,u,x,X変換に対しては、精度が指定されちゃってると0フラグは無視されちゃうよ。他の変換では、どうなるか決めてないよ。

最小フィールド幅(minimum field width)

An optional minimum field width. If the converted value has fewer characters than the field width, it is padded with spaces (by default) on the left (or right, if the left adjustment flag, described later, has been given) to the field width. The field width takes the form of an asterisk * (described later) or a decimal integer.
任意のフィールド幅。変換値がフィールド幅より少ない文字数なら、フィールド幅まで(デフォルトで)空白を左に埋めるぜ(後で記述される(この記事では前だけど) 左寄せフラグがある場合は右に埋める)。フィールド幅はアスタリスク*(後で記述)の形式か10進数整数をとっちゃうよ。

.精度(precision)

An optional precision that gives the minimum number of digits to appear for the d, i, o, u, x, and X conversions, the number of digits to appear after the decimal-point character for a, A, e, E, f, and F conversions, the maximum number of significant digits for the g and G conversions, or the maximum number of bytes to be written for s conversions. The precision takes the form of a period (.) followed either by an asterisk * (described later) or by an optional decimal integer; if only the period is specified, the precision is taken as zero. If a precision appears with any other conversion specifier, the behavior is undefined.
d, i, o, u, x, X変換に対しては最小桁数、a, A, e, E, f, F変換に対しては小数点後の桁数、g, G変換に対しては最大有効桁数、s変換に対しては書かれるべき最大バイト数を表す任意の精度。精度はピリオド.とそれに続いてアスタリスク*(後で記述)か任意の10進数整数の形式をとるぜ。ピリオドだけ指定される場合は0として扱うんだぜ。精度がその他の変換指定子で表れる場合の動作は未定義だぜ。
  • 浮動小数点の桁数だけだと思ってた…

長さ修飾子(length modifier)

An optional length modifier that specifies the size of the argument.
引数のサイズを指定する任意の長さ修飾子。
長さ修飾子 英語説明
適当翻訳
hh Specifies that a following d, i, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing); or that a following n conversion specifier applies to a pointer to a signed char argument.
後に続くd,i,o,u,x,X変換指定子がsigned charかunsigned char引数に適用されることを指定するぜ(引数は整数拡張に従って拡張されるよん。だけどその値は出力する前にsigned charかunsigned charに変換されるべきだよ。(詳しい人この意味教えて。。。))。または、後に続くn変換指定子がsigned char引数へのポインタに適用されることを指定するぜ。
h Specifies that a following d, i, o, u, x, or X conversion specifier applies to a short int or unsigned short int argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to short int or unsigned short int before printing); or that a following n conversion specifier applies to a pointer to a short int argument.
後に続くd,i,o,u,x,X変換指定子がshort intかunsigned short int引数に適用されることを指定するぜ(引数は整数拡張に従って拡張されるよん。だけどその値は出力する前にshort intかunsigned short intに変換されるべきだよ。)または、後に続くn変換指定子がshort int引数へのポインタに適用されることを指定するぜ。
l Specifies that a following d, i, o, u, x, or X conversion specifier applies to a long int or unsigned long int argument; that a following n conversion specifier applies to a pointer to a long int argument; that a following c conversion specifier applies to a wint_t argument; that a following s conversion specifier applies to a pointer to a wchar_t argument; or has no effect on a following a, A, e, E, f, F, g, or G conversion specifier.
後に続くd,i,o,u,x,X変換指定子がlong intかunsigned long int引数に適用されることを指定するぜ。後に続くn変換指定子がlong int引数へのポインタに適用されることを指定するぜ。後に続くc変換指定子がwint_t引数に適用されることを指定するぜ。後に続くs変換指定子がwchar_t引数へのポインタに適用されることを指定するぜ。後に続くa,A,e,E,f,F,g,G変換指定子では何の効果もないよ。
ll 気が向いたらね。
j 気が向いたらね。。
z Specifies that a following d, i, o, u, x, or X conversion specifier applies to a size_t or the corresponding signed integer type argument; or that a following n conversion specifier applies to a pointer to a signed integer type corresponding to size_t argument.
後に続くd,i,o,u,x,X変換指定子がsize_tか同様のsigned integer型引数に適用されることを指定するぜ。または後に続くn変換指定子がsize_t引数に相当するsigned integer型へのポインタに適用されることを指定するぜ。
t Specifies that a following d, i, o, u, x, or X conversion specifier applies to a ptrdiff_t or the corresponding unsigned integer type argument; or that a following n conversion specifier applies to a pointer to a ptrdiff_t argument.
後に続くd,i,o,u,x,X変換指定子がptrdiff_tか同様のunsigned integer type引数に適用されることを指定するぜ。または後に続くn変換指定子がptrdiff_t引数へのポインタに適用されることを指定するぜ。
L 気が向いたらね。。。

変換指定子(conversion specifier)

A conversion specifier character that specifies the type of conversion to be applied.
適応される変換タイプを指定する変換指定子文字。
変換指定子 英語内容
適当翻訳
d
i
The int argument is converted to signed decimal in the style [−]dddd. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it is expanded with leading zeros. The default precision is 1. The result of converting a zero value with a precision of zero is no characters.
int引数が[-]ddddの形で符号付き10進数に変換されるぜ。精度は最小桁数を指定するぜ。変換される値が精度より少ない桁数なら、0で拡張される。デフォルトの精度は1だぜ。精度0で値0を変換したら文字は無くなっちゃうぜ。
o
u
x
X
The unsigned int argument is converted to unsigned octal (o), unsigned decimal (u), or unsigned hexadecimal notation (x or X) in the style dddd; the letters abcdef are used for x conversion and the letters ABCDEF for X conversion. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it is expanded with leading zeros. The default precision is 1. The result of converting a zero value with a precision of zero is no characters.
unsigned int引数が符号無し8進数表記に(o)、符号無し10進数表記に(u)、または符号無し16進数表記に(xかX)、dddd形式で変換されるぜ。文字abcdefがx変換指定子では使われて、文字ABCDEFがX変換指定子では使われるぞ。精度は最小桁数を指定するよ。変換される値がより少ない桁数で表されるならば、先頭0で拡張されるぜ。デフォルトの精度は1や。精度0で値0を変換したら文字は無いぜよ。
f
F
気が向いたらね。
e
E
気が向いたらね。。
g
G
気が向いたらね。。。
a
A
気が向いたらね。。。。
c If no l length modifier is present, the int argument is converted to an unsigned char, and the resulting character is written. If an l length modifier is present, the wint_t argument is converted as if by an ls conversion specification with no precision and an argument that points to the initial element of a two-element array of wchar_t, the first element containing the wint_t argument to the lc conversion specification and the second a null wide character.
長さ修飾子lが指定されないなら、int引数がunsigned charに変換されて、その結果の文字が書かれるよ。長さ修飾子lが指定されるなら、精度無しでwchar_tの2要素配列(最初の要素はlc変換指定へのwint_t引数を含み、二つ目の要素はnullワイド文字を含む)の先頭を指し示す引数を持つls変換指定のように、wint_t引数が変換されるぜ。
s If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type. Characters from the array are written up to (but not including) the terminating null character. If the precision is specified, no more than that many bytes are written. If the precision is not specified or is greater than the size of the array, the array shall contain a null character. If an l length modifier is present, the argument shall be a pointer to the initial element of an array of wchar_t type. Wide characters from the array are converted to multibyte characters (each as if by a call to the wcrtomb function, with the conversion state described by an mbstate_t object initialized to zero before the first wide character is converted) up to and including a terminating null wide character. The resulting multibyte characters are written up to (but not including) the terminating null character(byte). If no precision is specified, the array shall contain a null wide character. If a precision is specified, no more than that many bytes are written (including shift sequences, if any), and the array shall contain a null wide character if, to equal the multibyte character sequence length given by the precision, the function would need to access a wide character one past the end of the array. In no case is a partial multibyte character written.
長さ修飾子lが指定されないなら、引数はchar型配列の先頭ポインタでなければならない。配列から文字達は終端null文字まで書かれる(けどnull文字は書かれないよ)。精度が指定されるなら、精度以上のバイトは書かれない。精度が指定されないか精度が配列サイズより大きければ、配列はnull文字を含むべきだ。長さ修飾子が指定されるなら、引数は、wchar_t型配列の先頭ポインタであるべきだ。配列からワイド文字は終端nullワイド文字を含むまでマルチバイト文字に(それぞれ、wcrtomb関数呼び出しによる様に、mbstate_tオブジェクト(最初のワイド文字が変換される前に0に初期化される)によって記述される変換状態で)変換される。(以下略。。。疲れた。。。)
p The argument shall be a pointer to void. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.
引数はvoidポインタであるべきやね。ポインタ値は実装で定義される方法で一連の表示文字に変換されるわ。
n 気が向いたらね。。。。。
% A % character is written. No argument is converted. The complete conversion specification shall be %%.
%文字が書かれる。変換される引数はないっす。完ペキな変換指定は%%であるべきっす。

note
a field width, or precision, or both, may be indicated by an asterisk. In this case, an int argument supplies the field width or precision. The arguments specifying field width, or precision, or both, shall appear (in that order) before the argument (if any) to be converted. A negative field width argument is taken as a - flag followed by a positive field width. A negative precision argument is taken as if the precision were omitted.
最小フィールド幅と精度はアスタリスクによって示されることができるっす。この場合、int型引数がフィールド幅か精度になるっす。必ずフィールドアバか精度かその両方を指定する引数は変換されるための引数(もしあれば)より前に(順番に)表れるっす。負のフィールド幅引数は-フラグ(この後に正のフィールド幅が続く)として扱われるっす。負の精度引数は精度が省略された様に扱われるっす。っすっすっす。

実装方針を決める。

一言で言うと、「だるっ!」
こんな方針で実装しようそうしよう。
  • 引数順はちょっと見なかったことにしよう。
  • フラグはちょっとがんばろうかな。
  • 最小フィールド幅もがんばろう。
  • 精度はうーん浮動小数点演算以外も使えるの知らなかった。。。がんばろ。
  • 長さ修飾子は4バイトまでなら対応する。あとは知らない。32bitOSだし(そういうことじゃない
  • 変換指定子も浮動小数点数とよくわからないのは見なかったことにするか。
英語読むだけで疲れたわ!誤訳ばっかだわたぶん!恥ずかしい!ごめん!

2019年2月2日土曜日

MochiOS@仮想ファイルシステムの作成part2

MochiOS@仮想ファイルシステムの作成part2

前提知識無しで作るなんちゃって仮想ファイルシステムpart2。

開発日記です。独り言です。

タスク名登録機能

前回、「あれ、仮想ファイルシステムにメッセージ送るにはどうすんのよ?」というわけで、カーネルに機能を追加した。
https://ja.osdn.net/projects/mochi/scm/git/master/commits/891fd7049c315ec439dd52fdd3eaeb780d374e95
タスク名登録(MkTaskNameRegister())
任意のタスク名を登録。タスク名とタスクIDの関連付けが行える。
タスク名登録解除(MkTaskNameUnregister())
登録したタスク名とタスクIDの関連付けを解除する。
タスクID取得(MkTaskNameGet())
タスク名に一致するタスクIDを取得する。

試したい

作っただけで動くか確認していないので、試したい。
けど、どう試したものか…。
タスクAmvfsカーネルタスク名登録( "VFS" )タスクID取得( "VFS" )タスクIDタスクIDをちゃんと取得できたかをどう確認する…?タスクAmvfsカーネル
LinuxやWindowsだったらprintfで出力できるけれど、まだそんな機能は無い。
  1. 画面にログを出力するタスクを作る。
  2. いっそシリアルポートにログをたれ流せる様なタスクを作る。
  3. カーネルのデバッグログ出力機能を使う。
  4. メモリにログを書き出して、頑張ってメモリダンプして見る。
  5. 成功したら再起動させる。
1、2、3、4は割と面倒だし、5はイケてないなぁ…

画面にログを出力するタスクを作る。

面倒臭いのは確か。
1トレースログ1行で画面に出力していくと最下行に来た時に画面スクロールする機能を作らないといけないし。
でも、これからもトレースログで動作を確認したい事はあるので頑張って作る事にする。幸い、カーネルにも以前OSCで展示用に作っていた端末エミュレータのソースコードもあるので、あまり考えずに切り貼りして作れそう。
とりあえずmlogという名前を付けた。Mochi Log。安易。déjà-vu。
タスクAmlogカーネル物理メモリ領域割当( VRAMアドレス )仮想アドレスタスク名登録( "LOG" )タスクID取得( "LOG" )タスクIDログ出力( "SUCCESS!" )画面出力!タスクAmlogカーネル
画面出力はVRAM(物理メモリ)に書き込む事で行うので、まずカーネルにVRAMメモリ領域の割当を要求する。この機能は実装済み。
ちなみに物理メモリ領域の割り当てはドライバタイプのプロセスしか行えない仕様なので、mlogはドライバとなる。イメージ的にはサーバタイプのプロセスなのだけれど、直接画面出力する機能を付ける為には仕方が無い。
本当はちゃんとビデオドライバ、端末エミュレータサーバに機能分割して、タスク間通信して機能実装するのが正論なのだけれど、それには結局、ファイルサーバの存在が不可欠でイタチゴッコとなるので、我慢。
mlog端末エミュレータビデオドライバログ出力送信先タスクIDはどうやって?画面出力だから送信先タスクIDはどうやって!?!?mlog端末エミュレータビデオドライバ
次に、"LOG"としてタスク名を登録。
後は、タスク名"LOG"を名乗ってるタスクにログ出力メッセージを投げつければ、うまい事画面にログが流れる。
とりあえず、mlogのリポジトリ登録した。
https://github.com/MasterMochi/mlog

MochiOS@仮想ファイルシステムの作成part1

MochiOS@仮想ファイルシステムの作成part1

前提知識無しで作るなんちゃって仮想ファイルシステムpart1。

開発日記です。独り言です。

作業リポジトリ

とりあえず、仮想ファイルシステムをmvfsと名付けてgithubにリポジトリ作りました。
MochiのVirtual(仮想)なFile(ファイル)のSystem(システム)でmvfs!安易!
https://github.com/MasterMochi/mvfs

ユースケース

mvfsにどんな機能を乗せようかと、ユースケースを考えてみる。
登場人物は、ユーザプロセス、mvfs、ビデオドライバ。
ユーザプロセスは、ビデオドライバによってビデオメモリを抽象化したファイルをopen/write/closeしてこのファイルに書き込む事で、画面に文字を出力する。readは割愛。
mvfsは、ファイルとそのアクセス先が誰なのか(タスクID)を管理する。
VGAドライバは、ファイルをmvfsに登録する。
ちょっとシーケンスを書いてみる。
ユーザプロセスmvfsビデオドライバmount( "/vram" )open( "/vram" )open( "/vram" )write()write()vram書込みclose()close()unmount( "/vram" )ユーザプロセスmvfsビデオドライバ
mount
このファイルへのopen/(read)/write/closeは私(シーケンスで言うとビデオドライバ)にスルーパスしてくれよ要求。名前はmountでいいのかな、、、まぁいいよね。
open/(read)/write/close
普通のファイル操作機能。先のmountによってファイルパスとタスクIDが関連づいているので、mvfsはこれを使って、ビデオドライバに機能呼出しをスルーパスする。
unmount
このファイルを削除してくれ。もうスルーパス要らないよ要求。
よし、これで、ユーザプロセスはVGAドライバプロセスのタスクIDなんか知らなくても、mvfsにファイルパスと機能番号を指定してメッセージ送信すれば、ビデオドライバプロセスに要求ができる。
ファイルディスクリプタとかファイル階層の管理とか、後で考えよう。アクセス権とかは、うん、いつか考えよう。

あれ…?

ビデオドライバもユーザプロセスも、mvfsにメッセージ送信するためには、mvfsのタスクID知らないとダメじゃね…?
タスクIDは動的に変わってしまうから、やっぱり、不変情報でタスクIDを引っ張ってくるカーネルの機能が必要だなぁ。
ユーザプロセスkernelmvfs仮想ファイルシステムは私って覚えといてーおい、仮想ファイルシステムのタスクID教えろはは、このIDでございます。ユーザプロセスkernelmvfs
こんな感じの。
なんか仮想ファイルシステムでやりたい事をkernelの機能でもやってしまってる感が否めないけれど、仕方が無いよねぇ。
まぁ、kernelはシンプルに小さく作りたいので、単純に名前とタスクIDの辞書だけとしよう。仮想ファイルシステムは、名前を階層化できるし、アクセス制限やらインタフェースの抽象化やら色々できるファイルシステムの共通処理部なので。
これで、機能の住み分けも付いた。
まずは、kernelの機能拡張からだなぁ。

MochiOS@仮想ファイルシステムの作成part0

MochiOS@仮想ファイルシステムの作成part0
開発中の自作カーネルであるMochi Kernelは、プロセスを起動したり、プロセス(タスク)間通信ができたり、デバイスI/O操作が出来る様になったりと、ちょっとまだ自由は効かないけれど、簡単なシステムを作れるようになってきた。
以前、試しに、Mochi Kernel上でSLコマンドを無限ループさせるデモを作った。
MochiKernel上で動くSLコマンド
このデモは、ビデオドライバ、端末エミュレータ、TTY、SLコマンドの4つのプロセスをカーネル上で動かし、プロセス間でメッセージをやり取りする事で、ただ80*25のテキスト画面上にSLを走らせる。
SLコマンドTTY端末エミュレータビデオドライバ指定行列番号に文字出力ビデオメモリ書込みloop[ 無限 ]SLコマンドTTY端末エミュレータビデオドライバ
いい感じに動いてるけど、実装はかなり汚い。たまにバグって止まるし。
無理やり実装している事の1つは、メッセージ送信先の指定。
メッセージはタスクIDを指定して送信するが、ビデオドライバや端末エミュレータなどのタスクIDは起動した順番で割り当てられる。
システムを作り上げた私は、起動させる順番を把握しているのでタスクIDは分かっていて、ソースコードにタスクIDを直接記載している。が、これは汎用的でない。
普通、HelloWorld!プログラムを書くのに出力先ドライバのプロセス番号を指定する必要はない。printf(“HelloWorld!\n”)で済む。
linuxはどうやってこれを解決しているかというと、ファイルシステム。
(linuxはマイクロカーネルではないので、厳密にはやらせたい事に違いがあるが。)
printfは標準出力に文字列を書いていて、標準出力の実体は、大体、/dev/pts/0とかいうファイル。
このファイルに書き込むとファイルシステムがうまい事ビデオドライバに橋渡ししてくれるというわけ。たぶん(かなり説明端折ってる)
じゃぁ、ファイルシステムを作って、そのファイルに書き込む事でビデオドライバにメッセージを送ろうそうしよう。
ファイルシステムというと、FAT32やらEXT4やらNTFSやら、色々なファイルシステム用語を思い浮かべるが、これらは単にフォーマットの話なので、少しやりたい事と違う。でも、今回作るファイルシステムを応用して今後対応できるようにしたい。
というわけで、よく知りませんが、仮想ファイルシステムを作ることとする。ファイルシステムの基礎となるような部分を仮想ファイルシステムとして作っておけば、今後の拡張も用意にできそう。
イメージ:
SLコマンド
仮想ファイルシステム
FAT32
EXT4
NTFS
ビデオドライバ
端末エミュレータ
TTY
次回に続く。。。

Rust@はじめてみた(インストール)

Rust@はじめてみた(インストール) 仕事が落ち着いて、年も明けたので、新しいことを始めたい。 そういえば、Rustという言語があるということは知っていたけれど、何も知らないなと思ってちょっとやってみるか!と。C言語大好き人間だけど、...