• ベストアンサー

C言語のselect

Cのプログラムでインターバル処理と配信データの受信をselectで制御するとき、配信データの受信後に、インターバルの引数をNULLにするとその後にはインターバルでは跳ねなくなってしまいます。 前回のインターバルの設定を替えずに、selectする方法を どなたかご存知でしたら教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.3

基本的には以下の考え方をとります。 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)
回答No.4

#3です。 前回の回答に以下の点を追記します。 1.select(...&timeout.tp )でtimeout.tp の内容が破壊されることがあります。従って if (FLG==1){ 残タイマー=60(残タイマーはtimeoutと同じ型) }else{ 残タイマー=残タイマー-(T2-T1) } timeout = 残タイマー としてtimeout をselectの引数とするようにしてください。 2.残タイマーを計算したとき、残タイマーが負になることがあるかもしれませんので、残タイマーが負の場合は 残タイマーに0をセットする部分を追加しておいてください。 3.受信処理に要する時間は考慮していません。その時間まで考慮する必要があるなら、T2は受信処理完了後の時間としてください。

bunarin
質問者

お礼

>問題は、selectが電文受信により終了したとき、 >タイマーの残り時間をどうやって取得するかです。 >これに対するシステムコールは提供されていません。 そうですか、selectを呼んでしまうとやはり、前回の timeoutの設定がなくなってしまうのは、 自前で対処するしかないんですね。 配慮の行き届いた回答をしていただき 大変参考になりました。 ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.2

行いたいことは、以下の事でよいでしょうか。 1.一定間隔(60秒)でインターバル処理を実行したい。 2.但し、その間にデータを受信した場合は、受信処理を行いたい。 3.受信処理を行った後は、受信処理後、60秒後にインタバル処理が実行されるのではなく、以前に発行したタイマーを含めて、そこから60秒後に、インタバル処理を行いたい。(要は受信処理に関係なくインタバル処理は60秒間隔で行いたい) 4.上記に対して全てyesと言う前提として、タイマーの精度は秒程度のオーダーで良いでしょうか。(つまり59~61秒程度の揺らぎはOKですか。)それとも、更に細かい精度で、60秒に1回起動されることを要求されますか?

bunarin
質問者

補足

1,2,3 全てYesです。 4は0.001秒まで必要です。 よろしくお願いします。

全文を見る
すると、全ての回答が全文表示されます。
  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.1

インターバル処理とはなんのことでしょうか? >インターバルでは跳ねなくなってしまいます。 具体的にはどういうことでしょうか? >インターバルの引数をNULLにする インターバル処理の引数はどのようになっていますか? また、どのような機能をもっていますか?

bunarin
質問者

補足

>インターバル処理とはなんのことでしょうか? 一定間隔で処理をするための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を実行するときに 直前で設定したタイムアウトの設定を消すことなく 実行するにはどうすればよろしいでしょうか?

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • TCPでの非同期型select関数について

    マッキントッシュOS-X上で動くTCP通信プログラムを作る場合について教えてください。 ウィンドウアプリを考えています。 ウィンドウ環境なので、アプリがフリーズしてしまわないために、 以下のような非同期処理が考えられます。 この場合、データが来ていなくても、while(1)がぐるぐる回るので 無駄にプロセスを食ってしまうと思うのですが、これはしかたのないことでしょうか。 もっとよい方法がるのでしょうか。 Winsockだとイベント応答関数での受信処理処理ができてエレガントなのですが、 バークレイソケットでは、どうすべきなのか疑問に思い質問させていただきました。 よろしくお願いします。 //ソケットを非同期モードにセット val = 1; ioctl(sock, FIONBIO, &val); while (1) {   memcpy(&fds, &readfds, sizeof(fd_set));   select(2, &fds, NULL, NULL, NULL);   // sockに読み込み可能データが届いている場合は、受信   if (FD_ISSET(sock, &fds)) {     memset(buf, 0, sizeof(buf));     recv(sock, buf, sizeof(buf), 0);   }   else{     //受信データがなかったときの処理   } }

  • H8 3694 を使ってI2C通信をC言語で試みようと思っています。 

    H8 3694 を使ってI2C通信をC言語で試みようと思っています。 H8 3694側をスレーブにして、受信を行っているのですが、上手くいきません。 プログラム&初期設定などはハードウェアマニュアルの使用例通りに設定しましたが、一度マスタからの受信を行うと、その後2回目の通信の途中でデータ受信が出来ないようなのです。使用例に載っているレジスタ意外にも設定しなければいけない項目・注意点などご存じの方がおりましたらよろしくお願いします。 なかなか、3694をスレーブに使用している方が見つからなくて困っています。参考になりそうなHPや本など何でも構いません。紹介していただけるとありがたいです。 C言語、マイコン歴1年の初心者です。未熟な質問で申し訳ありませんがよろしくお願いします。

  • C言語を勉強したい。

    C言語でモーター、LED制御等行いたいのですが、参考書等はほとんど私の目的としたものに関係しない分野での参考プログラムです。 また、ロボット等のプログラムはアイコンをならべてプログラムを記述する方法ですが、肝心のC言語での記述は確認できません。 どなたか、モーター、あるいはLED等の制御を行うプログラム及びその解説,等がある参考書、キットなどご存じの方、教えて頂けませんか。 

  • ”select”,”FD_ISSET”という関数はMicrosoft Visual C++ 2008 Express Editionで使えますか?

    今、Microsoft Visual C++ 2008 Express Editionでソケット通信のサーバ、クライアントプログラムを作りたくていろいろ調べていて、特に複数クライアントが接続できるサーバを作りたいと思っています。 ただ、どうしてもrecv関数に入ると、1台の端末からのデータ受け取りでステップが停止してしまって、通信を占有してしまい、他のクライアントからのデータを受け付けることができないので、どうすればよいのかと思い、 ググッていたら http://x68000.q-e-d.net/~68user/net/c-echo-2.html このページにselect関数とFD_ISSET関数というのがあって、select関数の引数にデータを設定すれば、FD_ISSET関数の戻り値で作成したソケットに読み取り可能なデータがあるのかを検知できて、検知したらrecv関数でとれるので、これでrecv関数での立ち止まりがないので、複数のクライアントからのデータを行えるというプログラムを見つけました。 ただ、このFD_ISSET関数、select関数はまさにマルチスレッドのやり方だと思うので、Microsoft Visual C++ 2008 Express EditionではMFCがないとのことなのですが、使用することはできるのでしょうか?

  • C言語でコマンド引数にワイルドカードを使うには?サブディレクトリも探索させるには?

    C言語で(下記の環境で) ワイルドカードを使うにはどうしたら良いでしょうか? 現在作成中のプログラムで、program.exe data1012.log とかいう形で処理ができるようになりました。ところで、引数に書いたdata1012.logとかが、300個ぐらいあるとして(あるいは別のdirectoryに更に1000個とか、そういうdirが20個ぐらいあり)これを、program.exe *.logとして、一発で処理させることはできるでしょうか?ワイルドカードの展開と、サブディレクトリの探索どちらかだけでもありがたいです。 どうぞよろしくお願い致します。 環境:Borland C v5.5 無料版, windows2000 (DOS窓), ThinkPadです。

  • SQLServer7.0で、SELECT文で列を抽出する ※Nullと0の条件について

    SQLServer7.0で、SELECT文で列を抽出する際なんですが、 列A 列B --------- 1  Null 2  Null 3  1 ↑のテーブルから、列Aの1、2だけを抽出するSELECT文を SELECT * FROM XXX WHERE 列B <> 1 と作成したのですが、うまく動作しません(汗) データが一件も取れないのです。(列B:tinyint型、Null許容) テーブル内のデータをNull→0にして、同SELECT文で抽出すればうまくいったのですが。。 Nullデータを、<>XX という条件で取得することはできないんですかねえ・・・。 それとも、テーブルの設定か、条件の記述方法がまずいんでしょうか。 どなたか、ご存知あればアドバイスをお願いします。

  • is null のandについて(日付型)

    SQLのSELECTで困っています。 プロシージャに引数を渡してSELECTしようと考えています。 複数の日付(datetime)型の引数をand条件で結びたいです。 検索したくない場合はnullを渡す。 例) select A,B,C from XXX where A = '1' and (B = 引数 or 引数 is null) and (C = 引数 or 引数 is null) and (D = 引数 or 引数 is null) ===== B,C,D はdatetimeです。 この形ですと、B,C,D全ての引数に日付が入っていれば取得できる のですが一箇所でも引数に何もセットしないと値0件で帰ってきて しまいます。 せっかく(is null)を使用しているのに意味がありません。 良いお知恵は無いでしょうか。 よろしくお願いします。

  • C言語のプログラムを書いたのですが上手く動きません

    C言語の練習問題をプログラミングしたのですが、上手く動きません。 コンパイルはできるのですが、実行すると「Segmentation fault(core dumped)」となります。 問題は、コマンドライン引数としてファイル名を指定したテキストファイルから読み込んだデータを,双方向リストに格納し,順番に表示するというものです。 テキストファイルは 10T5001 C 10T5002 A 10T5003 B 10T5004 C 10T5005 D 10T5006 B 10T5007 A 10T5008 D このように、IDとランクをランクABCDをスペースで区切ったもので、これをランクAから順番に表示させます。 上手く動けば ID: 10T5002, grade: A, ID: 10T5007, grade: A ID: 10T5003, grade: B ID: 10T5006, grade: B ID: 10T5001, grade: C ID: 10T5004, grade: C ID: 10T5005, grade: D ID: 10T5008, grade: D と表示されるはずなんですが・・・ これが僕の書いたプログラムです(長いです。ごめんなさい) #include <stdio.h> #include <stdlib.h> #include <string.h> #define ID_LENGTH 8 /* IDの長さ+ナル文字 */ #define EFOPEN -1 #define ENOMEM -2 #define EINVAL -3 /* 双方向リストのノード */ typedef struct sList /* タグ */ { char id[ID_LENGTH]; /* ID */ char grade; /* ランク*/ struct sList *prev; /* 前のノードのアドレス */ struct sList *next; /* 次のノードのアドレス */ } sNode; /* 双方向リストのノードを作成する関数 makeNewNode() 作成したノードのprevとnextはNULLにする 引数 ・ノードに格納するID ・ノードに格納するランク(A/B/C/D) 戻値 作成したノードの先頭アドレス.メモリの確保に失敗した場合はNULL */ sNode *makeNewNode(char *id, char grade) { sNode *pNewNode; pNewNode = (sNode*)malloc(sizeof(sNode)); if(pNewNode != NULL) { strncpy(pNewNode->id, id, ID_LENGTH-1); pNewNode->grade = grade; pNewNode->prev = NULL; pNewNode->next = NULL; } return pNewNode; } /* 引数で渡された任意のノードの後ろに新しいノードを追加する関数 insertNext() 引数 ・後ろにノードを追加したいノードの先頭アドレス ・追加するノードの先頭アドレス 戻値 なし */ void insertNext(sNode *node, sNode *newNode) { if(node->next == NULL) //一番後ろに追加する場合 { node->next = newNode; newNode->prev = node; } else //まん中に挿入する場合 { newNode->prev = node; newNode->next = node->next; node->next->prev = newNode; node->next = newNode; } } /* main() 引数 int argc コマンドライン引数の数 char *argv[] 与えられたコマンドライン引数の文字列の先頭アドレスの配列 戻値 int 正常終了の時 0 以下の場合は異常終了し,括弧内の値を返す データファイルが開けなかった場合(EFOPEN) makeNewNode()でメモリが確保できなかった場合(ENOMEM) 引数の数が正しくない場合(EINVAL) */ int main(int argc, char *argv[]) { sNode *top; /* リストの先頭ノードのアドレスを保持する変数*/ sNode *new; /* 新しく作成したノードのアドレスを保持する変数 */ sNode *now; /* 現在見ているノードのアドレスを保持する変数 */ FILE *fp; /* データファイルのファイルポインタ */ char id[ID_LENGTH]; /* ファイルから読み込んだIDを一時的に保持する変数 */ char grade; /* ファイルから読み込んだランクを一時的に保持する変数 */ /* コマンドライン引数の数をチェックする 数に過不足があれば,使い方を表示し,異常終了する */ if(argc != 2){ printf("Usage: %s datafilename.\n", argv[0]); return EINVAL; } /* データファイルを読み込み用に開く ファイルが開けなかった場合,エラーメッセージを表示し異常終了する */ fp = fopen(argv[1], "r"); if(NULL == fp){ printf("No such file %s.\n", argv[1]); return EFOPEN; } /* リストの先頭に番兵を立てる */ new = makeNewNode("Banpei", 'A'-1); if (new == NULL){ printf("Error: cannot allocate memory\n"); return ENOMEM; } top = new; /* データファイルから1行ずつデータを読み込み,ランク順にリストに追加していく 既にリストの先頭には番兵ノードがある点に注意 */ while(EOF != fscanf(fp, "%s %c", id, &grade)){ new = makeNewNode(id, grade); if(new == NULL){ printf("Cannot allocate memory.\n"); return EFOPEN; } if(top->next == NULL){ //リストが空の場合 insertNext(top, new); } else{ now = top; while(new->grade > now->grade){ now = now->next; } insertNext(now, new); } } /* できあがったリストの内容を先頭から順に表示する ただし,番兵ノードは表示しない */ now = top; while(now != NULL){ printf("ID: %s, grade: %c\n",now->id, now->grade,); now = now->next; } /* リストのノードを全て(番兵ノードも含む)解放し,リストを空にする */ now = top; while(now->next != NULL){ free(now->prev); now = now->next; } free(now); fclose(fp); return (0); } まだまだ練習中なもので、かなり拙いものだと思いますが、とりあえずまずはプログラム動くようにしたいです。 よろしくお願いします。

  • Cで他のプログラムを起動させる命令

    C言語でプログラムを作っているのですが、他のプログラムを起動させる命令が分かりません。そのプログラムが起動し、処理が終わった後は起動させたプログラムの作業に戻るようにしたいです。 後、引数もプログラムに渡したいです。 何かいい命令を知っている方はお願いします。

  • 再投稿_バックグラウンドで setInterval

    すみません、誤って同様の質問を締め切ってしまったので再投稿させていただきます。 https://gist.github.com/kawaz/72f61d8389fed0e9d4e7dc9eb01b39c8 setIntervalはブラウザのタブがバックグラウンドで動作しないとのことで動作させるためのサンプルコードが ないかと調べていたところ上記サイトを見つけました。 上記サイトでは下記のようなコードが記述されていたのですやっていることがわからなかったのですが どなたか解説して頂けないでしょうか。 大きく下記2点が不明です。 1. const code = `self.addEventListener('message', msg=>{setInterval(()=>self.postMessage(null), msg.data)})` の部分 setIntervalの第一引数に関数定義「()=>self.postMessage(null)」を渡し、第二引数にmsg.data を渡しています。 これはmessageイベントが発生したらsetIntervalで定期的にpostMessageでWorkerにメッセージ(msg.data)を送信する ということなのでしょうか? そしてその処理をWorkerに渡して処理をバックグラウンドで動かすということをやっているのでしょうか? そもそも'message'イベント(メッセージを受信?どこから?)っていつ起こるものなのでしょうか。 2. superIntervalに渡している引数 (使い方)にあるsuperInterval()に渡している引数がよくわからないです。  第二引数で500を渡していながらsuperIntervalの第二引数はinterval=1000となってるし、 その他の引数についても何のためにどういう使い方をしているかわかりますでしょうか? (superInterval.js) const superInterval = (cb, interval=1000, ...args) => { try { const code = `self.addEventListener('message', msg=>{setInterval(()=>self.postMessage(null), msg.data)})` const w = new Worker(`data:text/javascript;base64,${btoa(code)}`) w.onmessage = () => cb(...args) w.postMessage(interval) return {stop:()=>w.terminate()} } catch(_){ // 実装の問題またはCSPによる拒否などで Worker が使えなければ普通の setInterval を使う const id = setInterval(cb, interval, ...args) return {stop:()=>clearInterval(id)} } } (使い方) { const log = (...args) => console.log(...args) const {stop} = superInterval(log, 500, 1, {a:2}, [3]) setTimeout(stop, 3000) }

このQ&Aのポイント
  • 30代女性の交際相手が浮気をしているのではないかと思われる女性がTwitterのフォロワーにいる。
  • 彼とその女性は頻繁に一緒に遊びに行っており、昨日も一緒に泊まった様子がある。
  • 彼は男女の友情を認める立場であり、彼女は彼との関係を知りたいと考えている。
回答を見る