web-dev-qa-db-ja.com

シリアルポート(C / C ++)から読み取るときにread()のタイムアウトを実装するにはどうすればよいですか?

ファイル記述子とposix/unix read()関数を使用して、C++のシリアルポートからバイトを読み取っています。この例では、シリアルポートから1バイトを読み取っています(わかりやすくするために、ボーレート設定と同様のものは省略されています)。

#include <termios.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
   int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
   char buf[1];
   int bytesRead = read(fd, buf, 1);
   close(fd);
   return 0;
}

/ dev/ttyS0に接続されているデバイスが情報を送信しない場合、プログラムはハングします。タイムアウトを設定するにはどうすればよいですか?

私はこのようにタイムアウトを設定しようとしました:

struct termios options;
tcgetattr(fd, &options);
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);

1秒のタイムアウトになるはずだと思っていましたが、違いはありません。私はVMINとVTIMEを誤解したと思います。 VMINとVTIMEは何に使用されますか?

それから私はウェブを検索し、誰かがselect()関数について話しているのを見つけました。それが解決策ですか?もしそうなら、それを上記のプログラムにどのように適用して1秒のタイムアウトを作成しますか?

どんな助けでも大歓迎です。前もって感謝します :-)

13
pvh1987

はい、 select(2) を使用します。読み取りセットと空の書き込み/例外セットにfdのみを含むファイル記述子セットを渡し、適切なタイムアウトを渡します。例えば:

int fd = open(...);

// Initialize file descriptor sets
fd_set read_fds, write_fds, except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(fd, &read_fds);

// Set timeout to 1.0 seconds
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;

// Wait for input to become ready or until the time out; the first parameter is
// 1 more than the largest file descriptor in any of the sets
if (select(fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1)
{
    // fd is ready for reading
}
else
{
    // timeout or error
}
20
Adam Rosenfield

VMINとVTIMEは何に使用されますか?

MIN> 0およびTIME = 0の場合、MINは、読み取りが完了する前に受信する文字数を設定します。 TIMEはゼロなので、タイマーは使用されません。

MIN = 0およびTIME> 0の場合、TIMEはタイムアウト値として機能します。単一の文字が読み取られた場合、またはTIMEを超えた場合(t = TIME * 0.1 s)、読み取りは満たされます。 TIMEを超えると、文字は返されません。

MIN> 0およびTIME> 0の場合、TIMEは文字間タイマーとして機能します。 MIN文字を受信した場合、または2文字間の時間がTIMEを超えた場合、読み取りは満たされます。タイマーは、文字が受信されるたびに再起動され、最初の文字が受信された後にのみアクティブになります。

MIN = 0およびTIME = 0の場合、読み取りはすぐに実行されます。現在使用可能な文字数、または要求された文字数が返されます。 Antonino(寄稿を参照)によると、fcntl(fd、F_SETFL、FNDELAY);を発行できます。同じ結果を得るために読む前に。

ソース: http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

3
ceyun

キャプチャ信号を試行して、読み取り操作を停止できます。読み取りの前にalarm(1)を使用し、読み取り関数が返されない場合、alarmはSIGALRM信号を送信します。次に、次のように、この信号をキャプチャする信号処理関数を作成できます。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>

static jmp_buf env_alarm;

static void sig_alarm(int signo)
{
    longjmp(env_alarm, 1);
}

int main(void)
{
   int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
   char buf[1];

   if (signal(SIGALRM, sig_alarm) == SIG_ERR)
   {
       exit(0);
   }

   if (setjmp(env_alarm) != 0)
   {
      close(fd);
      printf("Timeout Or Error\n");
      exit(0);
   }

   alarm(1);
   int bytesRead = read(fd, buf, 1);
   alarm(0);

   close(fd);
   return 0;
}

ただし、プログラムが大きい場合は、select、poll、またはepollを使用する方が適切です。

1
vincent

select()は、この問題を解決する方法です。

Select()の使用方法に関する情報を提供するインターネット上のページがいくつかあります。たとえば、 http://www.unixguide.net/unix/programming/2.1.1.shtml

0
JoeyG

いくつかの可能なアプローチがあります。プログラムが最終的に複数のI/O操作のタイミングをとる場合は、select()が明確な選択です。

ただし、入力がこのI/Oからのみである場合は、非ブロッキングI/Oとタイミングを選択するのが簡単な方法です。より一般的に完全な例にするために、単一文字のI/Oから複数文字に拡張しました。

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/time.h>

int main(void)
{
   int   fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);  // sometimes "O_NONBLOCK"
   char  buf[10];
   int   done = 0, inbuf = 0;
   struct timeval start, now;

   gettimeofday (&start, NULL);
   while (!done)
   {
       int bytesRead = read(fd, &buf[inbuf], sizeof buf - inbuf);
       if (bytesRead < 0)
       {
            error_processing_here();
            continue;
       }
       if (bytesRead == 0)  // no data read to read
       {
            gettimeofday (&now, NULL);
            if ((now.tv.sec  - start.tv_sec) * 1000000 + 
                 now.tv.usec - start.tv_usec > timeout_value_in_microsecs)
            {
                done = 2;    // timeout
                continue;
            }
            sleep(1);  // not timed out yet, sleep a second
            continue;
       }
       inbuf += bytesRead;
       if (we have read all we want)
           done = 1;
   }
   if (done == 2)
        timeout_condition_handling();
   close(fd);
   return 0;
}
0
wallyk