可変長メモリプールと固定長メモリプール

メモリ管理機能

今回はT-Kernelのメモリ管理機能について説明します。

T-Kernelは組み込み機器用のリアルタイムOSです。組み込み機器では限られたリソースを有効に活用しなければなりません。その分りやすい例がメモリです。

情報系のOSでは、GB(ギガバイト)単位でメモリを搭載していたり、仮想メモリを利用することを前提にしていたりするため、メモリが枯渇する心配はほとんどありません。ところが、組み込み機器ではそうはいきません。実行中にメモリを確保しようとしても、メモリ不足により獲得できない可能性がありますので、そのような前提でプログラムを開発する必要があります。

そこで、T-Kernelでは、メモリを管理するための機能として以下の2種類を用意しています。

  • ●可変長メモリプール
  • ●固定長メモリプール

今回は、これらの使い方について説明します。

可変長メモリプール

可変長メモリプールは、任意のサイズのメモリブロックを管理するための機能です。C言語のライブラリにmallocという関数がありますが、これに似た機能を提供します。mallocとの主な違いは以下の通りです。

  • ●用途別に複数のメモリプールを用意することができる。
  • ●メモリブロックを獲得できない場合のタイムアウトを指定することができる。

T-Kernelでは複数のメモリプールが利用できますので、たとえば、メモリ不足の際に待ってもよい処理と、メモリ不足による中断が許されないクリティカルな処理に対して、別々のメモリプールを割り当てることができ、クリティカルな処理が他の処理から影響されにくいシステムを構築できます。

図1

【図1:可変長メモリプールとmalloc(1)】

メモリが獲得できない場合、情報系のOSでは、メモリ不足の場合に二次記憶にメモリ内容を退避すること(ページアウト)でメモリの空き領域を確保したり、自動的にガベージコレクションを行ってくれる場合もあります。これらは便利な機能ではありますが、いつメモリが獲得できるのかが分らないまま延々待たされる危険性があるので、リアルタイム性が必要とされる組み込み機器には不向きです。

一方T-Kernelでは、メモリが獲得できない場合の動作を明確に制御できるように、メモリ獲得用のシステムコールにタイムアウト機能が付いています。タイムアウト時間を指定できるので、メモリが獲得できない場合の動作として以下を選択することができます。

  • ●メモリが獲得できなければ即時エラー終了する。
  • ●メモリが獲得できるまで待つ。
  • ●指定された時間待ってもメモリを獲得できなければエラー終了する。

このように、メモリを獲得できなかった場合に、待ち時間の最大値をアプリケーションから制御することで、システム全体としてのリアルタイム性を確保できるようになっています。

図2

【図2:可変長メモリプールとmalloc(2)】

リスト1に、実際に可変長メモリプールを利用した例を示します。

【リスト1:可変長メモリプールの使用例】

									#include <basic.h>
									#include <tk/tkernel.h>
									#include <tm/tmonitor.h>

									#define HIST_NO		(10)					/* 保存する履歴の数  	*/

									#define PROMPT		"> "					  /* プロンプト	      */

																				/* コマンド一覧    	*/
									#define CMD_EXIT	"exit"					/* 終了        	*/
									#define CMD_HISTORY	"history"				/* 履歴表示      	*/
									#define CMD_REQUEST	"request"				/* 変換処理開始要求  	*/
									#define CMD_DISPLAY	"display"				/* 変換結果表示    	*/
									#define CMD_BANG	'#'						/* コマンド置換制御  	*/

									#define TMO_GETBLK	(3000)					/* 不足時の待ち時間  	*/

																				/* 可変長メモリプール 	*/
									#define BUFSZ		(80)					/* 入力用バッファサイズ	*/
									#define MPLSZ		((BUFSZ*HIST_NO)/2)		/* MPL全体のサイズ	  */
														/* ↑ サイズをわざと少なくするため、2分の1にしている */

									LOCAL	ID		mplid;						/* 可変長メモリプールID  */
									
									LOCAL	B*		hist[HIST_NO];				/* 履歴へのポインタ  	*/
									LOCAL	INT		idx;						/* 次の履歴登録位置  	*/
									#define IDX(x)	((x)%HIST_NO)				/* 履歴配列指定用   	*/
									
									IMPORT	char*	strcpy( char *dst, const char *src );
									IMPORT	size_t	strlen( const char *s );
									IMPORT	int		strcmp( const char *s1, const char *s2 );
									
									#define isdigit(c)	(('0'<=(c))&&((c)<='9'))
									
									INT		atoi( B *str )						/* 文字列 → 整数   	 */
									{
										INT		i = 0;
									
										while( isdigit(*str) ){
											i = i*10 + (*str++ - '0');
										}
										return i;
									}
									
									void	itoa3( INT val, B *str )			/* 整数 → 文字列(3桁)   */
									{
										INT		i;
									
										for( i = 2; i >= 0; i-- ){       	   /* 最大3桁      	*/
											*(str+i) = val % 10 + '0';
											val /= 10;
										}
										/* '\0' は追加していないので注意! */
									}
									
									ER		getCommand(B **cmdBuf )				/* コマンドの入力   	*/
									{
										UB		buf[BUFSZ];						/* 入力用の固定バッファ	*/
										B		*blk;
										INT		size;							/* 入力された文字数  	*/
										ER		ercd;
									
										tm_putstring( (UB*)PROMPT );			/* プロンプトを出力  	*/
										size = tm_getline(buf);					/* コンソールから入力 	*/
									
										if( size == 0 ){						/* 改行のみ      	*/
											return 0;
										}
										if( size < 0 ){					    /* Ctrl-C 入力 → 終了  */
											strcpy( buf, CMD_EXIT );
											size = sizeof(CMD_EXIT);
										}
										size++;									/* 終端の '\0' 分を追加  */
									
										ercd = tk_get_mpl( mplid, size, &blk, TMO_GETBLK );
										if( ercd == E_OK ){
											strcpy( blk, buf );					/* 新しい入力を保存  	*/
											*cmdBuf = blk;						/* メモリブロックを返す	*/
										}else{
											tm_putstring( "Can't get memory block!\n" );
											size = ercd;
										}
									
										return size;							/* 入力コマンドサイズ 	*/
									}
									
									ER		replaceCommand( B **p_cmdBuf )		/* コマンドの置換   	*/
									{
										INT		reqIdx;							/* 要求された履歴   	*/
										B		*cmdBuf = *p_cmdBuf;			/* 入力されたコマンド 	*/
										B		*newCmd;						/* 置換した場合の領域 	*/
										INT		size;							/* 置換した場合のサイズ	*/
										ER		ercd;
									
										if( *cmdBuf != CMD_BANG ){				/* 置換不要      	*/
											return E_OK;
										}
									
										cmdBuf++;
										if( *cmdBuf == CMD_BANG ){				/* 1つ前       	*/
											reqIdx = idx - 1;
										}else if( isdigit(*cmdBuf) ){			/* + "数字"     	 */
											reqIdx = atoi(cmdBuf) - 1 + HIST_NO;
										}else if( (*cmdBuf == '-') && isdigit(*(cmdBuf+1)) ){
											reqIdx = idx - atoi(cmdBuf+1);
										}
									
										if( (reqIdx < (idx - HIST_NO))||(idx <= reqIdx) ){
											tm_putstring( "Out of range!\n" );
											return E_PAR;
										}
									
										size = strlen(hist[IDX(reqIdx)]) + 1;	/* 確保すべきサイズ  	*/
										ercd = tk_get_mpl( mplid, size, &newCmd, TMO_GETBLK );
										if( ercd < 0 ){
											tm_putstring( "Can't get memory block!\n" );
										}else{
											tk_rel_mpl( mplid, *p_cmdBuf );		/* 置換指示文字列は破棄	*/
											*p_cmdBuf = newCmd;
											strcpy( newCmd, hist[IDX(reqIdx)] );
											tm_putstring( newCmd );
											tm_putstring( "\n" );
										}
									
										return ercd;
									}

									void	commandHistory(void)				/* 履歴の表示     	*/
									{
										B		preStr[] = "[000] ";
										INT		i;
									
										for( i = idx - (HIST_NO-1); i <= idx; i++ ){
											if( *hist[IDX(i)] != '\0' ){
												itoa3( i - (HIST_NO-1), &preStr[1] );
												tm_putstring( preStr );
												tm_putstring( hist[IDX(i)] );
												tm_putstring( "\n" );
											}
										}
									}
									
									ER		commandRequest(	B* cmd )			/* 変換要求      	*/
									{
										/* リスト2で追加 */
										return E_OK;
									}
									
									void	commandDisplay(void)				/* 変換結果の表示   	*/
									{
										/* リスト2で追加 */
									}
									
									ER		executeCommand( B *cmdBuf )			/* コマンドの実行   	*/
									{
										ER		ercd = E_OK;
									
										if( !strcmp( cmdBuf, CMD_HISTORY ) ){	/* 履歴の表示     	*/
											commandHistory();
										}
										else if( !strncmp( cmdBuf, CMD_REQUEST, sizeof(CMD_REQUEST)-1 ) ){
											ercd = commandRequest( cmdBuf );	/* 変換要求      	*/
										}
										else if( !strcmp( cmdBuf, CMD_DISPLAY ) ){
											commandDisplay();					/* 変換結果の表示   	*/
										}else{
											ercd = E_PAR;
											tm_putstring( "NO SUCH COMMAND\n" );
										}
									
										return ercd;
									}
									
									void	createTranslateTask(void)
									{
										/* リスト2で追加 */
									}
									
									EXPORT	INT	usermain( void )
									{
									CONST	T_CMPL	cmpl = { NULL, TA_TFIFO, MPLSZ };
											B		*cmdBuf;
											INT		i;
											ER		ercd;
									
										/*--------------*/
										/* 初期化 		 */
										/*--------------*/
										mplid = tk_cre_mpl( &cmpl );
									
										for( i = 0; i < HIST_NO; i++ ){
											tk_get_mpl( mplid, 1, &cmdBuf, TMO_FEVR );
											*cmdBuf = '\0';
											hist[i] = cmdBuf;			/* ダミーのメモリブロックを追加	 */
										}
										idx = HIST_NO;
									
										createTranslateTask();					/* 変換用タスクの生成 	*/
									
										/*--------------*/
										/* 主ループ		 */
										/*--------------*/
										while( 1 ){
											ercd = getCommand( &cmdBuf );		/* コンソールから入力 	*/
											if( ercd <= 0 ){					   /* エラー or 改行のみ	 */
												continue;
											}
									
											if( !strcmp( cmdBuf, CMD_EXIT ) ){	/* 終了確認      	*/
												break;/*while*/					/* → ループから脱出  	*/
											}
									
											ercd = replaceCommand( &cmdBuf );	/* 再実行用の置換   	*/
											if( ercd < 0 ){
												tm_putstring( "Replace error\n" );
												continue;
											}
									
											tk_rel_mpl( mplid, hist[IDX(idx)] );
											hist[IDX(idx)] = cmdBuf;			/* コマンドの入替   	*/
									
											executeCommand( cmdBuf );			/* コマンドの実行   	*/
									
											idx++;								/* 次の履歴登録位置  	*/
									
										}/*while*/
									
										/*--------------*/
										/* 終了処理		 */
										/*--------------*/
										for( i = 0; i < HIST_NO; i++ ){
											tk_rel_mpl( mplid, hist[i] );		/* メモリブロックを解放	*/
										}
									
										tk_del_mpl( mplid );					/* メモリプールを削除 	*/
									
										return 1;
									}
									

リスト1は、コンソールから入力した文字列の簡単なヒストリ機能を実現するプログラムです。コンソールから入力された文字列(*1)を保存しておいて、後から一覧を表示したり、以前に入力した別の文字列に置き換えたりできます。

文字列が入力されると、可変長メモリプールから必要なサイズのメモリブロックを獲得し、そこに保存していきます。また、新しい文字列の入力があった場合、古い入力文字列を入れたメモリブロックを解放することにより、メモリが不足しないようにしています。

ヒストリ機能に対するコマンドは以下の2種類です。

exit プログラムを終了する。
history 保存されている履歴を表示する。

また、以下を入力すると以前入力した文字列(コマンド)に置き換えます。

## 直前のコマンドに置き換える。
#[数字] [数字]で示される履歴番号のコマンドに置き換える。
#-[数字] [数字]で示されるだけ前のコマンドに置き換える。
  • (*1)実装を簡単にするため、コンソールからの入力にはT-Monitorのtm_getline()を利用しています。tm_getline()用のバッファは80バイトしか用意していませんので、それ以上は入力せず、その前に必ず[Enter]を入力してください。入力しすぎた場合は[DEL]やCtrl+Hで文字を削除してください。
  •  

固定長メモリプール

固定長メモリプールは、固定サイズのメモリブロックを管理するための機能です。メモリブロックのサイズが固定である以外は、基本的に可変長メモリプールと同じ機能です。

機能的に同じであれば、可変長メモリプールで代用すればいいようにも思えますが、固定長の場合は可変長の場合のようなメモリの断片化(フラグメンテーション)が発生しないため、メモリブロックを効率的に管理できるというメリットがあります。また、固定長メモリプールはメモリブロックのサイズを管理する必要がない分、管理領域のサイズが小さくなり、処理もわずかですが速くなります。

さらに、実際のプログラムでは、固定サイズのメモリブロックを多用するような用途がたくさんあります。たとえば、ネットワークからの受信パケットを処理する場合やディスクのセクタを処理する場合などは固定長で十分なことがあります。また、センサーなどのちょっとした入出力デバイスと通信する場合は、固定サイズに決まったフォーマットのパケットにデータを設定して送受信することもよくあります。

当然、用途によってメモリプールのサイズは異なります。それぞれの用途に応じたサイズの固定長メモリプールを個別に用意することで、ある処理のメモリ不足が全く関係のない処理のメモリ不足を引き起こすことがないように実装することが可能となり、システムの安定性を増すことができます。

リスト2に、固定長メモリプールを利用した例を示します。

【リスト2:固定長メモリプールの使用例】

									#include <basic.h>
									#include <tk/tkernel.h>
									#include <tm/tmonitor.h>
									
									#define HIST_NO		(10)					/* 保存する履歴の数      	*/
									
									#define PROMPT		"> "					   /* プロンプト             */
									
																				/* コマンド一覧        	*/
									#define CMD_EXIT	"exit"					/* 終了            	*/
									#define CMD_HISTORY	"history"				/* 履歴表示          	*/
									#define CMD_REQUEST	"request"				/* 変換処理開始要求      	*/
									#define CMD_DISPLAY	"display"				/* 変換結果表示        	*/
									#define CMD_BANG	'#'						/* コマンド置換制御      	*/
									
									#define TMO_GETBLK	(3000)					/* 不足時の待ち時間      	*/
									
																				/* 可変長メモリプール     	*/
									#define BUFSZ		(80)					/* 入力用バッファサイズ    	*/
									#define MPLSZ		((BUFSZ*HIST_NO)/2)		/* MPL全体のサイズ        */
														/* ↑ サイズをわざと少なくするため、2分の1にしている		 */
									
																				/* 固定長メモリプール     	*/
									#define MPFSZ		sizeof(T_MSGPKT)		/* ブロックサイズ       	*/
									#define MPFNO		(4)						/* ブロック数         	*/
									
									LOCAL	ID		mplid;						/* 可変長メモリプールID   	*/
									LOCAL	ID		mpfid;						/* 固定長メモリプールID   	*/
									LOCAL	ID		mbfid;						/* メッセージバッファID   	*/
									LOCAL	ID		mbxid;						/* メールボックスID     	*/
									
									typedef	struct {							/* request−display間の     */
										T_MSG	msgque;							/* 変換結果転送パケット    	*/
										B		bstr[32+1];						/* ←	変換結果の本体      */
									} T_MSGPKT;
									
									LOCAL	B*		hist[HIST_NO];				/* 履歴へのポインタ      	*/
									LOCAL	INT		idx;						/* 次の履歴登録位置      	*/
									#define IDX(x)	((x)%HIST_NO)				/* 履歴配列指定用       	*/
									
									IMPORT	char*	strcpy( char *dst, const char *src );
									IMPORT	size_t	strlen( const char *s );
									IMPORT	int		strcmp( const char *s1, const char *s2 );
									
									#define isdigit(c)	(('0'<=(c))&&((c)<='9'))

									INT		atoi( B *str )						/* 文字列 → 整数         */
									{
										INT		i = 0;
									
										while( isdigit(*str) ){
											i = i*10 + (*str++ - '0');
										}
										return i;
									}
									
									void	itoa3( INT val, B *str )			/* 整数 → 文字列(3桁)      */
									{
										INT		i;
									
										for( i = 2; i >= 0; i-- ){		     /* 最大3桁            */
											*(str+i) = val % 10 + '0';
											val /= 10;
										}
										/* '\0' は追加していないので注意! */
									}

									ER		getCommand(B **cmdBuf )				/* コマンドの入力       	*/
									{
										UB		buf[BUFSZ];						/* 入力用の固定バッファ    	*/
										B		*blk;
										INT		size;							/* 入力された文字数      	*/
										ER		ercd;
									
										tm_putstring( (UB*)PROMPT );			/* プロンプトを出力      	*/
										size = tm_getline(buf);					/* コンソールから入力     	*/
									
										if( size == 0 ){						/* 改行のみ          	*/
											return 0;
										}
										if( size < 0 ){					  	  /* Ctrl-C 入力 → 終了     */
											strcpy( buf, CMD_EXIT );
											size = sizeof(CMD_EXIT);
										}
										size++;									/* 終端の '\0' 分を追加     */
									
										ercd = tk_get_mpl( mplid, size, &blk, TMO_GETBLK );
										if( ercd == E_OK ){
											strcpy( blk, buf );					/* 新しい入力を保存      	*/
											*cmdBuf = blk;						/* メモリブロックを返す    	*/
										}else{
											tm_putstring( "Can't get memory block!\n" );
											size = ercd;
										}
									
										return size;							/* 入力コマンドサイズ     	*/
									}
									
									ER		replaceCommand( B **p_cmdBuf )		/* コマンドの置換       	*/
									{
										INT		reqIdx;							/* 要求された履歴       	*/
										B		*cmdBuf = *p_cmdBuf;			/* 入力されたコマンド     	*/
										B		*newCmd;						/* 置換した場合の領域     	*/
										INT		size;							/* 置換した場合のサイズ    	*/
										ER		ercd;
									
										if( *cmdBuf != CMD_BANG ){				/* 置換不要          	*/
											return E_OK;
										}
									
										cmdBuf++;
										if( *cmdBuf == CMD_BANG ){				/* 1つ前           	*/
											reqIdx = idx - 1;
										}else if( isdigit(*cmdBuf) ){			/* + "数字"				   */
											reqIdx = atoi(cmdBuf) - 1 + HIST_NO;
										}else if( (*cmdBuf == '-') && isdigit(*(cmdBuf+1)) ){
											reqIdx = idx - atoi(cmdBuf+1);
										}
									
										if( (reqIdx < (idx - HIST_NO))||(idx <= reqIdx) ){
											tm_putstring( "Out of range!\n" );
											return E_PAR;
										}
									
										size = strlen(hist[IDX(reqIdx)]) + 1;	/* 確保すべきサイズ      	*/
										ercd = tk_get_mpl( mplid, size, &newCmd, TMO_GETBLK );
										if( ercd < 0 ){
											tm_putstring( "Can't get memory block!\n" );
										}else{
											tk_rel_mpl( mplid, *p_cmdBuf );		/* 置換指示文字列は破棄    	*/
											*p_cmdBuf = newCmd;
											strcpy( newCmd, hist[IDX(reqIdx)] );
											tm_putstring( newCmd );
											tm_putstring( "\n" );
										}
									
										return ercd;
									}
									
									void	commandHistory(void)				/* 履歴の表示         	*/
									{
										B		preStr[] = "[000] ";
										INT		i;
									
										for( i = idx - (HIST_NO-1); i <= idx; i++ ){
											if( *hist[IDX(i)] != '\0' ){
												itoa3( i - (HIST_NO-1), &preStr[1] );
												tm_putstring( preStr );
												tm_putstring( hist[IDX(i)] );
												tm_putstring( "\n" );
											}
										}
									}
									
									ER		commandRequest(	B* cmd )			/* 変換要求          	*/
									{
										UW		v = 0;
										ER		ercd;
									
										cmd += sizeof(CMD_REQUEST) - 1;			/* "request" をスキップ     */
										if( *cmd != ' ' ){
											return E_PAR;
										}
										cmd++;									/* " " をスキップ         */
									
										while( isdigit(*cmd) ){					/* 文字列 → UW型の数値      */
											v = (v*10) + (*cmd++ - '0');
										}
									
										ercd = tk_snd_mbf( mbfid, &v, sizeof(UW), TMO_POL );
										return ercd;							/* タスクXに処理を依頼    	*/
									}
									
									void	commandDisplay(void)				/* 変換結果の表示       	*/
									{
										T_MSGPKT	*pkt;
										ER			ercd;
									
										do{										/* 変換結果を全て受信     	*/
											ercd = tk_rcv_mbx( mbxid, &pkt, TMO_POL );
											if( ercd == E_OK ){
												tm_putstring( pkt->bstr );    /* 結果を表示           */
												tm_putstring( "\n" );
												tk_rel_mpf( mpfid, pkt );		/* メモリブロックを解放    	*/
											}
										}while( ercd == E_OK );
									}
									
									ER		executeCommand( B *cmdBuf )			/* コマンドの実行       	*/
									{
										ER		ercd = E_OK;
									
										if( !strcmp( cmdBuf, CMD_HISTORY ) ){	/* 履歴の表示         	*/
											commandHistory();
										}
										else if( !strncmp( cmdBuf, CMD_REQUEST, sizeof(CMD_REQUEST)-1 ) ){
											ercd = commandRequest( cmdBuf );	/* 変換要求          	*/
										}
										else if( !strcmp( cmdBuf, CMD_DISPLAY ) ){
											commandDisplay();					/* 変換結果の表示       	*/
										}else{
											ercd = E_PAR;
											tm_putstring( "NO SUCH COMMAND\n" );
										}
									
										return ercd;
									}
									
									void	taskX( INT stacd, void *exinf )		/* タスクX : 変換関数			*/
									{
										T_MSGPKT	*pkt;						/* 変換結果格納パケット    	*/
										UW			v;							/* 変換する値         	*/
										UW			uw;							/* ビット処理用        	*/
										B			*bstr;
									
										while(1){
											tk_rcv_mbf( mbfid, &v, TMO_FEVR );	/* v: 変換する値         */
									
											tk_get_mpf( mpfid, &pkt, TMO_FEVR );/* 変換結果格納用       	*/
									
											bstr = pkt->bstr;
											for( uw = 0x80000000UL; uw > 0; uw >>= 1 ){
												*bstr++ = (v & uw)? '1':'0';	/* 2進数の文字列       	*/
											}
											*bstr = '\0';
									
											tk_snd_mbx( mbxid, (T_MSG*)pkt );
										}
										tk_exd_tsk();							/* ここには来ない       	*/
									}
									
									void	createTranslateTask(void)
									{
									CONST	T_CMPF	cmpf = { NULL, TA_TFIFO, MPFNO, MPFSZ };
									CONST	T_CMBF	cmbf = { NULL, TA_TFIFO, 16, sizeof(UW) };
									CONST	T_CMBX	cmbx = { NULL, TA_TFIFO|TA_MFIFO };
									CONST	T_CTSK	ctsk = { NULL, TA_HLNG|TA_RNG0, taskX, 1, 4*1024 };
											ID		taskXid;
											ER		ercd;
																				/* オブジェクトの生成     	*/
										mpfid = tk_cre_mpf( &cmpf );			/* 固定長メモリプール     	*/
										mbfid = tk_cre_mbf( &cmbf );			/* メッセージバッファ     	*/
										mbxid = tk_cre_mbx( &cmbx );			/* メールボックス       	*/
									
										taskXid = tk_cre_tsk( &ctsk );			/* タスクの生成        	*/
										ercd = tk_sta_tsk( taskXid, 0 );		/* タスクの起動        	*/
									}
									
									EXPORT	INT	usermain( void )
									{
									CONST	T_CMPL	cmpl = { NULL, TA_TFIFO, MPLSZ };
											B		*cmdBuf;
											INT		i;
											ER		ercd;
									
										/*--------------*/
										/* 初期化 		 */
										/*--------------*/
										mplid = tk_cre_mpl( &cmpl );
									
										for( i = 0; i < HIST_NO; i++ ){
											tk_get_mpl( mplid, 1, &cmdBuf, TMO_FEVR );
											*cmdBuf = '\0';
											hist[i] = cmdBuf;		     	/* ダミーのメモリブロックを追加	*/
										}
										idx = HIST_NO;
									
										createTranslateTask();					/* 変換用タスクの生成     	*/
									
										/*--------------*/
										/* 主ループ		 */
										/*--------------*/
										while( 1 ){
											ercd = getCommand( &cmdBuf );		/* コンソールから入力     	*/
											if( ercd <= 0 ){            /* エラー or 改行のみ       */
												continue;
											}
									
											if( !strcmp( cmdBuf, CMD_EXIT ) ){  /* 終了確認          	*/
												break;/*while*/					/* → ループから脱出       */
											}
									
											ercd = replaceCommand( &cmdBuf );	/* 再実行用の置換       	*/
											if( ercd < 0 ){
												tm_putstring( "Replace error\n" );
												continue;
											}

											tk_rel_mpl( mplid, hist[IDX(idx)] );
											hist[IDX(idx)] = cmdBuf;			/* コマンドの入替       	*/
									
											executeCommand( cmdBuf );			/* コマンドの実行       	*/
									
											idx++;								/* 次の履歴登録位置      	*/
									
										}/*while*/
									
										/*--------------*/
										/* 終了処理		 */
										/*--------------*/
										for( i = 0; i < HIST_NO; i++ ){
											tk_rel_mpl( mplid, hist[i] );		/* メモリブロックを解放    	*/
										}
									
										tk_del_mpl( mplid );					/* メモリプールを削除     	*/
									
										return 1;
									}
									

リスト2は、リスト1で空になっていた以下の関数を置き換えています。また、追加で必要となる関数や定義なども加えてあります。

createTranslateTask 変換用のタスクXを生成する。
commandRequest requestコマンドを処理する。
commandDisplay displayコマンドを処理する。

リスト2では、以下の2つのコマンドが追加されます。

request タスクXに数値の変換を依頼する。
スペースを空けて数値を指定する。
display タスクXで変換した結果を表示する。

requestコマンドでは、引数で指定された値を32ビットの二進数に変換し、変換した結果は別のコマンド(display)によって表示します。

実際に実行した際のログを図3に示します(*2)。

  • (*2)図3はT-Kernel 2.0 Software Packageに含まれるエミュレータで動作させた場合の表示です。
図3

【図3:リスト2の実行結果】

実際に変換を行うのは別のタスクXであり、タスクXにはメッセージバッファ(第4回参照)を利用して変換する値を送信します。タスクXは変換結果を固定長メモリプールから獲得したメモリブロックに設定し、変換結果を設定したメモリブロックへのポインタをメールボックス(第4回参照)を利用して初期タスクに送信しています。

requestコマンドとdisplayコマンドによる一連の処理を図示すると図4のようになります。

図4

【図4:リスト2で追加した変換機能の全体構成】

リスト2のタスクXの処理であれば元のタスクで変換することも可能です。

ただし、例えば、複雑で時間のかかる処理が必要となる場合や、別のタスクからの入力などを待ってから変換する必要がある場合、他の機器との通信が必要となる場合などにはタスクを分離した方がよいことがあります。

図4のようにコマンド入力と実際の処理を分離した構成にすれば、時間のかかる処理をバックグランドで実行しながら、コンソールからの入力にも対応できるようになります。

まとめ

組み込み機器では、限りある資源をいかに効率良く利用できるが開発の鍵となります。特にメモリは消費電力や製品のコストを大きく左右する資源ですので、できるだけ効率的に利用する必要があります。

T-Kernelでは、このメモリを効率良く利用するための機能として、可変長メモリプールと固定長メモリプールを提供しています。これらの機能を利用して、効率の良いメモリ管理を行うシステムを開発してください。