Is there any other efficient solution in order not to use polling?
Multi-threading approach
Some time ago I had the same task, and the best way I found to solve it is to use pthreads. Basically, I did next:
- producer thread (main thread): wait for new data in serial port; once new data is available -- write it into circular buffer (produce data) and wake up the consumer thread via
pthread_cond_signal()
- consumer thread: wait for producer thread to wake me (using
pthread_cond_wait()
function); once woke up -- read new data from circular buffer (consume data) and process it properly
- in my case, serial port was open in non-blocking mode, because I wanted to wait for new data with timeout, which provided in
poll()
function. If you don't need this -- you can open serial port in blocking mode and just use blocking read()
call (as proposed by @sawdust in comment)
This way you don't waste CPU time waiting for new data to arrive, and in the same time you don't need to mess with signals (using signals has a lot of complications, so I decided to avoid it).
AIO API
The only other API to do asynchronous read is aio_read() from POSIX AIO interface. But as I understand, aio_read()
based on signals. The only difference is:
SIGIO
signal tells you that new data is available for reading, and you need to read it manually
aio_read()
gives you read data (as read()
does, only asynchronously).
I would not recommend you using it, as this API doesn't seem to be widespread and doesn't have any benefits over signal-driven approach.
So, what is the difference between interrupts and signals in serial communication?
- Interrupt is low-level concept, so it's being dealt with only in kernel
- Signal is OS concept, which is used for kernel to notify your user-space application when some event is occurred
In other words, when your serial port hardware generates interrupt, it's handled in kernel, and kernel generates SIGIO
signal, notifying your user-space application of new event (e.g. new data is available for reading).
Is that right when using interrupts I should write a device driver or kernel module or so?
Generally yes, you can handle interrupts only in kernel. But in this case you don't need to. It was already done for you in kernel, more specifically, in line discipline code. It generates signal for you (when interrupt occurred), which you can use in your user-space application.
In case you want some details:
- driver code flow: tty_flip_buffer_push() ->
tty_schedule_flip()
-> queue_work(...)
-> flush_to_ldisc()
-> receive_buf()
-> tty_ldisc_receive_buf()
-> .receive_buf2()
- line discipline code flow:
n_tty_receive_buf2()
-> n_tty_receive_buf_common()
-> __receive_buf()
__receive_buf()
does:
if (read_cnt(ldata)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
}
which sends SIGIO
signal, like this: kill_fasync()
-> kill_fasync_rcu()
-> send_sigio()
-> send_sigio_to_task()
-> do_send_sig_info()
-> send_signal()
So you can rely on kernel to deliver you a message (via signal) when corresponding interrupt occurred in serial port driver.
See also
[1] Serial Programming HOWTO: Asynchronous Input Example
[2] The TTY demystified
[3] LDD3: Asynchronous Notification