UNIXv1における制御文字を用いたclearコマンドの作り方

はじめに

本記事は「日本陰キャ協会 Advent Calendar 2022」における12日目の記事になります。

qiita.com

目次

動作確認に関して

今回の方法はManjaroLinuxにおけるkonsoleにて動作確認を行いました。また、制御文字に依存しているため、全ての端末で動作するとは限りません。

UNIXv1とは

UNIXv1とは1971年に公開されたUNIXの1st Editionとなります。また、特徴としてマルチタスキング、マルチユーザー、階層型ファイルシステム持っており、現代のOSにつながる要素を持っています。

制御文字とは?

制御文字とは、文字コードで決められた周辺機器の制御などに用いる特殊な文字のことです。今回利用するのは、ターミナルを制御するエスケープシーケンスとなります。

clearコマンドとは?

端末の画面をクリアにするコマンドになります。

処理の流れ

処理の流れは以下のようになります。

last1120cを用いた実装

下記コードが全体のソースコードになります。

/* clear command */

main(){
  extern clear, rcur;
  clear();
  rcur();
  return;
}

/* clear:screen clear */
clear(){
  extern esc;
  esc("2J");
}

/* rcursor: reset curosor */
rcur(){
  extern esc;
  esc("H");
}

/* esc */
/* code:escape sequences code */
esc(code)
char code[];
{
  extern printf;
  printf("%c[%s", 033, code);
}

esc関数

エスケープシーケンスを発行する処理を行う関数です。033を1バイトの8進数として表示をし、文字列として続くcodeを出力します。

/* esc */
/* code:escape sequences code */
esc(code)
char code[];
{
  extern printf;
  printf("%c[%s", 033, code);
}

clear関数

画面をクリアするエスケープシーケンスを出力する処理を行う関数です。"\033[2J"を出力させます。

/* clear:screen clear */
clear(){
  extern esc;
  esc("2J");
}

rcur関数

カーソル位置を0行目の0文字目に移動する処理を行う関数です。 "\033[H"を出力させます。

/* rcursor: reset curosor */
rcur(){
  extern esc;
  esc("H");
}

使ってみる

おやなにかの木が...。

見ていると心が傷んでくるので消しておきましょう。

これで心の安寧は保たれました。

まとめ

クリスマスは消えました!!!

PS)
消しそこねた人は「クリスマス?なにそれ美味しいの?」を聞きながら当日を過ごしましょう。

www.nicovideo.jp

参考文献

The Restoration of Early UNIX Artifacts
https://www.usenix.org/legacy/event/usenix09/tech/full_papers/toomey/toomey.pdf:

FreeBSD Manual Pages - clear
https://www.freebsd.org/cgi/man.cgi?query=clear&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html

プログラミング色々
http://7ujm.net/etc/esc.html

UNIXv1の操作方法に関して

はじめに

本記事では、初期UNIXであるResearch Unix version1(以降UNIXv1)を操作するための基本的な知識、操作方法および注意点を記載する。

前回の記事

no000.hateblo.jp

もくじ

UNIXv1のディレクトリ構造

図1ではUNIXv1におけるディレクトリ構造を図に起こしたものである。tmpディレクトリの中身は最初から存在するutmpとedコマンドで利用されるetm* を記載している。使用するプログラムによっては他のtmpファイルが生成されることもある。

図1 Unixv1のディレクトリ構造

プロセス

図2は自作したpsコマンドの出力結果である。ここではSLOTが16個までしかないと疑問を感じると思う。これは、UNIXv1が16個までのプロセスしか管理できないためそのような表記となっている。また、基本的にSLOT1はinitに、SLOT2~10は端末、システムコンソールに利用されているため、残り6個のSLOTがユーザーによって利用できるプロセスとなる。

図2 psコマンドの出力結果

シェルの操作方法

ログイン

rootユーザーログインをしたい場合はlogin:の際にrootと入力、ユーザーログインをしたい場合はlogin:の後にユーザー名を入力する。

ユーザーken

root

ログイン可能なデフォルトのユーザーは以下となる。

ken
jack

ログアウト

Ctrl-d(EOF)を押すことでログアウトができる。

ディレクトリの移動

chdirコマンドを利用して移動する。

@ chdir ..

最後が/(スラッシュ)で終わるとエラーとなる。

@ chdir ../
Bad directory

ディレクトリ内部のリスト

lsコマンドを利用することで取得ができる。

@ ls
as2
getty
glob
init
msh
passwd
std0
suftab
uids
@

ls -alもサポートされている

@ ls -al
total   34
104 sdrwr-  2 root    110 Jan  1 00:00:00 .
 41 sdrwr-  7 root    110 Jan  1 00:00:00 ..
106 lxrwr-  1 bin    5778 Jan  1 00:00:00 as2
105 sxrwr-  1 bin     446 Jan  1 00:00:00 getty
107 sxrwr-  1 sys    2662 Jan  1 00:00:00 glob
108 sxrwr-  1 sys    1192 Jan  1 00:00:00 init
109 sxrwr-  1 sys     186 Jan  1 00:00:00 msh
110 s-rw--  1 sys     299 Jan  1 00:00:00 passwd
111 s-rwr-  1 root    512 Jan  1 00:00:00 std0
112 s-rwr-  1 bin    2082 Jan  1 00:00:00 suftab
113 s-rwr-  1 sys      96 Jan  1 00:00:00 uids
@

プロンプト

rootユーザーの場合#、userユーザーの場合@がプロンプトとして表示がされる。

ユーザーken

root

逐次処理

コマンドの逐次処理は;(セミコロン)を利用してできる。

@ ls; ls

同時実行

&を利用して同時実行を行わせることができる。

@ ls & ls

バックグラウンドプロセス

入力の最後を&にしてコマンドを実行した場合、バックグラウンドプロセスとして稼働させることができる。

@ ls &

注意してほしいのが、バックグラウンドプロセスを無限ループで稼働させた場合、バックグラウンドプロセスの数によってはシステムがハングアップするということである。

リダイレクト

現在におけるリダイレクトもある。ファイル名の先頭2文字を特殊扱いすることで動作することに注意。

標準出力

@ ls >file

標準入力

@ cat <file

文字列

"(ダブルクォーテーション)、'(シングルクォーテーション)によって表現ができる。

@ echo "hello"

文字の削除

文字の削除は#を使い入力を行う。画面上では#として入力されているように見えるが、システム内部では1文字削除されたものとして扱われている。もし、#を入力したい場合は\#エスケープシーケンスを利用して入力する。

@ echo "hello#####unix"
unix 

行の削除

行単位で削除する場合は@を利用する。これが入力された場合、行頭から@が入力された箇所までの入力が無効化される。@を入力したい場合は\@エスケープシーケンスを利用する。

@ echo "hello"@echo "unix"
unix 

割込み

無限ループプログラムなどを稼働させた場合に中止させたい場合、ASCII DELを入力することで中止することができる。現代のキーボードではBackSpaceやDelキーを入力することでASCII DELの送信ができる。実行している方法は割とトリッキーな方法であり、おもしろい。

正規表現

初期の単純な正規表現として? * が存在する。
?は一文字マッチとなる

@ ls ma?i.s
maki.s
@

*はワイルドカードとなる。

@ ls *.a
bilib.a
filib.a
liba.a
libb.a
libc.a
libf.a
@ 

その他注意事項

扱えるプロセスの制限

プロセスは端末のプロセスを合わせて16個までしか実行可能状態にできない。もし、それ以上のプロセスを立ち上げようとした場合

@ ls
Try again

という警告が出力され、実行することができない。端末、initのプロセスは1~10までのプロセスとして常に利用しているため、残りの6個が扱えるということになる。

動かしているカーネルイメージおよびバイナリに関して

今回動かしているUNIXv1のイメージは純粋なUNIXv1ではない。コマンドにはUNIXv2のバイナリが利用されており、それを利用するためにUNIXv1にはパッチが施されている。また、C言語を動作させるためにユーザー空間の拡張が行われている。

マニュアルをダウンロード

下記の場所に公開がされている。

UNIXv1

https://www.tuhs.org/Archive/Distributions/Research/Dennis_v1/UNIX_ProgrammersManual_Nov71.pdf

もし、OCR化された文書が欲しい場合、以下の場所に公開されている。各章に分かれているのはpdfuniteコマンド等で連結すれば便利である。

Unix Manual, first edition

UNIXv2

実行される基本コマンドはUNIXv2のものであるため、コマンドなどはこちらを参照した方が良い。

https://www.tuhs.org/Archive/Distributions/Research/Dennis_v2/unix_2nd_edition_manual.pdf

最後に

次回は基本的なコマンドの利用について記事にする予定である。

参考

次回

未公開

UNIXv1のエミュレーション環境の構築

はじめに

本記事では、初期UNIXであるResearch Unix version1(以降UNIXv1)をPDP-11/20のエミュレーションが可能なsimhを利用し環境構築する手法を説明する。

目次

説明する環境に関して

今回環境構築をするにあたってUbuntu 20.04.4 LTS x86_64を用いて環境構築を行った。

作業ディレクトリを用意

$ mkdir UNIXv1
$ cd UNIXv1

simhのビルド

simhをソースコードからビルドする。

ビルドに必要なパッケージの準備

$ sudo apt install build-essential
$ sudo apt install libpcap-dev
$ sudo apt install bridge-utils
$ sudo apt install uml-utilities

simhの準備

まず、エミュレータであるsimhをビルドする。simhはgithubにおけるリポジトリ(https://github.com/simh/simh)で開発がされている。よって、このリポジトリを任意のディレクトリにダウンロードする。また、この記事を書いた時simhのmasterはversion4である。

$ wget https://github.com/simh/simh/archive/refs/heads/master.zip -O simh.zip
$ unzip simh.zip

simhのビルド

simhディレクトリ内に入り、ビルドを行う。

$ cd simh-master
$ make pdp11

ビルドされた実行ファイルはsimh-master/BIN/pdp11として存在する。

イメージの準備

イメージのダウンロード

UNIXv1ディレクトリに戻り、イメージ関連の圧縮ファイルがダウンロードする。

$ wget https://www.tuhs.org/Archive/Distributions/Research/Dennis_v1/images-20080625.tgz -O V1image.tgz
$ mkdir V1image; tar -xzvf V1image.tgz -C V1image --strip-components 1

ディレクトリの構成が以下のようになっているかを確認する。

UNIXv1
├── V1image
├── V1image.tgz
├── images-20080625
├── pdp11
├── simh-master
└── simh.zip

この際、simh.zipは削除しても大丈夫だが、イメージ内の環境を壊してしまった場合やリセットしたい場合のためにV1image.tgzは保持しておくことをお勧めする。

UNIXv1の起動

simh.cfgを起動スクリプトとして渡し、UNIXv1を起動する。

$ cd V1image
$ ../simh-master/BIN/pdp11 simh.cfg 

以下のような表示がされた場合UNIXv1の起動には成功している。

初期セットアップ

rootでログインを行う。ここからは、UNIXv1内での作業に移る。以降はバックスペースが効かないため注意すること。もし間違えた場合、@を行末に追加しEnterを押すことで入力を無効化することができる。

login: root
root
# ls -@
# 

セットアップスクリプトと実行

tmpディレクトリに移動する

chdir tmp

cat コマンドを利用し、set.shを作成する。chdir /で始まる内容を入力するにはクリップボードを使用すれば良い。また、入力後に改行してからCtrl-dを押せば確定する。

# cat >set.sh
chdir /
chmod 017 tmp
ls -al
chdir /usr
chown 6 ken
ls -al
chdir /usr/src/lib
as crt0.s; mv a.out crt0.o
mv crt0.o /usr/lib/crt0.o
# 

set.shを実行する。

# sh set.sh

set.shでやっていること

セットアップスクリプトで実行していることを説明する。

tmpディレクトリの権限書き換え

一般ユーザーでもedコマンド等を利用できるようにtmpディレクトリの権限を変更する。rootユーザーでtmpディレクトリの権限を変更する。

# chdir /
# chmod 017 tmp

ls -alを使って権限が変更されたかを確認する。

# ls -al
...
114 sdrwrw  2 root    120 Jan  1 00:00:00 tmp
...

/usr/kenの所有者の変更

kenディレクトリ内でユーザーkenによるリダイレクトを利用できるようにする。そのために/usr/kenの所有者をrootからkenに変更する。こちらでも、rootユーザーで所有者を変更する。

# chdir /usr
# chown 6 ken

ls -alを使い確認

# ls -al
...
 57 sdrwr-  2 ken      70 Jan  1 00:00:00 ken
...

C言語の実行起動ルーチンの準備

/usr/src/lib/crt.sをビルドし適切な場所に配置する必要がある。まず、/usr/src/libに移動する。

# chdir /usr/src/lib

次に、ビルドをする。

# as crt0.s; mv a.out crt0.o

最後に、アセンブルしたファイルを/usr/libに配置する。

# mv crt0.o /usr/lib/crt0.o

C言語が動作するか確認

次の作業をするためにCtrl-dを押しログアウトをする。すると、プロンプトがでてくるため、そこにkenと入力しユーザーkenでログインする。その後、catコマンドを利用しソースファイルを作成する。

@ cat >hello.c

このコマンドを入力した時点で入力待ちになるため、以下のコードを入力する。

@ cat >hello.c
main(){
  extern printf;
  printf("hello UNIXv1\n");
  return;
}

入力後は改行をし、Ctrl-dを押すことで確定する。その後、コンパイルをする。

@ cc hello.c

この際、コンパイルできていればセットアップは完了である。実行ファイルであるa.outファイルが生成されるので実行する。

@ a.out
hello UNIXv1

停止のさせ方

Ctrl-eを押すことでsimhのコマンドモードに移行する。

# 
Simulation stopped, PC: 007332 (MOV (SP)+,25244)
sim> 

ここでquitコマンドを入力することで終了する。

# 
Simulation stopped, PC: 007332 (MOV (SP)+,25244)
sim> quit
Goodbye
%SIM-INFO: RF: writing buffer to file: rf0.dsk

最後に

今回は、UNIXv1をエミュレータで実行するための環境構築に関して解説した。他にもdockerイメージなども公開されているため調べてみてほしい。次回は、UNIXv1の操作方法に関しての記事を予定している。

次回

未公開

no000.hateblo.jp

自作OSに関する記事のリンク

はじめに

もうちょい整理してとかあったらおしえてね。

目次

ブートプロトコル

multiboot2

wiki.osdev.org

bootboot

wiki.osdev.org

gitlab.com

Stivale1 Stivale2

wiki.osdev.org

SMBIOS

Wiki

UEFIだとGUIDで開けるため、アンカーをメモリ上が探す必要はない。

wiki.osdev.org

仕様書

2.xと3.xには違いがあるので注意 www.dmtf.org

実装に関する参考資料

LibGetSmbiosString関数を見るといいかも。あとは、仕様書でdouble nullに関して調べる。

github.com

いろんなOS

GitHubとかで実装を探してる時に見つけたやつ

learn-os

github.com

EggOS

Go言語のx86カーネル(gccgoじゃないっぽい?珍しい)

github.com

TomatOS

github.com

skiftOS

ビルド時の注意

github.com

github.com

Minoca OS

github.com

CurrOS

github.com

luakernel

github.com

FlorenceOS

Zig言語で書かれてるっぽい

github.com

MITTOS64

github.com

Suckless Operating System

github.com

ClamAVメモ

目次

メモ

freshclamが正常に動作をしているかを確認

/var/log/clamav/freshclam.logを確認

clamdscanでpermissin deniedになる

--fdpassオプションを付ける。(ファイルの権限とデーモンの権限の問題?)

現在作っているブートローダに関して

Table of Contents

  1. はじめに
  2. どのようなブートローダを目指しているのか?
  3. 現在の見た目
  4. 動作に関して
  5. どのようにしたか?
    1. 表示の遅延に関して
    2. statusのみの色を変えたことに関して
    3. ロゴの表示に関して
  6. 妙に引っかかった箇所
    1. ウォッチドッグタイマの無効化忘れ
  7. 今後に関して
  8. 書き終えて

はじめに

メリークリスマス!適当に入れたらクリスマスイブ担当となった猫(1010)です。ちなみにクリスマスはアドベントカレンダーを書いて、夕方にセキュリティキャンプのチームとRust入門をやり、深夜にVRChatをやるといった感じですね。充実しているんだかしてないんだか…。

仕切り直して、最近の近況といきます。今年は色々変化があった年でした。しかし、もう終わりとなると感慨深いものがあります。11月ごろから、セキュリティ・キャンプ全国大会2020onlineにYトラックOS自作ゼミとして参加をしてきました。同じような趣味の方にであうことができて非常に楽しい経験だったことを覚えています。また、セキュリティキャンプを通してブートローダを作成していました。セキュリティ・キャンプ中には完成はしなかったのですが(計画性の破綻)、期間中に作るためのイメージはついたのでチョコチョコ作業をしております。

どのようなブートローダを目指しているのか?

  • おしゃれ
  • できる限りの情報をじっくり確認できる
  • ELF形式のカーネルをブート

これらを達成できるといいなぁと考えながら作成をしています。

特にできる限りの情報をじっくり確認できるという点に関しては、以前からブートローダがスッ…と終わってしまい。ブートローダー何やっとるん?といった感覚になっていたというのが理由としてあります。要はLinuxのブート時のログのように表示できれば面白そうってことですね。ですが、Linuxのログも表示が早いため、なんか悲しいです。まぁログファイルが残るといえば早くてもいいだろうという感覚やブート時に遅かったらダルいと考えると理解できます。ですが、自作のブートローダーくらいじっくり見たいのです!

あとは、開発するにあたりはじめはフルスクラッチで書こうとしていました。ですが、ヘッダーファイルを書くのが結構大変ということもあり、現在ではEDK2を使っています。

現在の見た目

f:id:No000:20201224235220j:plain
このような感じで、Linux風のログを表示します。変わっている点としては、ELFヘッダの内容を一部表示させている点ですね。現状としては普通の自作のブートローダでも出力している内容に近いですが、色を変更したりしてわかりやすくすることと情報の量を増やしていこうと考えています。また、ロゴに関してはアスキージェネレータで生成したものをPrint()で愚直に表示をさせています。

ちなみに、動画バージョンは以下に

動作に関して

『どのようなブートローダを目指しているのか?』でも述べましたが、表示をじっくり見ることができるように画面出力の合間にStall()を利用して時間差をつけています。また、'''Kernel boot(press RET)'''の箇所では、Retrunを押すと処理が始まるようにしています。

どのようにしたか?

それぞれの調整はほんとに愚直にやりました。

表示の遅延に関して

以下のようにしています

SystemTable->BootServices->Stall(n);

nの箇所をマイクロ秒として仕込んであげることで遅延を起こすことができます。自分は、500000にしています。

statusのみの色を変えたことに関して

SetAttribute()を利用して色を変えていました。

SystemTable->ConOut->SetAttribute(SystemTable->ConOut, EFI_LIGHTRED);

上記コード例では、ライトレッドの色合い色合いにしています。このEFI_LIGHTREDはありがたいことにEDK2側で定義されており

edk2/MdePkg/Include/Protocol/SimpleTextOut.h

にて定義されています。git grepコマンド等で探せば早いかと思います。

ロゴの表示に関して

これは以下のURL先のASCIIジェネレータを利用して生成したものをPrint()で出力させています。
partorjk
これを使って以下のように、出力させると表示できました。

  Print(L" __       __                        __       ______    ______  \n");
  Print(L"/  |  _  /  |                      /  |     /      \\  /      \\ \n");
  Print(L"$$ | / \\ $$ |  _______   ______   _$$ |_   /$$$$$$  |/$$$$$$  |\n");
  Print(L"$$ |/$  \\$$ | /       | /      \\ / $$   |  $$ |  $$ |$$ \\__$$/ \n");
  Print(L"$$ /$$$  $$ |/$$$$$$$/  $$$$$$  |$$$$$$/   $$ |  $$ |$$      \\ \n");
  Print(L"$$ $$/$$ $$ |$$ |       /    $$ |  $$ | __ $$ |  $$ | $$$$$$  |\n");
  Print(L"$$$$/  $$$$ |$$ \\_____ /$$$$$$$ |  $$ |/  |$$ \\__$$ |/  \\__$$ |\n");
  Print(L"$$$/    $$$ |$$       |$$    $$ |  $$  $$/ $$    $$/ $$    $$/ \n");
  Print(L"$$/      $$/  $$$$$$$/  $$$$$$$/    $$$$/   $$$$$$/   $$$$$$/  \n");
  Print(L"                                                               \n");
  Print(L"                                                               \n");
  Print(L"                                                               \n");

ここで注意するべきなのはエスケープシーケンスです。そこだけを一つずつ修正すると良い感じとなります。

妙に引っかかった箇所

ウォッチドッグタイマの無効化忘れ

Qemuを放っておくと、5分ごとに再起動を起こしており不思議だったのですが、ウォッチドッグタイマを無 効化することで解決しました。

SystemTable->BootServices->SetWatchdogTimer(0, 0, 0, NULL);

今後に関して

今後は選択画面を実装したいと考えています。Clearscreenとprintを高速で繰り返すことで実装することをイメージしており、最初はUEFI shellに行ける選択肢とカーネルブートの選択肢か、ダミーの選択肢を作成するかで迷い中…。あとは、memorymapの取得まで実装が完了しているので、ExitBootServices()周辺を実装してあげれば動くかなと考えています。

書き終えて

気づいた人は少ない、もしくはいないかもしれないですが、いつものブログの書き方とは少し体裁が異なっています。その理由は、emacsのorg-modeで作成したorgファイルをmark-downに変換した後にはてなブログに貼っつけ、微調整をしたからなんですよね。もしかして、私はemacsに盆栽をするようにカスタマイズしまくることが好きなのかな、とか思いながらアドベントカレンダーを書いていました。