2017-03-21 6 views
2

Linuxのシリアルポートまたはソケットでnバイトのデータを待つ必要があります。 現在、私は時間を測定、世論調査でループを使用してタイムアウトをデクリメント:シリアルポートにC:タイムアウトのあるブロッキングソケットでn文字待機する

static int int_read_poll(int fd, uint8_t *buffer, size_t count, int timeout) 
{ 
    struct pollfd pfd; 
    int rc; 

    pfd.fd = fd; 
    pfd.events = POLLIN; 

    rc = poll(&pfd, 1, timeout); 
    if (rc < 0) { 
     perror("poll"); 
     return 0; 
    } 

    if (rc > 0) { 
     if (pfd.revents & POLLIN) { 
      rc = read(fd, buffer, count); 
      return rc; 
     } 
    } 
    return 0; 
} 

static int int_read_waitfor(int fd, uint8_t *buffer, size_t count, int timeout) 
{ 
    int rc; 
    struct timespec start, end; 
    int delta_ms; 
    int recv = 0; 

    do { 
     clock_gettime(CLOCK_MONOTONIC_RAW, &start); 
      rc = int_read_poll(fd, buffer + recv, count - recv, timeout); 
     clock_gettime(CLOCK_MONOTONIC_RAW, &end); 
     delta_ms = (end.tv_nsec - start.tv_nsec)/1000000; 

     if (!rc || (rc < 0)) return 0; 
     recv += rc; 
     timeout -= delta_ms; 

     if (timeout <= 0) 
      return 0; 

    } while (recv != count); 
    return recv; 
} 

、世論調査では、各単一バイトに返し、多くの反復が発生します。

この問題を解決するためのより洗練された方法がありますか?

ボーレートによっては、タイムアウトがそのコード部分で減少しないことがあります。ナノ秒を数えるほうが良いアプローチかもしれません。

答えて

0

お寄せいただきありがとうございます!

いくつかのテストの結果、関数をライブラリに移植したりソースとして公開したりすると、アプリケーションを妨害する可能性があるため、最後にシグナルを使用しないことにしました。

私は最終的にポーリングとのtermiosを使用ニート溶液(4つのみシステムコール)が見つかりました:通常パケットベース、シリアルポート(NB:非標準モード)であるネットワークソケットとは異なり

static int int_read_waitfor(int fd, uint8_t *buffer, size_t count, int timeout) 
{ 
    struct termios tm; 
    struct pollfd pfd; 
    int rc; 

    tcgetattr(fd, &tm); 
    tm.c_cc[VTIME] = 1; // inter-character timeout 100ms, valid after first char recvd 
    tm.c_cc[VMIN] = count; // block until n characters are read 
    tcsetattr(fd, TCSANOW, &tm); 

    pfd.fd = fd; 
    pfd.events = POLLIN; 

    rc = poll(&pfd, 1, timeout); 
    if (rc > 0) { 
     rc = read(fd, buffer, count); 
     if (rc == -1) { 
      perror("read"); 
      return 0; 
     } 
     return rc; 
    } 

    return 0; 
} 

文字でありますベース。ポーリングを伴うループは、到着するすべてのキャラクタに対して、特に低いボーレートで反復されることが期待される。

私のアプリケーションシリアルラインを介してデバイスにコマンドラインを送信し、応答を待ちます。 答えが受信されない場合、タイムアウトが発生し、再試行します。

termiosオプション "VMIN"は、私が誇示したい文字の数を指定できるので便利です。通常、n個の文字が到着するまで読み取りはブロックされます。

答えがない場合、コマンドは永遠にブロックされます。

VMIN> 0と組み合わせてtermiosオプション "VTIME"を指定すると、文字間のタイムアウトを1秒単位で指定します(1 = 100ms)。これは便利ですが、タイムアウトは最初の文字の受信後にのみ開始されます。さもなければ、文字間タイムアウトは意味をなさないでしょう。

したがって、私はtermiosオプションだけを使用すると、読み込みはスレーブシリアルデバイスのブロックが死んでしまうでしょう。

この問題を回避するために、私は読み込みの前にポーリングを使用します。 最初の文字が到着すると(ポーリングはrc = 1で返されます)、私は読み始めます。 「VTIME」もアクティブで、100msのインターキャラクター時間(可能な限り低い設定)を強制します。

はタイムアウト処理が最適化されたボーナスとして:

は、スレーブデバイスが死んでいる場合は、スレーブが動作すると応答した場合、ポーリングが400msで

  • 後に戻ります400msの

    • のタイムアウトを取ることができます50ms以内に(最初の文字)、ポーリングが戻り、読み取りが開始されます。スレーブがデータをあまり送信しない場合、VTIMEは50ms + 100ms後に受信を停止し、受信を停止します。最後の(欠落した)バイトが到着するまで400msの間待つ必要はありません。
  • +0

    いくつかのシリアルコマンドを使用した私のサンプルアプリケーションでは、前のコードでは合計272のシステムコールが使用されました。 –

    0

    単純な解決策は、alarm(タイムアウトが秒単位である場合)またはとITIMER_REALタイマーを使用することです。信号が発生したときにエラーが発生してreadコールが返される(errno == EINTR

    +0

    setitimerで 'int_read_waitfor'を変更し、ソケットレベルでVMIN = countを使用しました。それでも6つのシステムコールを使用しますが、正常に動作します。とにかく:プロセス全体ではなくソケットだけに信号を送る方法はありますか? –

    +0

    @ broth-itkいいえ、ソケットだけに信号を送ることはできません。シグナルはプロセスプリミティブであり、ソケットプリミティブではありません。 –

    +1

    @ broth-itk:しかし、ソケット読み取りでブロックしている特定のスレッド*だけに信号を送ることは可能です。これは 'timer_create()'を 'SIGEV_THREAD_ID'型' struct sigevent'と使い、 'pthread_t'を' tid'にマッピングすることでLinuxで行うことができます。より良いPOSIX移植可能なオプションは、空のハンドラ本体にPOSIXリアルタイム信号(例えば、 'SIGRTMIN + 0')を使用することです(その配送はブロックされたシステムコールを中断します)。' pthread_killそれらがタイムアウトしたときに他のスレッドを中断させます。 –

    関連する問題