Thread View: pl.comp.os.linux.programowanie
4 messages
4 total messages
Started by spryciarzsprytny7
Thu, 05 Jul 2018 08:11
Czekanie na wolny bufor portu szeregowego - co robię źle?!?
Author: spryciarzsprytny7
Date: Thu, 05 Jul 2018 08:11
Date: Thu, 05 Jul 2018 08:11
185 lines
8676 bytes
8676 bytes
Witam
Piszę obsługę portu szeregowego w C++ z użyciem Linux Api. 2 obiekty klasy PortSzeregowy mają równolegle zapisywać i odczytywać ten port. Odczyt i zapis generalnie działa. Jednak chcę zapewnić działanie tej kasy (PortSzeregowy) w warunkach ekstremalnych (pod dużym obciążeniem). Dlatego nie robię tej komunikacji na timerach tylko na blokadzie pselect. Ta blokada działa w przypadku odczytu z portu, jednak analogiczny kod nie działa w przypadku zapisu.
Po prostu pselect (funkcja SerialPort::send()) nie zatrzymuje się w momencie gdy bufor wyjściowy portu jest pełen (gdy jest wywoływana przed odczytem). Poniżej zamieszczam funkcje zapisu i odczytu z portu szeregowego. Jeśli to możliwe to proszę o pomoc w zdiagnozowaniu powodu nie zatrzymywania się pselect (funkcja SerialPort::send()) w przypadku gdy bufor wyjściowy portu szeregowego jest pełen.
z góry dzięki i pozdro
Jacek
void SerialPort::receive()
{
// http://www.linuxprogrammingblog.com/code-examples/using-pselect-to-avoid-a-signal-race
// https://www.cmrr.umn.edu/~strupp/serial.html
if(mPortDescriptor < 0)
throw Exception(tr("Zły deskryptor pliku: %1").arg(mPortDescriptor).toUtf8().data());
struct sigaction lSigAction;
memset (&lSigAction, 0, sizeof(lSigAction));
lSigAction.sa_handler = gSignTermHandler;
// This server should shut down on SIGTERM.
if(sigaction(SIGTERM, &lSigAction, 0))
throw Exception(tr("Błąd sigaction! %1, %2").arg(errno).arg(strerror(errno)));
sigset_t lMask;
sigemptyset (&lMask);
sigaddset (&lMask, SIGTERM);
sigset_t orig_mask;
if(sigprocmask(SIG_BLOCK, &lMask, &orig_mask) < 0)
throw Exception(tr("Błąd sigprocmask! %1, %2").arg(errno).arg(strerror(errno)));
struct timespec timeout;
timeout.tv_sec = 1;
timeout.tv_nsec = 0;
fd_set lFileDescriptorSet;
/* BANG! we can get SIGTERM at this point, but it will be
* delivered while we are in pselect(), because now
* we block SIGTERM.
*/
FD_ZERO(&lFileDescriptorSet);
FD_SET(mPortDescriptor, &lFileDescriptorSet);
while(true)
{
int lRes = pselect(mPortDescriptor + 1, &lFileDescriptorSet, NULL, NULL, &timeout, &orig_mask);
if(mEnd)
return;
if((lRes < 0) && (errno != EINTR))
throw Exception(tr("Bład pselect! %1, %2").arg(errno).arg(strerror(errno)));
if(lRes == 0) // timeout
continue;
if(gExitRequest)
throw Exception(tr("Przechwycono sygnał SIGTERM! Koniec odbierania danych."));
if(!FD_ISSET(mPortDescriptor, &lFileDescriptorSet))
continue;
int lSize = 0;
ioctl(mPortDescriptor, FIONREAD, &lSize);
if(lSize <= 0)
continue;
QByteArray lResult;
lResult.resize(lSize);
int lReaded(0), lPartial(0);
while(lReaded < lSize)
{
if(mEnd)
return;
lPartial = ::read(mPortDescriptor, lResult.data(), lSize);
if(lPartial > 0)
lReaded += lPartial;
else if(lPartial < 0)
{
qCritical() << tr("Uwaga! Błąd czytania portu szeregowego! Kod błędu: %1, wiadomość: %2").arg(errno).arg(strerror(errno));
break;
}
}
emit read(lResult);
}
}
void SerialPort::send()
{
// http://www.linuxprogrammingblog.com/code-examples/using-pselect-to-avoid-a-signal-race
// https://www.cmrr.umn.edu/~strupp/serial.html
if(mPortDescriptor < 0)
throw Exception(tr("Zły deskryptor pliku: %1").arg(mPortDescriptor).toUtf8().data());
struct sigaction lSigAction;
memset (&lSigAction, 0, sizeof(lSigAction));
lSigAction.sa_handler = gSignTermHandler;
// This server should shut down on SIGTERM.
if(sigaction(SIGTERM, &lSigAction, 0))
throw Exception(tr("Błąd sigaction! %1, %2").arg(errno).arg(strerror(errno)));
sigset_t lMask;
sigemptyset(&lMask);
sigaddset(&lMask, SIGTERM);
sigset_t orig_mask;
if(sigprocmask(SIG_BLOCK, &lMask, &orig_mask) < 0)
throw Exception(tr("Błąd sigprocmask! %1, %2").arg(errno).arg(strerror(errno)));
struct timespec timeout;
timeout.tv_sec = 1;
timeout.tv_nsec = 0;
fd_set lFileDescriptorSet;
/* BANG! we can get SIGTERM at this point, but it will be
* delivered while we are in pselect(), because now
* we block SIGTERM.
*/
FD_ZERO(&lFileDescriptorSet);
FD_SET(mPortDescriptor, &lFileDescriptorSet);
int lNumberToAquire(0);
while(true)
{
lNumberToAquire = 0;
if(mBufferEnd > mBufferStart)
lNumberToAquire = mBufferEnd - mBufferStart;
else if(mBufferEnd < mBufferStart)
lNumberToAquire = mSendBuffer.size() - mBufferStart;
if(lNumberToAquire <= 0)
lNumberToAquire = 1;
int lFreeSpaceSize = 0;
ioctl(mPortDescriptor, TIOCOUTQ, &lFreeSpaceSize);
if(lFreeSpaceSize <= 0) // we must wait for serial port
{
int lRes = pselect(mPortDescriptor + 1, NULL, &lFileDescriptorSet, NULL, &timeout, &orig_mask);
if(mEnd)
return;
if((lRes < 0) && (errno != EINTR))
throw Exception(tr("Bład pselect! Kod błedu: %1, komunikat: %2").arg(errno).arg(strerror(errno)));
if(lRes == 0) // timeout
continue;
if(gExitRequest)
throw Exception(tr("Przechwycono sygnał SIGTERM! Koniec wysyłania danych."));
if(!FD_ISSET(mPortDescriptor, &lFileDescriptorSet))
continue;
}
ioctl(mPortDescriptor, TIOCOUTQ, &lFreeSpaceSize);
if(lFreeSpaceSize <= 0)
{
qWarning() << tr("%1 Uwaga: Brak miejsca w bufoerze wyjściowym portu szeregowego!").arg(QTime::currentTime().toString("hh:mm:ss.z"));
continue;
}
lNumberToAquire = qMin(lNumberToAquire, lFreeSpaceSize);
//#ifdef DEBUG
// int lSemaphore(mAvailableData.available());
//#endif
if((lNumberToAquire > 0) && mAvailableData.tryAcquire(lNumberToAquire, 1000))
{
//#ifdef DEBUG
// int lSemaphore2(mAvailableData.available());
//#endif
int lWriten(0), lPartial(0);
while(lWriten < lNumberToAquire)
{
lPartial = ::write(mPortDescriptor, mSendBuffer.data() + mBufferStart, lNumberToAquire);
if(lPartial > 0)
lWriten += lPartial;
else if(lPartial < 0)
{
qCritical() << tr("Błąd zapisu do portu szeregowego!!! Kod błędu: %1, Wiadomość błędu: %2").arg(errno).arg(strerror(errno));
break;
}
}
// qDebug() << QTime::currentTime().toString("hh:mm:ss.z") << "Write to the serial port.";
mBufferStart = (mBufferStart + lNumberToAquire) % mSendBufferSize;
mFreeSpace.release(lNumberToAquire);
}
if(mEnd)
return;
}
}
Re: Czekanie na wolny bufor portu szeregowego - co robię źle?!?
Author: Bogdan (bogdro)
Date: Sat, 07 Jul 2018 13:41
Date: Sat, 07 Jul 2018 13:41
64 lines
3717 bytes
3717 bytes
W dniu 05.07.2018 o 10:11, spryciarzsprytny7@gmail.com pisze:
> Witam
> Piszę obsługę portu szeregowego w C++ z użyciem Linux Api. 2 obiekty klasy PortSzeregowy mają równolegle zapisywać i odczytywać ten port. Odczyt i zapis generalnie działa. Jednak chcę zapewnić działanie tej kasy (PortSzeregowy) w warunkach ekstremalnych (pod dużym obciążeniem). Dlatego nie robię tej komunikacji na timerach tylko na blokadzie pselect. Ta blokada działa w przypadku odczytu z portu, jednak analogiczny kod nie działa w przypadku zapisu.
>
> Po prostu pselect (funkcja SerialPort::send()) nie zatrzymuje się w momencie gdy bufor wyjściowy portu jest pełen (gdy jest wywoływana przed odczytem). Poniżej zamieszczam funkcje zapisu i odczytu z portu szeregowego. Jeśli to możliwe to proszę o pomoc w zdiagnozowaniu powodu nie zatrzymywania się pselect (funkcja SerialPort::send()) w przypadku gdy bufor wyjściowy portu szeregowego jest pełen.
>
> z góry dzięki i pozdro
> Jacek
Funkcja select() i pochodne usuwają deskryptory ze zbiorów.
Przypuszczam, że po pierwszym "trafieniu" kolejne wywołania sprawdzają
już puste zbiory. Musisz robić FD_SET (a najlepiej też FD_ZERO) w
każdej iteracji pętli, przed wywołaniem pselect().
> void SerialPort::receive()
> {
[...]
> FD_ZERO(&lFileDescriptorSet);
> FD_SET(mPortDescriptor, &lFileDescriptorSet);
>
> while(true)
> {
> int lRes = pselect(mPortDescriptor + 1, &lFileDescriptorSet, NULL, NULL, &timeout, &orig_mask);
[...]
>
> void SerialPort::send()
> {
[...]
> FD_ZERO(&lFileDescriptorSet);
> FD_SET(mPortDescriptor, &lFileDescriptorSet);
>
> int lNumberToAquire(0);
> while(true)
> {
> lNumberToAquire = 0;
> if(mBufferEnd > mBufferStart)
> lNumberToAquire = mBufferEnd - mBufferStart;
> else if(mBufferEnd < mBufferStart)
> lNumberToAquire = mSendBuffer.size() - mBufferStart;
> if(lNumberToAquire <= 0)
> lNumberToAquire = 1;
>
> int lFreeSpaceSize = 0;
> ioctl(mPortDescriptor, TIOCOUTQ, &lFreeSpaceSize);
> if(lFreeSpaceSize <= 0) // we must wait for serial port
> {
> int lRes = pselect(mPortDescriptor + 1, NULL, &lFileDescriptorSet, NULL, &timeout, &orig_mask);
[...]
--
Pozdrawiam/Regards - Bogdan (GNU/Linux & FreeDOS)
Kurs asemblera x86 (DOS, GNU/Linux): http://bogdro.evai.pl
Grupy dyskusyjne o asm: pl.comp.lang.asm alt.pl.asm alt.pl.asm.win32
www.Xiph.org www.TorProject.org Soft(EN): http://bogdro.evai.pl/soft
Re: Czekanie na wolny bufor portu szeregowego - co robię źle?!?
Author: spryciarzsprytny7
Date: Tue, 10 Jul 2018 06:13
Date: Tue, 10 Jul 2018 06:13
10 lines
2113 bytes
2113 bytes
> Funkcja select() i pochodne usuwają deskryptory ze zbiorów. Jak możesz tak pisać, skoro select/pselect zwraca liczbę gotowych do pracy deskryptorów i one są w tej strukturze fd_set - sprawdza się je makrem FD_ISSET(). Inna sprawa: co w sytuacji gdy ta struktura ponownie zostanie podana do pselect/slelect gdy ma już ustawiony bit "set". I w dodatku co to znaczy: Return Value: Upon successful completion, the pselect() and select() functions shall return the total number of bits set in the bit masks. Co to za maska bitowa?!? Czy to to samo co: "zwraca liczbę deskryptorów gotowych do pracy"?!?
Re: Czekanie na wolny bufor portu szeregowego - co robię źle?!?
Author: Adam Wysocki
Date: Tue, 10 Jul 2018 09:47
Date: Tue, 10 Jul 2018 09:47
32 lines
2379 bytes
2379 bytes
spryciarzsprytny7@gmail.com wrote: >> Funkcja select() i pochodne usuwają deskryptory ze zbiorów. > > Jak możesz tak pisać, skoro select/pselect zwraca liczbę gotowych do > pracy deskryptorów i one są w tej strukturze fd_set - sprawdza się je > makrem FD_ISSET(). Inna sprawa: co w sytuacji gdy ta struktura ponownie > zostanie podana do pselect/slelect gdy ma już ustawiony bit "set". No właśnie. select() modyfikuje przekazany fd_set tak, żeby ustawione były tam jedynie deskryptory, na których wystąpiła aktywność. Inne są zerowane. Jak struktura zostanie przekazana ponownie to select() weźmie pod uwagę tylko te deskryptory, które mają ustawioną flagę w fd_set czyli te, których aktywność zwrócił poprzedni select(). To prawie na pewno nie jest to, co chcesz osiągnąć. Zawsze na nowo dodawaj deskryptory do fd_set przed wywołaniem select(). > Upon successful completion, the pselect() and select() functions shall > return the total number of bits set in the bit masks. > > Co to za maska bitowa?!? > Czy to to samo co: "zwraca liczbę deskryptorów gotowych do pracy"?!? Tak. fd_set może być zaimplementowany jako tablica masek bitowych. -- [ Email: a@b a=grp b=chmurka.net ] [ Web: http://www.chmurka.net/ ]
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads