マルチタスクモニタ


まだ、Windowsというものが無かった頃の話です。
そこのころの一般向けソフトウエア開発のプラットフォームは、
MS-DOS(DOS/V)ベースのものが主流でした。
MS-DOSは、WindowsのようなマルチタスクのOSではないので、
ソフト開発上必ず問題となることがありました。
それは、MS-BASICなどで作ったユーザーインターフェイスの裏で、
ホストやサーバとの通信を同時進行的に行いたい。
というものです。
そこで必要となってくるのが、複数のEXEファイルを時分割実行できる簡単な
オペレーティングシステムです。オペレーティングシステムといっても、
MS-DOSアプリケーションを複数起動しなければならないので、
当然ながらMS-DOS互換で無ければなりませんでした。
こうしてFritzのマルチタスクモニタ「GEN16」が誕生したわけです。
このページは、古き良きアセンブラ時代の産物を保存するためのに作った
ページなのですが、現在でも組み込みシステムなどで、DR-DOSなどの
MS−DOSの流れを汲むOSが使われているのも事実なので、
何かの役にたてばと思い、あえてこの古いOSを掲載することにしました。

ダウンロードNOW!(全ソース & サンプルプログラム & 実行環境)



【GEN16最低マシンスペック】

機種   IBM PC/AT互換機 または NEC PC-9801 シリーズ
CPU  Intel i386以上
メモリ  基本640KBあればOK
OS   MS-DOS Ver 3.x 以上

【開発環境】

MASM Version5.0
MS−C Version6.0

【プログラム作成時の注意事項】

・コンパイラのスイッチについて
コンパイラのスイッチには、必ず以下の設定をしてください。この設定をしない
場合、アプリケーションが正常に動作しない可能性があります。

コンパイルスイッチ: /ALu
コンパイル例   : cl /ALu /c file_name.c

・リンク時の注意事項
Generic16システムの各プロセスは、各EXEファイルに予め割り当てられた
スタック領域を分けあって使用します(実際はalloca関数を使って各プロセスのスタックを
割り当てています)。そのため、リンク時には大きめのスタック領域を設定する
必要があります。

リンクオプション: /stack:20000
リンク例    : link /stack:20000 filename,,,genclib.lib;
【Generic16カーネルの技術的資料】

・Generic16カーネルは以下の割り込みベクタを使用します。

リクエスト受付ベクタ F0h
タイマー割り込みベクタ 1Ch

・Generic16カーネルの各種サービスに於ける最大値を示します。

最大プロセス数 = 32
IPC_POSTで一度にバインドできるプロセス数 = 32
IPC_POSTでCreateできるキーの最大数 = 32
Exportできるアドレスの最大数 = 255
タイマーエレメント最大数 = 32
セマフォ最大数 = 32

・Generic16カーネルが参照しているMS−DOSファンクションについて

Generic16カーネルは、未公開のものも含めて幾つかのMS−DOS
ファンクションを使用していますが、以下に特に重要と思われる情報について
記述します。

PSPの再設定として ah=50h, INT 21h
ディスク転送アドレスの再設定として ah=1ah, INT 21h
PSPの取得として ah=51h, INT 21h
ディスク転送アドレスの取得として ah=2fh, INT 21h
IN-DOSフラグ・致命的エラーアドレスぜ萋 ah=34h, INT 21h




【GEN16 APIリファレンス】



シンボル 名 称
_checkstay カーネル常駐有無判定
CreateThread プロセス登録
dispatch 1サイクルSleep
getpid 自プロセスid取得処理
kernel_start カーネル制御開始
kernel_quit カーネル制御終了
getstate プロセス状態取得
setstate プロセス状態設定
kill プロセス削除
export アドレスエクスポート
import アドレスインポート
unexport エクスポートされたアドレスの破棄
ipc_pinit ポスト初期化
ipc_post ポスト通知
ipc_precv ポスト受信
TimerSet タイマー設定
TimerGet タイマー設定値取得
ipc_semget セマフォ取得
ipc_semop セマフォ操作
kbd_scan キー入力待ち
kbd_input キー入力
sleep プロセススリープ
modeget カーネル動作モード取得
TimeSlice 時分割平行処理開始・停止
getpinfo プロセス情報取得


Title : _checkstay (カーネル常駐有無判定)

int _checkstay( void )

Generic−16カーネルが常駐していれば 0 を返します。
常駐していない場合は -1 を返します。

Title : CreateThread (プロセス登録)

int CreateThread(char *func,char *stack,int stacklen)

( IN) func   = プロセス(関数)のアドレス
( IN) stack  = プロセスに割り当てるスタックのアドレス
( IN) stacklen = プロセスに割り当てるスタックのサイズ

処理成功時は、-1以外の値(プロセスID)を返します。
処理失敗時は、-1を返します。

Title : dispatch (1サイクルSleep)

void dispatch( void )

この処理を呼び出したプロセスは、レディー状態のまま実行権を他の
プロセスに譲ります。無限ループなどでシステムのパフォーマンスが落ちる
場合などに定期的に他プロセスを起動するために使用します。

Title : getpid (自プロセスid取得処理)

int getpid( void )

自プロセスのpidを返します。この関数を、割り込みルーチン内などで
使用した場合は、現在実行中のプロセスのidが返ります。
処理成功時は、-1以外の値(プロセスID)を返します。
処理失敗時は、-1を返します。

Title : kernel_start (カーネル制御開始)

void kernel_start( void )

Generic−16のプロセスモニタリング処理が開始されます。
この関数を実行した直後は、タイムスライス(時分割平行処理)が
行われない設定になっています。タイムスライスによるマルチタスク処理を
開始したい場合は、 TimeSlice()関数によってタイムスライス開始要求を
発行してください。

Title : kernel_quit (カーネル制御終了)

Void kernel_quit( void )

カーネルの処理を中止し、kernel_start()関数を発行した直後の行へ
処理を返します。

Title : getstate (プロセス状態取得)

int getstate( int pid )

( IN) pid = 対象となるプロセスのid

pidで指定されたプロセスの状態を返します。状態が0の場合は、
そのプロセスは実行可能状態です。0以外の場合は、何等かの要因で
待ち状態に入っています。
処理成功時は、-1以外の値(プロセス状態)を返します。
処理失敗時は、-1を返します。

Title : setstate (プロセス状態設定)

int setstate(int pid, int state)

( IN) pid = 対象となるプロセスのid
( IN) state = プロセス状態

getstate()関数で取得した状態を変更します。ただし stateの下位8ビット
はシステムで予約していますので、絶対に変更しないでください。上位8
ビットはユーザーに開放されていますので、自由に設定できます。プロセス
状態が0以外になっているプロセスは、スケジューリングから除外されます。
処理成功時は、-1以外の値を返します。処理失敗時は、-1を返します。

Title : kill (プロセス削除)

int kill( int pid )

( IN) pid = 対象となるプロセスのid

pidで指定したプロセスが消去されます。自プロセスに対してkill()関数を
使用した場合、自プロセスの情報削除後、即座に他プロセスへ実行権が移り
ますので、kill()関数より後の行は実行されません。また、ポストを取得し
ている場合は、自動的に開放されます。
処理成功時は、-1以外の値を返します。処理失敗時は、-1を返します。

Title : export (アドレスエクスポート)

int export(int key, char *addr)

( IN) key = キー値
( IN) addr = エクスポートするアドレス

EXEファイル間でデータの交換がしたい場合や関数を相互に呼び出したい
場合に使用します。addrで指定するアドレスには、スタック領域のアドレス
を指定しないほうが良いでしょう。処理成功時は、-1以外の値を返します。
処理失敗時は、-1を返します。

Title : import (アドレスインポート)

char *import(int key)

( IN) key = キー値

export()関数で指定したキー値と一致するキー値を指定すると、そのキーに
対応するアドレス値が返却されます。処理成功時は、NULL以外の値を返しま
す。処理失敗時は、NULLを返します。

Title : unexport (エクスポートされたアドレスの破棄)

int unexport(int key)

( IN) key = キー値

キー値で指定されたエクスポート情報を破棄します。
処理成功時は、-1以外の値を返します。処理失敗時は、-1を返します。

Title : ipc_pinit (ポスト初期化)

int ipc_pinit(int key)

( IN) key = キー値

キー値に対応するポスト領域を取得します。初めて使用されるキー値の
場合、ポストバッファの初期化が行われます。既に他のプロセスで初期化
されているポストバッファに対して ipc_pinit()関数を実行すると、既存の
ポストバッファに自プロセスのidをバインドします。処理成功時は、
-1以外の値を返します。処理失敗時は、-1を返します。

Title : ipc_post (ポスト通知)

int ipc_post(int key, int mesg)

( IN) key = キー値
( IN) mesg = 送信メッセージ

キー値に対応するポストバッファにメッセージを書き込みます。この時、
ipc_pinit()関数を使用してバインドしたすべてのプロセスにメッセージが
届きます。処理成功時は、-1以外の値を返します。処理失敗時は、-1を
返します。

Title : ipc_precv (ポスト受信)

int ipc_precv(int key, int mask, int ntype)

( IN) key = キー値
( IN) mask = 受信マスク
( IN) ntype= 受信動作指定

キー値に対応するポストバッファからメッセージを受信します。未だ
メッセージが届いていない状態でこの関数を実行すると、プロセスが待ち
状態になり、メッセージが送信されるまで待機します。受信マスクは、
ポストされるメッセージを限定する際に使用します。例えば、送信側が
メッセージ(0x01)を送信し、受信側では受信マスクとして
(0x02)を指定していた場合、受信側にはメッセージが届きません。
送信側が0x03や0xFF等のように0x02のビットを含んだ
メッセージを送信した場合、受信側には0x02のみが返却されます。
残ったビットは保留され、次に受信側が残りのビットを受信しようとする
まで残ります。処理成功時は、受信したメッセージの値を返します。
処理失敗時は、-1を返します。
受信動作指定(ntype)には、以下の値を設定できます。

PRECV_NOWAIT = 受信メッセージが無い場合エラー復帰
PRECV_WAIT = 受信メッセージが届くまで待つ

Title : TimerSet (タイマー設定)

int TimerSet(int elem,int tick,int post_key,int post_code)

( IN) elem = タイマーエレメント番号
( IN) tick = タイマー間隔(1=50msec)
( IN) post_key = タイムアウト通知ポストキー
( IN) post_code = タイムアウト通知コード

elemで指定したタイマー(0〜31)を設定します。tickは50msec
単位で何回カウントする毎にメッセージをポストするかを指定します。
タイムアウト自には、post_keyで示されるポストキーにpost_codeで指定
されたメッセージが送信されます。処理成功時は、-1以外の値を返します。
処理失敗時は、-1を返します。

Title : TimerGet (タイマー設定値取得)

int TimerGet(int elem, struct _tmgetinf far *pbuf)

( IN) elem = タイマーエレメント番号
(OUT) pbuf = タイマー情報構造体

elemで指定したタイマー(0〜31)の設定状態を返します。
タイマー構造体のメンバーは以下の通り。

struct _tmgetinf {
int nkyT ; /* 受信者 IPC_POSTキー */
int nCdT ; /* 受信者 IPC_POSTコード */
int norg ; /* タイムアウト叩癖歛戸僉 */
int ncur ; /* カレントタイマー値 */
} ;

Title : ipc_semget (セマフォ取得)

int ipc_semget(int key, int max)

( IN) key = セマフォキー値
( IN) max = セマフォ最大値

セマフォを作成する。
処理成功時は、-1以外の値を返します。処理失敗時は、-1を返します。

Title : ipc_semop (セマフォ操作)

int ipc_semop(int key, int cmd)

( IN) key = セマフォキー値
( IN) cmd = セマフォ操作命令

キー値で指定されたセマフォに、セマフォ操作命令で指定された操作を
行います。操作命令として指定できるのは以下の通り。

SEM_LOCK セマフォ増加
SEM_UNLOCK セマフォ減少
SEM_NWLOCK 待ち無しでセマフォ増加
SEM_LOAD セマフォ状態取得

1) SEM_LOCK / SEM_NWLOCK

セマフォを増加します。ipc_semget()関数で指定した maxサイズに
なるまで増加できますが、maxを越えて増加しようとした場合、
SEM_LOCKでは他プロセスがセマフォを減少させるまで待ち状態に
入り、SEM_NWLOCKでは、エラー(-1)復帰します。

2) SEM_UNLOCK

セマフォを減少させます。セマフォは0以下の値に成る事はありません。

3) SEM_LOAD

セマフォの現在の値を返します。

Title : kbd_scan (キー入力待ち)

int kbd_scan( void )

キーボードの入力が有る場合1を返します。キー入力が無い場合0を
返します。

Title : kbd_input (キー入力)

int kbd_input( void )

キーボードから文字を1文字入力します。何もキーボードを押していない
時に本関数を呼び出すと、待ち状態になりますが、他プロセスへは制御を
渡さないので、注意してください。

Title : sleep (プロセススリープ)

void sleep( int cnt )

( IN) cnt = ディスパッチ回数

cntで指定された回数分他プロセスへ実行権を回します。

Title : modeget (カーネル動作モード取得)

int modeget( struct _modgetbuf *pbuf)

(OUT) pbuf = カーネル状態バッファ

カーネルの現在の動作状態を返します。
カーネル状態バッファの内容は以下の通り。

struct _modgetbuf {
int nmacid ; /* 動作機種 1=PC_9801, 2=IBM_PC */
int ntmslice ; /* タイムスライス楊 */
int nkernel ; /* カーネル動作状態 0=停止,1=動作中,2=終了中*/
int nmaxtss ; /* 登録可能最大プロセス */
int nmaxpost ; /* 登録可能最大POST数 */
int nmaxexp ; /* 登録可能最大EXPORT数 */
int nmaxtimer ; /* 登録可能最大タイマー数 */
int nmaxsemafo ; /* 登録可能最大セマフォ数 */
} ;

Title : TimeSlice (時分割平行処理開始・停止)

void TimeSlice( int flag )

( IN) flag = タイムスライス有無フラグ

このフラグに1を指定すると、時分割平行処理がスタートします。
0を指定すると、時分割平行処理をストップします。

#define TMC_TMCLON 1 /* Time Slice Start */
#define TMC_TMCLOF 0 /* Time Slice Stop */

Title : getpinfo (プロセス情報取得)

int getpinfo(int pid, struct _pinfo *pbuf)

pid = 対象となるプロセスのid
pbuf = プロセス情報バッファ

指定したプロセスの情報をプロセス情報バッファに書き込みます。
プロセス情報バッファの構造は以下の通り。

struct _pinfo {
int nptyp ; /* プロセスタイプ */
long nstack ; /* スタックフレームアドレス */
int npid ; /* PID */
int psp ; /* PSP */
long dta ; /* DTA */
} ;




 戻る