1

タイマーを使用して一定のサンプルレート (200Hz) でデータの取得と処理を行うアプリケーションを作成しています。アプリケーションはサーバーのように機能し、バックグラウンドで実行されます。UDP から他のプロセスまたは他のマシンから制御できる必要があります。

そのために、timer_create() API を使用して定期的に SIGUSR1 を生成し、取得と処理を行うハンドラーを呼び出します。

タイマーを構成するコードは次のとおりです (明確にするためにエラー チェックを差し引いています)。

sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;
sev.sigev_value.sival_ptr = &timerid;
timer_create(CLOCK_REALTIME, &sev, &timerid);
timer_settime(...)

上記のコードは、UDP から「開始」コマンドを受信したときに呼び出されます。コマンドを確認するために、メイン プログラムに recvfrom() syscall を呼び出す無限ループがあります。

問題は、「開始」コマンドが受信され、タイマーが適切に開始されて実行されている場合 (上記のコードを使用)、タイマーによって送信された SIGUSR1 シグナルが原因で「中断されたシステム コール」エラー (EINTR) が発生することです。 recvfrom() 呼び出しを中断します。この特定のエラー コードをチェックして無視すると、最終的に recvfrom() を呼び出すときに「接続が拒否されました」というエラーが発生します。

だからここに私の質問:

  1. この 'interrupted system calls' エラーを解決するにはどうすればよいですか?
  2. 約 20 回試行した後に「接続が拒否されました」というエラーが表示されるのはなぜですか?
  3. 私が理解しているように、SIGEV_THREADを使用すると解決策になる可能性があると感じています。シグナルを生成せずに新しいスレッド(phread_createなど)を作成します。私は正しいですか?
  4. ここでシグナル番号は重要ですか?リアルタイム信号を使用するプラスはありますか?
  5. 私が意図していることを行う他の方法はありますか: UDP からのコマンドとリアルタイムの定期的なタスクをバックグラウンド ループでチェックすることはありますか?

そしてここにボーナスの質問があります:

  • ハンドラーでデータの取得と処理を行うのは安全ですか?それとも、セマフォ メカニズムを使用してそれを行うスレッドを起動する必要がありますか?

解決策: 回答とコメントで提案されているように、SA_RESTART を使用すると主な問題が解決するようです。

解決策 2: SIGEV_SIGNAL よりも SIGEV_THREAD を使用しても機能します。SIGEV_THREAD を使用すると、SIGEV_SIGNAL よりも多くのリソースが必要になる可能性があることをどこかで読んだことがあります。ただし、タスクのタイミングに関して大きな違いは見られませんでした。

4

4 に答える 4

1

Linux を使用していることを考えると、timerfd_create代わりに使用することを選択します。

そうすれば、代わりにselect(2)poll(2)、またはepoll(7)だけを使用して、メイン ループ内のシグナル ハンドラの問題を解決することなくタイマー イベントを処理できます。

EINTR(中断されたシステムコール)に関しては、中断された特定のシステムコールを再開するだけで適切に処理されます。

于 2012-07-26T20:30:39.593 に答える
1

タイマーは、SIGALARM を使用して実装される傾向があります。

SIGALARM を含むシグナルの受信により、長時間実行されているシステム コールが早期に EINTR を errno に返します。

SA_RESTART はこれを回避する 1 つの方法であるため、シグナルの受信によって中断されたシステム コールは自動的に再開されます。もう 1 つは、システム コールの errno から EINTR をチェックし、EINTR を受け取ったら再起動することです。

もちろん、 read() と write() を使用すると、ただ再開することはできません。中断したところから再開する必要があります。そのため、これらは送信されたデータの長さを返します。

于 2012-07-26T20:33:08.460 に答える
0

質問 5: メッセージとリアルタイムの定期的なスレッドの使用はまったく問題ありません。ただし、タイマーはシグナルを使用するため、タイマーを完全に使用しないことをお勧めします。私は自分でこの問題に遭遇し、最終的にタイマーを単純な clock_nanosleep() に置き換えました。これは、TIMER_ABSTIME を使用して更新された時間を使用して、目的のレートを維持します (つまり、期間を絶対時間に追加します)。その結果、コードが単純になり、シグナルに関する問題がなくなり、シグナルベースのタイマーよりも正確なタイマーが得られました。ところで、ハンドラーでタイマーの期間を測定して、十分に正確であることを確認する必要があります。タイマーの経験は8年前なので、精度の問題は修正されるかもしれません。ただし、シグナルに関する他の問題はシグナル自体に固有のものであるため、「解決」することはできません。回避するしかありません。

また、ハンドラーからデータを取得しても問題はないと思います。データを取得する際のレイテンシーが確実に短縮されるはずです。

于 2012-07-27T18:49:44.630 に答える
0

中断されたシステム コールを再開することは、 に対する正しい応答EINTRです。「接続拒否」の問題は無関係なエラーです。UDP ソケットでは、そのソケットで送信された以前のパケットが宛先によって拒否されたことを示します (ICMP メッセージを介して通知されます)。

于 2012-07-27T04:36:34.400 に答える