Linux Blocking Vs. Non Blocking Serial Read

Linux Blocking vs. non Blocking Serial Read

The code you mention is IMO poorly coded and commented. That code does not conform to POSIX practices for portability as described in Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systems. That code does not mention that it uses non-canonical (aka raw) mode, and reuses the "blocking" and "nonblocking" terminology to describe the VMIN and VTIME attributes.

(The author of that code reports that it predates the POSIX standard, and hence its non-compliance. That is understandable, but to then post and advocate the use of old code that may not be portable (i.e. function as expected in an alternate situation) is questionable.)


The conventional definition of a "blocking" versus "nonblocking" read is based on "when" the read call will return to your program (and resume execute with the next statement) and whether there will be data stored in your program's read buffer. A blocking read is the default mode, unless non-blocking is requested by opening the serial terminal with the O_NONBLOCK or O_NDELAY flag.

Canonical mode

For a blocking canonical read call of a serial terminal, a line (aka record) of text will always be returned in the provided buffer (unless an error occurred). The read call will block (i.e. suspend execution of your program) for as long as it takes for a line termination character to be received and processed.

A nonblocking canonical read call of a serial terminal will always return "immediately". The read may or may not return any data.

If (since the previous read call) at least a line of text has been received and stored in the system buffer, then the oldest line will be removed from the system buffer and copied to the program's buffer. The return code will indicate the data length.

If (since the previous read call) a line-termination character has not been received and processed, then there is no (complete) line of text available. The read() will return an EAGAIN error (i.e. a -1 return code and errno set to EAGAIN). Your program can then perform some calculation, or request I/O from another device, or delay/sleep. Either after an arbitrary delay or by notification by poll() or select(), your program can retry the read().

An example program using blocking canonical mode for reads is included in this answer.

Non-canonical mode

When the serial terminal is configured for non-canonical mode, the termios c_cc array elements VMIN and VTIME should be used to control "blocking", but this requires that the terminal be opened in the default blocking mode, i.e. do not specify the O_NONBLOCK open flag.

Otherwise O_NONBLOCK will have precedence over the VMIN and VTIME specification, and read() will set errno to EAGAIN and immediately return -1 instead of 0 when there is no data available. (This is the behavior observed in recent Linux 3.x kernels; older 2.6.x kernels may behave differently.)

The termios man page describes (c_cc array index) VMIN as the "minimum number of characters for noncanonical read", and (c_cc array index) VTIME as the "timeout in deciseconds for noncanonical read".

VMIN should be adjusted by your program to accommodate the typical message or datagram length that is expected and/or the minimum size for data to retrieve & process per read().

VTIME should be adjusted by your program to accommodate the typical burstiness or arrival rate of serial data that is expected and/or the maximum time to wait for data or a datum.

The VMIN and VTIME values interact to determine the criterion for when read should return; their precise meanings depend on which of them are nonzero. There are four possible cases.

This web page explains it as:

  • VMIN = 0 and VTIME = 0

This is a completely non-blocking read - the call is satisfied immediately directly from the driver's input queue. If data are available, it's transferred to the caller's buffer up to nbytes and returned. Otherwise zero is immediately returned to indicate "no data". We'll note that this is "polling" of the serial port, and it's almost always a bad idea. If done repeatedly, it can consume enormous amounts of processor time and is highly inefficient. Don't use this mode unless you really, really know what you're doing.

  • VMIN = 0 and VTIME > 0

This is a pure timed read. If data are available in the input queue, it's transferred to the caller's buffer up to a maximum of nbytes, and returned immediately to the caller. Otherwise the driver blocks until data arrives, or when VTIME tenths expire from the start of the call. If the timer expires without data, zero is returned. A single byte is sufficient to satisfy this read call, but if more is available in the input queue, it's returned to the caller. Note that this is an overall timer, not an intercharacter one.

  • VMIN > 0 and VTIME > 0

A read() is satisfied when either VMIN characters have been transferred to the caller's buffer, or when VTIME tenths expire between characters. Since this timer is not started until the first character arrives, this call can block indefinitely if the serial line is idle. This is the most common mode of operation, and we consider VTIME to be an intercharacter timeout, not an overall one. This call should never return zero bytes read.

  • VMIN > 0 and VTIME = 0

This is a counted read that is satisfied only when at least VMIN characters have been transferred to the caller's buffer - there is no timing component involved. This read can be satisfied from the driver's input queue (where the call could return immediately), or by waiting for new data to arrive: in this respect the call could block indefinitely. We believe that it's undefined behavior if nbytes is less then VMIN.

Note when VMIN=1 that the VTIME specification will be irrelevant. The availability of any data will always satisfy the minimum criterion of a single byte, so the time criterion can be ignored (since it would be an intercharacter time specification with a nonzero VMIN). This special case was pointed out by @IanAbbot.


That code you mention configures "nonblocking" mode as VMIN=0 and VTIME=5. This will not cause the read() to return immediately like a nonblocking canonical read would; with that code a read() should always wait at least a half second before returning.

The conventional definition of a "nonblocking" is that your calling program is not preempted during the syscall and gets control back (almost) immediately.

To get an (unconditional and) immediate return (for a non-canonical read), set VMIN=0 and VTIME=0 (with the attendant warnings).

Best Practise to Read Desired Amount of Data from Serial?

But AFAIK system calls are expensive, ...

True, a system call can consume many more CPU cycles than a local procedure/function call. The system call requires CPU-mode transitions between user-mode to (protected) kernel mode, and then back to user mode.

... thus, isn't that approach somehow crude, especially when the desired length is large?

The first question you have to ask yourself when reading from a serial terminal (e.g. a /dev/ttyXn device)(rather than a serial port) is "what kind of data is going to be received, that is, is the data lines of (ASCII) text terminated by some type of EOL (end of line) character, or does the data need to be simply treated as binary (or raw) data?"

Lines of text should be read from a serial terminal using canonical (aka cooked) mode. The OS will perform a lexical scan of the received data for your program, and delimit each line of text based on the EOL characters you specify.
The read() can then return a line assuming that blocking I/O is used, and the line of text is not longer than the buffer that is provided.

Binary data should be read from a serial terminal using noncanonical (aka raw) mode. The OS will ignore the values of the data, and (when blocking I/O is used) each read() will return an amount of data based on constraints of time and number of bytes.

See this answer for more details.

Note that the post your question refers to actually is about reading text, yet the OP is (mis)using non-canonical mode. If the OP had used the proper mode to match the input, then he might have never had a partial-read problem.



I am just curious that whether there is a better practise to avoid intensive system calls in this scenario?

Proper termios configuration is essential for efficient I/O with serial terminals.

Blocking I/O mode should be considered the preferred mode.

The OS can perform its scheduling for multitasking better when processes relinquish control more often.

The OS is more efficient in determining when data is available for return to a user.

Note also that the termios configuration is most effective when blocking mode is used, e.g. the VMIN and VTIME specifications in noncanonical mode.

For example using a select() or poll() and then a read() is one additional syscall more than when compared to just the (blocking) read(). And yet you can find many such code examples because there seems to be some popular misconception that the program can get the data faster from the "UART" that way.

But non-blocking and async modes are not necessarily faster (in a multitasking OS), and the read() merely fetches data from the termios buffer which is several layers removed from the actual hardware.

If your program uses non-blocking mode but does not perform useful work while waiting for data, and instead uses select() or poll() (or even worse calls sleep()), then your program is unnecessarily complex and ineffecient. See this answer.

A blocking-mode read() can do all that waiting for your program, make your program simpler and easier to write and maintain, and be more runtime efficient.

However for blocking non-canonical reads, you will have to accept some degree of inefficiency. The best you can do is trade-off latency versus the number of syscalls. One possible example is this answer which tries to fetch as much data per syscall, yet allow for an easy byte-by-byte lexical scan of the received binary data.


Note that a possible source of latency when reading a serial terminal is a poorly configured kernel, rather than the termios API and read() overhead.

For instance, setting the ASYNC_LOW_LATENCY flag via ioctl() (e.g. see High delay in RS232 communication on a PXA270) is one way to improve read() latency.

Non blocking read with fixed data in input

I want use serial port to communicate with another device txdev, the problem is that txdev is sending data asynchronously and i don't want the read function to block,

You seem to misunderstand how a Linux application program should read from and write to a serial port. All data is buffered between your program and the UART. Your program does not have to be always ready to read the data as it comes off the wire. The OS (specifically the UART device driver and the tty subsystem which includes line disciplines) does that, and buffers the data for your program.

RS-232 is by definition an asynchronous communication link. The characters/bytes can be transmitted at any time. Message packets will arrive at any point in time, so the application program will have to wait for the bytes that comprise a message packet. That's a case for using blocking reads.

The typical problem of receiving serial data is how to perform a lexical scan of received data to identify a complete message packet. Text messages (canonical mode) simply use ASCII line control characters to delimit messages/lines. Even packets of fixed length need validation (to ensure that the message actually starts on the correct byte). See this example of scanning fixed-length packets.

i think i have to use interrupt handler instead of a read do you agree ?

No, the UART device driver is already servicing its interrupts (or employing DMA) to capture every byte that is received off the serial link.

Since a sender can't get synchronized with the receiver is there an interrupt handler that stores the received data on specified buffer (i undrestand that this is the case for Canonical mode but not in Non canonical mode) or this is only done when read function is reached,

There is no need for the serial port on the receive end to "synchronize" with the sending serial port at the byte level. RS-232 is an asynchronous communication link: the sender can/will transmit a character/byte at any time, and the other end must be prepared to receive it (unless there is either hardware or software flowcontrol in place) at the device driver level (and not the application program).

The OS always buffers this received data. A read() syscall by an application program is merely a copy operation from the system buffer to the user buffer. This copy operation is for both canonical and non-canonical modes.

"Getting synchronized" is typically an issue to be resolved at the message or packet level by the protocol. Master-slave is a common configuration for defining a serial link protocol. The master side sends a query message to the slave side. The slave must always be prepared to receive a query. The slave side can respond with a transaction request or data capture or whatever, or a NAK message to indicate that it has nothing for the master. This request-response dialog is intended to control or pace the data flow (and processing load) between the two units.

Addendum

what i'am planing to do is actually a blocking read (VTIME =0 VMIN = DATA_LENGTH) in an infinite loop

Presumably you will always initiate your program before the sending device so that the first transmitted DATA_LENGTH will align with the first read by the program.

That will probably work most of the time in an ideal or monitored situation.

But for industrial 24/7 applications, the possibility of losing message frame alignment is real (e.g. sending device is restarted in middle of a transmission), and therefore a scheme that has no active means to verify and regain message frame alignment is inadequate.

IOW a raw read() of the serial port may not return an aligned message in your buffer (i.e. buf[0] may not contain the first byte of the message).

A much smaller VMIN with a nonzero VTIME is the typical termios configuration based on the premise that the serial link is idle (for a short interval) after each message.

But the code should be robust to handle any/all message fragments as they are read. So more than one read() may be needed to reconstruct the entire message.



Related Topics



Leave a reply



Submit