Вы не должны использовать SIGIO.Я никогда не нашел хорошей документации, чтобы использовать это, чтобы сделать что-нибудь, и есть лучшие способы.Существует poll , select и epoll семейств функций, которые будут работать намного лучше для этого.Мне кажется, что ваша программа занята ожиданием получения данных, что означает, что она расходует процессорное время.Альтернативы позволяют вашей программе засыпать, когда нечего делать.Вы можете использовать pause , чтобы получить этот эффект, и программа перейдет в спящий режим, пока не получит сигнал (например, SIGIO).Вы также можете просто использовать файл tty, открытый в режиме блокировки, и позволить чтению блокировать вас, пока не произойдет чтение.
Вам не следует использовать printf
из обоих обработчиков сигналови обычный код.Обычно вам никогда не следует использовать операции stdio из обработчика сигнала, потому что он может спать, но вы часто можете избежать неприятностей во время тестирования.Использование его как из обработчика сигнала, так и из обычного кода (или в нескольких обработчиках сигнала, если один сигнал не блокирует другие), плохо, потому что тогда функции будут получать доступ к одним и тем же данным.По существу, printf
в вашем обычном коде может быть прервано SIGIO, и тогда вызывается printf
, и оба printf
хотят получить доступ к внутренним данным FILE stdout
- либо к этим, либо к блокировкам (которыепредназначена для остановки, чтобы блокировать разные потоки, а не тот же поток, вызывающий второй раз), заблокирует вызов обработчика SIGIO для printf
, который заблокирует всю программу.Проблема здесь называется состоянием гонки и может произойти или не произойти в зависимости от времени.
В коде:
_
do {
res = read(fd,buf+offset,255);
offset += res;
} while (offset < bytes_expected);
_
Вы постоянно передаете 255 нужной длины, даже если вы уже заполнили часть буфера.Если вы в первый раз проходите через цикл, вы получаете 254 байта данных, а затем снова вызываете, запрашивая 255 байтов, и получаете более 1 байта, что вы переполнили буфер.Кроме того, вы никогда не сбрасываете offset
обратно на 0
, поэтому он просто увеличивается и увеличивается, поэтому у вас возникает переполнение буфера, если ваша программа когда-либо читает более 255 байтов.
Вы также не проверяетечто возвращаемое значение read
не равно -1.Если бы это было -1, то ваша программа делает очень плохие вещи.
Кроме того, нет никакой реальной причины (которую я вижу), чтобы попытаться накопить 255 байт данных, прежде чем записывать уже имеющиеся данныечитать обратно.Даже если вы хотите обрабатывать только первые 255 байтов, поступающих в последовательный порт, вы можете продолжить и записать первые по мере поступления последних, а затем выйти из цикла после того, как будет достигнут предел 255.
- Вы устанавливаете только одно значение в
struct sigaction saio
, но оно имеет другие поля.Они должны быть установлены на что-то или вы просто добавляете случайные биты как флаги и маски.
_
struct sigaction saio = {0};
saio.sa_handler = signal_handler_IO;
/* Now you don't have to worry about the flags and mask b/c those both are
zeroed out and I'm pretty sure those are decent values for your purposes,
but you could still set them explicitly. */
_
- Вы передаете
newtio
tcsetattr
, но возможно, что вы не полностью его инициализировали,Вы должны были сделать:
_
tcgetattr(fd,&oldtio); /* save current port settings */
memcpy(&newtio, oldtio, sizeof(struct termios) ); /* !! <-- This line !! */
, прежде чем устанавливать другие флаги.Таким образом, вы можете просто использовать значения, которые уже были там, для любых полей, которые вы не устанавливали явно.
Кроме того, вы, кажется, устанавливаете все флаги BAUDRATE .
/* set new port settings for canonical input processing */
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
Это, скорее всего, неправильно.BAUDRATE
используется для маскировки кода бода следующим образом: tcflag_t baud_code = tio.c_cflag & BAUDRATE;
И теперь baud_code
имеет только биты, соответствующие набору бодов, без CRTSCTS
и CLOCAL
набор вещей.Вам следует использовать один из кодов скорости передачи, который выглядит здесь как B9600
и B115200
вместо BAUDRATE, если только вы не хотите:
tcflag_t non_baud_flags = tio.c_cflag | ~BAUDRATE;
tcflag_t new_flags = non_baud_flags | B9600;
, который только изменит скорость передачи и оставит остальную частьодни флаги.Вот для чего int cfsetspeed(struct termios *termios_p, speed_t speed);
.
- Я также не уверен насчет порядка, в котором вы делаете некоторые вещи.Я думаю, что вы можете открыть себя для получения SIGIO до того, как вы настроите последовательный порт, что, вероятно, даст вам ненужные данные.Вероятно, следует сделать:
_
/* I swapped these two lines */
tcsetattr(fd,TCSANOW,&newtio);
tcflush(fd, TCIFLUSH); /* Now the settings are correct before you flush, so no junk */
/* And moved these from above */
sigaction(SIGIO,&saio,NULL); /* There was no real reason for this to be up there,
but no real harm, either. It just goes better here,
but does need to be set before you set the file as
async. */
/* allow the process to receive SIGIO */
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, FASYNC); /* Now SIGIO won't be called on behalf of this file until after
the serial port has be set up and flushed. */