メモリ管理機能
今回はT-Kernelのメモリ管理機能について説明します。
T-Kernelは組み込み機器用のリアルタイムOSです。組み込み機器では限られたリソースを有効に活用しなければなりません。その分りやすい例がメモリです。
情報系のOSでは、GB(ギガバイト)単位でメモリを搭載していたり、仮想メモリを利用することを前提にしていたりするため、メモリが枯渇する心配はほとんどありません。ところが、組み込み機器ではそうはいきません。実行中にメモリを確保しようとしても、メモリ不足により獲得できない可能性がありますので、そのような前提でプログラムを開発する必要があります。
そこで、T-Kernelでは、メモリを管理するための機能として以下の2種類を用意しています。
- 可変長メモリプール
- 固定長メモリプール
今回は、これらの使い方について説明します。
可変長メモリプール
可変長メモリプールは、任意のサイズのメモリブロックを管理するための機能です。C言語のライブラリにmalloc
という関数がありますが、これに似た機能を提供します。malloc
との主な違いは以下の通りです。
- 用途別に複数のメモリプールを用意することができる。
- メモリブロックを獲得できない場合のタイムアウトを指定することができる。
T-Kernelでは複数のメモリプールが利用できますので、たとえば、メモリ不足の際に待ってもよい処理と、メモリ不足による中断が許されないクリティカルな処理に対して、別々のメモリプールを割り当てることができ、クリティカルな処理が他の処理から影響されにくいシステムを構築できます。
メモリが獲得できない場合、情報系のOSでは、メモリ不足の場合に二次記憶にメモリ内容を退避すること(ページアウト)でメモリの空き領域を確保したり、自動的にガベージコレクションを行ってくれる場合もあります。これらは便利な機能ではありますが、いつメモリが獲得できるのかが分らないまま延々待たされる危険性があるので、リアルタイム性が必要とされる組み込み機器には不向きです。
一方T-Kernelでは、メモリが獲得できない場合の動作を明確に制御できるように、メモリ獲得用のシステムコールにタイムアウト機能が付いています。タイムアウト時間を指定できるので、メモリが獲得できない場合の動作として以下を選択することができます。
- メモリが獲得できなければ即時エラー終了する。
- メモリが獲得できるまで待つ。
- 指定された時間待ってもメモリを獲得できなければエラー終了する。
このように、メモリを獲得できなかった場合に、待ち時間の最大値をアプリケーションから制御することで、システム全体としてのリアルタイム性を確保できるようになっています。
リスト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に含まれるエミュレータで動作させた場合の表示です。
実際に変換を行うのは別のタスクXであり、タスクXにはメッセージバッファ(第4回参照)を利用して変換する値を送信します。タスクXは変換結果を固定長メモリプールから獲得したメモリブロックに設定し、変換結果を設定したメモリブロックへのポインタをメールボックス(第4回参照)を利用して初期タスクに送信しています。
request
コマンドとdisplay
コマンドによる一連の処理を図示すると図4のようになります。
リスト2のタスクXの処理であれば元のタスクで変換することも可能です。
ただし、例えば、複雑で時間のかかる処理が必要となる場合や、別のタスクからの入力などを待ってから変換する必要がある場合、他の機器との通信が必要となる場合などにはタスクを分離した方がよいことがあります。
図4のようにコマンド入力と実際の処理を分離した構成にすれば、時間のかかる処理をバックグランドで実行しながら、コンソールからの入力にも対応できるようになります。
まとめ
組み込み機器では、限りある資源をいかに効率良く利用できるが開発の鍵となります。特にメモリは消費電力や製品のコストを大きく左右する資源ですので、できるだけ効率的に利用する必要があります。
T-Kernelでは、このメモリを効率良く利用するための機能として、可変長メモリプールと固定長メモリプールを提供しています。これらの機能を利用して、効率の良いメモリ管理を行うシステムを開発してください。
こちらも是非
“もっと見る” RTOS編
Toppers/ASP3の使い方
SOLID-OSによる割り込みは、Toppers/ASP3がベースになっており、カーネルの管理下で割り込みの処理を行いますので、「割り込みサービスルーチン(ISR)」または「管理内割り込み」と呼んだりします。それ以外のものは、「割り込みハンドラ」もしくは「カーネル管理外割り込み」と呼びます。
組み込みソフトウェア開発にRTOSを採用する場合の「4つの心得」
リアルタイムOSを使った組み込みソフトウェア開発を行う際、覚えておいたほうがよい「4つの心得」を紹介しましょう。
組み込みOSに最適なのは?リアルタイムOSとLinuxの違い
組み込みシステムの構成を考えていく上で、どのOSを採用するかは、開発初期段階においてとても重要です。一般的には、時間的制約があるシステムの場合はリアルタイムOSが、ネットワークやファイルシステム、高度なグラフィカル表示が必要な場合はLinuxが向いていると言われています。