1

私は Windows でかなりのプログラミングを行ってきましたが、今では初めての Linux アプリを作成する必要があります。

UDP を使用してハードウェア デバイスと通信する必要があります。サイズが 40 バイトのパケットを 1 秒間に 60 個送信する必要があります。1 秒以内に 60 未満のパケットを送信すると、悪いことが起こります。パケットのデータの生成には時間がかかる場合があります。ただし、データを送信する準備ができていない場合は、前回送信したデータと同じデータを送信してもかまいません。コンピューターはコマンドラインのみのセットアップであり、このプログラムのみを実行します。

私は Linux についてあまり詳しくないので、これらの要件を満たすためにアプリをどのようにセットアップするかについての一般的なアイデアを得たいと思っていました。私は次のような答えを望んでいました:

2 つのスレッドを作成します。1 つはパケットの送信用で、もう 1 つは計算用です。

しかし、それがそれほど単純であるかどうかはわかりません(多分そうです)。たぶん、共有メモリなどからパケットを送信したばかりのある種のデーモンを作成し、別のアプリに計算をさせる方が信頼性が高いでしょうか? 複数プロセスのソリューションである場合、どの通信メカニズムをお勧めしますか? アプリを通常よりも優先する方法や、それに類似した方法はありますか?

PS: 防弾性が高いほど良いです!

4

10 に答える 10

3

私は同様のプロジェクトを行ったことがあります。組み込み Linux コンピュータ上の単純なソフトウェアで、通常の速度で CAN メッセージを送信します。

私は2つのスレッドのアプローチに行きます。送信スレッドの優先順位を少し高くし、他のスレッドがそれらのブロックの計算に時間がかかっている場合は、同じデータ ブロックをもう一度送信するようにします。

1 秒あたり 60 UDP パケットは、ほとんどのシステム (組み込みシステムを含む) ではかなり緩和されているため、スレッド間のデータ共有とパケットの送信を最適化することにあまり労力を費やすことはありません。

実際、私は次のように言います:シンプルに保ちましょう!あなたは本当にシステム内の唯一のアプリであり、そのシステムを適切に制御できます。複雑な IPC スキームやその他のトリックから得るものは何もありません。シンプルに保つことで、欠陥の少ない、より短い時間でより良いコードを作成することができます。つまり、実際には、テストにより多くの時間を費やすことができます。

于 2008-12-23T05:15:40.870 に答える
1

あなたが提案した2つのスレッドが機能します。それらの間に pipe() がある場合、計算スレッドは生成されたときにパケットを提供できますが、通信スレッドは select() を使用して新しいデータがあるかどうかを確認します。そうでない場合は、キャッシュから最後のものを送信するだけです。

私は問題を少し単純化しすぎたかもしれません...

于 2008-12-23T05:01:55.030 に答える
1

スレッドのペアを使用するという提案は、計算を実行する負荷がそれほど大きくない限り、うまくいくように思えます。

pipe()Cogsy が提案するように使用する代わりに、ミューテックスを使用して、計算スレッドの出力を格納するために使用するメモリのチャンクをロックする傾向があります-スレッド間の転送領域として使用します。

計算スレッドがバッファーに出力する準備ができたら、mutex を取得し、転送バッファーに書き込み、mutex を解放します。

送信スレッドがパケットを送信する準備ができると、ミューテックスをロックしようとします。ロックを取得した場合は、転送バッファのコピーを取得して送信します。ロックを取得できない場合は、最後のコピーを送信します。

「nice」を使用し、負の調整値を指定してプロセスの優先度を高くすることで、プロセスの優先度を制御できます。負の値を指定できるようにするには、スーパーユーザーとして (ルートとして、または「sudo」を使用して) これを行う必要があることに注意してください。


編集: 追加するのを忘れました -これは Linux の pthreads に関する優れたチュートリアルです。また、ミューテックスの使用についても説明します。

于 2008-12-23T05:19:12.843 に答える
1

60 パケット/秒の要件がどれほど難しいか、よくわかりませんでした。1 秒あたり 60 パケットのバーストで要件を満たすことができますか? それとも、各パケット間に 1/60 秒の鋭い間隔が必要ですか?

少し話が逸れるかもしれませんが、もう 1 つの重要な問題は、Linux ボックスをどのように構成するかです。私自身、リアルタイムのLinux カーネルを使用して、不要なサービスをすべて無効にします。そうしないと、選択したアーキテクチャに関係なく、アプリケーションがある時点でパケットを見逃すという実際のリスクがあります。

いずれにせよ、2 つのスレッドがうまく機能するはずです。

于 2008-12-23T05:48:04.827 に答える
0

私はこの回答を投稿して、「明白な」アプローチとはまったく異なるアプローチを説明しました。誰かがそれがまさに必要なものであることに気付くことを期待しています。ベストアンサーに選ばれるとは思っていませんでした!潜在的な危険性と同時実行性の問題があるため、このソリューションは注意して扱ってください...

setitimer()システムコールを使用して、指定されたミリ秒数後にSIGALRM(アラーム信号)をプログラムに送信できます。シグナルは、実行中のプログラムを中断してシグナルハンドラーを実行させる非同期イベント(メッセージに少し似ています)です。

プログラムの開始時にOSによって一連のデフォルトのシグナルハンドラーがインストールされますが、sigaction()を使用してカスタムシグナルハンドラーをインストールできます。

したがって、必要なのは単一のスレッドだけです。グローバル変数を使用して、シグナルハンドラーが必要な情報にアクセスし、新しいパケットを送信したり、必要に応じて最後のパケットを繰り返したりできるようにします。

これがあなたの利益のための例です:

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
int ticker = 0;

void timerTick(int dummy)
{
    printf("The value of ticker is: %d\n", ticker);
}

int main()
{
    int i;

    struct sigaction action;
    struct itimerval time;

    //Here is where we specify the SIGALRM handler
    action.sa_handler = &timerTick;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    //Register the handler for SIGALRM
    sigaction(SIGALRM, &action, NULL);

    time.it_interval.tv_sec = 1;       //Timing interval in seconds
    time.it_interval.tv_usec = 000000; //and microseconds
    time.it_value.tv_sec = 0;  //Initial timer value in seconds
    time.it_value.tv_usec = 1; //and microseconds

    //Set off the timer
    setitimer(ITIMER_REAL, &time, NULL);

    //Be busy
    while(1)
        for(ticker = 0; ticker < 1000; ticker++)
            for(i = 0; i < 60000000; i++)
                ;
}
于 2008-12-23T06:32:15.487 に答える
0

2 つのスレッドが機能します。送信スレッドが更新の途中でそれを認識しないように、共有データ構造をロックする必要があります。

1 秒あたり 60 回というのは、それほど難しくはありません。

スケジューリングについて本当に心配している場合は、送信スレッドのスケジューリング ポリシーを SCHED_FIFO に設定し、そのメモリを mlockall() に設定します。そうすれば、パケットの送信を止めることはできません(他のものが同時に送信されている場合でも、遅れて送信される可能性があります)

デバイスにはある程度の許容範囲が必要です。1 秒あたり 60 パケットは問題ありませんが、デバイスの許容範囲はどのくらいですか? 毎秒20?受信しないとデバイスが故障する場合は、必要な速度の 3 倍の速度で送信します。

于 2008-12-23T13:07:12.540 に答える
0

私はスレッドから離れて、プロセスと (おそらく) シグナルとファイルを使用します。送信しないと「悪いこと」が発生する可能性があると言うので、ロックアップや競合状態を回避する必要があります。また、個別のプロセスとデータをファイルに保存すると、より簡単に実行できます。

データをファイルに保存し、名前を変更して新たに開始するプロセスの行に沿った何か。もう一方のプロセスは現在のファイルを取得し、その内容を 1 秒に 1 回送信します。

Windows とは異なり、開いているファイルをコピー (移動)できます。

于 2008-12-23T13:37:17.657 に答える
0

長年にわたる Unix のベスト プラクティスに従ってください。シンプルでモジュール化された状態に保ち、アクションを分離し、可能な限り多くの作業を OS に任せてください。

ここでの答えの多くは正しい軌道に乗っていますが、もっと簡単にできると思います。

  • 2 つの別個のプロセスを使用します。1 つはデータを作成して stdout に書き込み、もう 1 つは stdin からデータを読み取って送信します。基本的な I/O ライブラリにプロセス間のデータ ストリーム バッファリングを処理させ、OS にスレッド管理を処理させます。

  • 最初にタイマー ループと偽のデータのバッファーを使用して基本的な送信側を構築し、適切な頻度でデバイスに送信します。

  • 次に、送信者が stdin からデータを読み取れるようにします。たとえば、「sender < textdata」などのように、ファイルからデータをリダイレクトできます。

  • 次にデータ プロデューサーをビルドし、その出力を送信側にパイプします (例: "producer | sender")。

これで、送信者側をいじることなく、必要に応じて新しいプロデューサーを作成できるようになりました。この回答は、一方向の通信を前提としています。

特に Linux/Unix ベースのシステムにまだあまり精通していない場合は、答えをできるだけシンプルに保つことでより多くの成功を収めることができます。これは新しいシステムを学ぶ絶好の機会ですが、やりすぎないようにしてください。ツールがあれば複雑な答えにジャンプするのは簡単ですが、簡単なこてで十分な場合にブルドーザーを使用する必要はありません。ミューテックス、セマフォ、共有メモリなどはすべて便利で利用可能ですが、実際には必要ないほど複雑になります。

于 2008-12-28T02:24:55.937 に答える
0

みんなありがとう、私はみんなのアドバイスを利用します。1 つよりも多くの回答を選択できればと思います。

興味のある方へ。デバイスのソースがありません。独自のロックダウン システムです。1 秒あたり 60 パケットがどれだけうるさいかを確認するには、まだ十分なテストを行っていません。それは、彼らの限られたドキュメントが「毎秒60パケット」と言っているだけです。ただし、デバイスの性質上、パケットのバーストは好ましくありません。時折見逃されるパケットを補うために、1秒間に60回以上送信することでうまくいくと思います..

于 2009-01-08T10:30:10.713 に答える
0

2 スレッド アプローチに同意します。また、2 つの静的バッファーと共有列挙型も使用します。送信スレッドには、このロジックが必要です。

loop
    wait for timer
    grab mutex
    check enum {0, 1}
    send buffer 0 or 1 based on enum
    release mutex
end loop

他のスレッドには次のロジックがあります。

loop
    check enum
    choose buffer 1 or 0 based on enum (opposite of other thread)
    generate data
    grab mutex
    flip enum
    release mutex
end loop

このようにして、送信者は、データを送信している間、常に有効なバッファーを保持します。ジェネレーター スレッドのみがバッファー ポインターを変更でき、送信が進行中でない場合にのみ変更できます。さらに、enum フリップは、優先順位の高い送信側スレッドを非常に長い間遅らせるほど多くのサイクルをとるべきではありません。

于 2008-12-28T03:11:28.773 に答える