C LANGUAGE TECHNOLOGY

【C言語】タイマと割り込み処理の書き方

2021年10月19日

悩んでいる人
悩んでいる人

C言語でタイマと割り込み処理の書き方を教えて!

こういった悩みにお答えします.

本記事の信頼性

  • リアルタイムシステムの研究歴12年.
  • 東大教員の時に,英語でOS(Linuxカーネル)の授業.
  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
  • プログラミング歴15年以上,習得している言語: C/C++PythonSolidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Verse(UEFN), Assembler (x64,ARM).
  • 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」GitHubにオープンソースとして公開
  • 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
  • 最近は自然言語処理AIイーサリアムに関する有益な情報発信に従事.
    • (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
    • (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.

こういった私から学べます.

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!

友だち追加

独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

タイマと割り込み

タイマとは,予め設定した時間を経過した後に,その旨を通知するためのハードウェアの仕組みです.

タイマを利用することで,周期もしくは非周期にタスクを起床することができます.

また,タイマにより発生したソフトウェア割り込み(シグナル)をタイマ割り込みと呼び,タイマ割り込みが発生した時の処理をする関数をシグナルハンドラと呼びます.

シグナルハンドラ内では非同期安全な関数しか呼び出すことができませんので注意して下さい.

例えば,シグナルハンドラ内ではprintf関数を利用できないので,標準出力や標準エラー出力にデータを書き込みたい場合はwrite関数を利用しましょう.

sigaction関数

sigaction関数は,シグナルの動作の確認と変更を行う関数です.

actはNULL以外であれば,シグナルsignumの新しい動作(action)としてactが設定されます.

oldactがNULLでなければ,今までの動作がoldactに格納されます.

※sigaction関数と似た処理をするsignal関数がありますが,Linuxのバージョンにより動作が異なるので利用を控えましょう.

sigaction構造体は以下になります.

※sa_handlerとsa_sigactionはunionで定義されていない場合があるので注意して下さい.

以降の説明では,sigaction関数を利用したタイマと割り込み処理の書き方を解説していきます.

getitimer/setitimer関数

getitimer/setitimer関数は,インターバルタイマの値を取得または設定する関数です.

struct itimeval構造体の定義は以下になります.

getitimer関数は,whichで指定されたタイマ(ITIMER_REAL,ITIMER_VIRTUAL,ITIMER_PROFのいずれか)の現在の値(次のタイマ満了までの残り時間)をcurr_valueで指定された構造体に格納します.

it_valueフィールドのにはタイマの残り時間,it_intervalフィールドにはタイマのインターバル(期間)が設定されます.

setitimer関数は指定されたタイマにnew_valueの値を設定します.

old_valueがNULL以外の場合,タイマの古い値(すなわち getitimer関数で返されるのと同じ情報)がold_valueに格納されます.

new_value.it_value のいずれかのフィールドが非ゼロの場合,タイマは指定された時間で開始するために待機します.

new_value.it_valueの両方のフィールドがゼロであれば,タイマは解除されます.

new_value.it_intervalフィールドは,タイマの新しい間隔を指定します.

そのサブフィールド(new_value.it_interval.tv_secとnew_value.it_interval.tv_usec)の両方がゼロの場合,タイマはシングルショットになります.

getitimer/setitimer関数を利用するコードは以下になります.

実行結果は以下になります.

最初に2秒待機した後,「timer_handler() is called.」が1秒間隔で表示されて,3回表示されたら終了します.

実際にタイマが正常にカウントダウンされているかどうか不安になりますよね.

そんなあなたはgetitimer関数でカウントダウンしているタイマの値を表示してみましょう.

以下のように-DCOUNTDOWNオプションを付けてコンパイルして実行してみて下さい.

カウントダウンタイマの値が表示されることがわかります.(とても長いので注意して下さい.)

timer_create/timer_gettime/timer_settime/timer_delete関数

timer_create関数は,プロセスごとのインターバルタイマを新しく作成します.

新しいタイマのIDは,timeridが指すバッファに格納されます(IDはタイマが削除されるまではプロセス内でユニーク).

また,新しいタイマは初期状態は未起動になります.

引数clockidは,新しいタイマが時間を計測するために使用する時計を指定します.

引数sevpはsigevent構造体を指し,タイマが時間切れ(タイムアウト)した時に呼び出し側にどのように通知するかを指定します.

timer_gettime/timer_settime関数は,timeridで識別されるタイマを起動または停止します.

引数new_valueは,タイマの新しい初期値と新しい間隔を指定するitimerspec構造体へのポインタです.

struct itimerspec構造体の定義は以下になります.

timer_delete関数は,timeridで指定されたIDのタイマを削除します.

タイマの削除に成功すると0,失敗すると-1を返します.

timer_create/timer_gettime/timer_settime/timer_delete関数を利用するコードは以下になります.

実行結果は以下になります.

setitimer関数と同様です.

コンパイルする時に-rtオプションを付けることを忘れないようにして下さい.

-DCOUNTDOWNオプションを付けてコンパイルした場合も同様です.

timerfd_create/timerfd_gettime/timerfd_settime関数

timerfd_create関数は,タイマオブジェクトを生成し,そのタイマを参照するファイルディスクリプタを返します.

引き数clockidは,タイマの進捗を管理するためのクロックを指定するもので,CLOCK_REALTIMEかCLOCK_MONOTONICのどちらかを設定します.

引数flagsは,以下のフラグを設定できます.

  • TFD_NONBLOCK:ファイルディスクリプタの操作をノンブロッキングに設定します.
  • TFD_CLOEXEC:明示的にファイルディスクリプタをクローズするのではなく,他のプログラムを実行する際に自動でファイルディスクリプタをクローズするように指定するclose-on-execオプションを設定します.

timerfd_gettime関数は, ファイルディスクリプタfdで参照されるタイマの現在の設定が入ったitimerspec構造体を,curr_valueに格納して返します.

it_value フィールドはタイマが次に満了するまでの残り時間,it_intervalフィールドはタイマの間隔を返します.

timerfd_settime関数は,ファイルディスクリプタfdにより参照されるタイマを起動または停止します.

引数new_valueはタイマの満了時間の初期値と間隔を指定します.

timerfd_create/timerfd_gettime/timerfd_settimeシステムコールは,上記で紹介したsetitimer関数やtimer_create関数の代替になるものです.

この理由として,timerfd_create関数で生成したファイルディスクリプタをread関数select関数poll関数epollファミリー関数で監視できるメリットがあるからです.

timerfd_create/timerfd_gettime/timerfd_settime関数を利用するコードは以下になります.

sigaction関数を利用しないことで,シンプルなコードになっていることがわかります.

また,タイマ割り込みの発生は51行目のread関数で待機し,割り込みが発生したらread関数から返ります.

実行結果は以下になります.

-DCOUNTDOWNオプションを付けた場合の実行結果は以下になります.

まとめ

C言語でタイマと割り込み処理の書き方を紹介しました.

具体的には,以下の関数の使い方を解説しました.

  • getitimer/setitimer関数
  • timer_create/timer_gettime/timer_settime/timer_delete関数
  • timerfd_create/timerfd_gettime/timerfd_settime関数

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!

友だち追加

独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

-C LANGUAGE, TECHNOLOGY
-, , , ,