- ベストアンサー
C言語のselect
Cのプログラムでインターバル処理と配信データの受信をselectで制御するとき、配信データの受信後に、インターバルの引数をNULLにするとその後にはインターバルでは跳ねなくなってしまいます。 前回のインターバルの設定を替えずに、selectする方法を どなたかご存知でしたら教えてください。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
基本的には以下の考え方をとります。 1.selectを行う。 2.timeoutならタイムアウト処理を行い、次のタイムアウト時間は60秒にする。 3.電文受信なら受信処理を行う。但し、次のタイムアウト時間は、前回使ったタイマーの残り時間とする。 問題は、selectが電文受信により終了したとき、タイマーの残り時間をどうやって取得するかです。これに対するシステムコールは提供されていません。従って、以下のようにします。selectの直前で現在の時刻を取得(T1とする)。次にselectの直後で現在の時刻を取得する(T2とする)T2-T1がselectに要した時間となる。従って、前回タイマーの残り時間-(T2-T1)が、次に発行する時のタイマーの時間となります。 前回タイマーの残り時間は、最初又はタイムアウト直後は60秒から開始し、電文受信によるselect終了を継続している間はそれを持ち回ります。 次に、現在時刻の取得ですが、0.001秒のオーダーで必要と言うことですので、gettimeofdayを使用します。これによりマイクロ秒オーダーで現在時刻が取得出来ます。 又、本件とは直接は関係ありませんが、電文受信とタイムアウトの切り分けをFD_ISSETで行っていますが、それは確実に動作していますか。厳密に言えば、以下の手順を踏むべきです。 1.selectの戻り値を取得 2.戻り値=0でタイムアウト 3.戻り値>0電文受信 4.戻り値<0シグナル補足による終了 シグナル補足による終了を考慮する必要がなければ、現行のままでも良いかと思いますが。 コーディングイメージは FLG=1をタイムアウトとすると FLG=1 while(1){ ディスクリプタの設定 if (FLG==1){ timeoutへ60秒をセット }else{ timeout-(T2ーT1) } T1を取得(gettimeofday) select(上記で求めたtimeoutを使用) T2を取得(gettimeofday) if (電文受信によるselect完了){ FLG=0 受信処理 }else{ FLG=1 インタバル処理 } } 時間の引き算は秒とマイクロ秒がありますので、それなりの計算が必要です。たぶんbunarinさんなら判ると思いますのでこれは省略します。 不明点があれば、再度質問して下さい。
その他の回答 (3)
- tatsu99
- ベストアンサー率52% (391/751)
#3です。 前回の回答に以下の点を追記します。 1.select(...&timeout.tp )でtimeout.tp の内容が破壊されることがあります。従って if (FLG==1){ 残タイマー=60(残タイマーはtimeoutと同じ型) }else{ 残タイマー=残タイマー-(T2-T1) } timeout = 残タイマー としてtimeout をselectの引数とするようにしてください。 2.残タイマーを計算したとき、残タイマーが負になることがあるかもしれませんので、残タイマーが負の場合は 残タイマーに0をセットする部分を追加しておいてください。 3.受信処理に要する時間は考慮していません。その時間まで考慮する必要があるなら、T2は受信処理完了後の時間としてください。
- tatsu99
- ベストアンサー率52% (391/751)
行いたいことは、以下の事でよいでしょうか。 1.一定間隔(60秒)でインターバル処理を実行したい。 2.但し、その間にデータを受信した場合は、受信処理を行いたい。 3.受信処理を行った後は、受信処理後、60秒後にインタバル処理が実行されるのではなく、以前に発行したタイマーを含めて、そこから60秒後に、インタバル処理を行いたい。(要は受信処理に関係なくインタバル処理は60秒間隔で行いたい) 4.上記に対して全てyesと言う前提として、タイマーの精度は秒程度のオーダーで良いでしょうか。(つまり59~61秒程度の揺らぎはOKですか。)それとも、更に細かい精度で、60秒に1回起動されることを要求されますか?
補足
1,2,3 全てYesです。 4は0.001秒まで必要です。 よろしくお願いします。
- tatsu99
- ベストアンサー率52% (391/751)
インターバル処理とはなんのことでしょうか? >インターバルでは跳ねなくなってしまいます。 具体的にはどういうことでしょうか? >インターバルの引数をNULLにする インターバル処理の引数はどのようになっていますか? また、どのような機能をもっていますか?
補足
>インターバル処理とはなんのことでしょうか? 一定間隔で処理をするためのselectの単にタイムアウトの設定のことです。 timeout.tv_sec = 60; timeout.tv_usec = 0; ret = select(0,NULL,NULL,NULL,&timeout); >>インターバルでは跳ねなくなってしまいます。 >具体的にはどういうことでしょうか? while( 1 ){ FD_ZERO( &readfds ); FD_SET( channel, &readfds ); timeout.tp.tv_sec = 60; timeout.tp.tv_usec = 0; /* select によりイベント待ち */ status = select( FD_SETSIZE, &readfds, NULL, NULL, &timeout.tp ); if( FD_ISSET( channel, &readfds ) ){ // 受信処理 } else { // インターバル処理 } } こう書くとインターバル処理のときだけでなく 受信処理の時もタイマーが再設定されてしまいますが、 本来やりたいのはインターバル処理の時だけselectにタイマーを設定したいので int FLG=1; while( 1 ){ FD_ZERO( &readfds ); FD_SET( channel, &readfds ); timeout.tp.tv_sec = 60; timeout.tp.tv_usec = 0; /* select によりイベント待ち */ if( FLG==1 ){ select( FD_SETSIZE, &readfds, NULL, NULL, &timeout.tp ); } else{ select( FD_SETSIZE, &readfds, NULL, NULL, NULL ); } if( FD_ISSET( channel, &readfds ) ){ // 受信処理 FLG=0; } else { // インターバル処理 FLG=1 } } と書くと今度は受信処理を一度行うと タイムアウトが利かなくなってしまいます。 そこで受信処理後のselectを実行するときに 直前で設定したタイムアウトの設定を消すことなく 実行するにはどうすればよろしいでしょうか?
お礼
>問題は、selectが電文受信により終了したとき、 >タイマーの残り時間をどうやって取得するかです。 >これに対するシステムコールは提供されていません。 そうですか、selectを呼んでしまうとやはり、前回の timeoutの設定がなくなってしまうのは、 自前で対処するしかないんですね。 配慮の行き届いた回答をしていただき 大変参考になりました。 ありがとうございました。