简体   繁体   English

Linux串行通信与中断

[英]Linux serial communication with interrupts

I have two devices connected to each other via serial port. 我有两个通过串口相互连接的设备。 One operates as a master and the other as a slave. 一个作为主机,另一个作为从机。

The master device is an ARM based kit that is running linux. 主设备是运行Linux的基于ARM的套件。

The current situation is master sends command then poll the com port until the slave replies. 当前情况是主机发送命令,然后轮询com端口,直到从机答复为止。

Now I don't wan't to use polling. 现在,我不想使用轮询。 I need the processor to do other tasks until slave replies. 我需要处理器执行其他任务,直到从属答复为止。

I know that the solution is to use interrupt but can't find more details. 我知道解决方案是使用中断,但找不到更多详细信息。 I found some solution that uses signals. 我找到了一些使用信号的解决方案。 It reads the ttys in nonblocking mode then send io signal when data is ready. 它以非阻塞模式读取ttys,然后在数据就绪时发送io信号。

So, what is the difference between interrupts and signals in serial communication? 那么,串行通信中的中断和信号之间有什么区别? Is that right when using interrupts I should write a device driver or kernel module or so? 使用中断时应该写设备驱动程序或内核模块吗? Is there any other efficient solution in order not to use polling? 为了不使用轮询,还有其他有效的解决方案吗?

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 . 不久前,我完成了相同的任务,而解决该问题的最佳方法是使用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() 一旦有新数据可用-将其写入循环缓冲区产生数据)并通过pthread_cond_signal()唤醒使用者线程
  • consumer thread : wait for producer thread to wake me (using pthread_cond_wait() function); 使用者线程 :等待生产者线程唤醒我(使用pthread_cond_wait()函数); 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. 在我的情况下,串行端口在非阻塞模式下处于打开状态,因为我想等待新数据超时poll()poll()函数中提供poll() 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) 如果您不需要它-您可以以阻止模式打开串行端口,而只需使用阻止read()调用(由@sawdust在评论中建议)

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). 这样,您就不会浪费CPU时间来等待新数据到达,并且同时您也无需弄乱信号(使用信号会带来很多麻烦 ,因此我决定避免使用它)。

AIO API AIO API

The only other API to do asynchronous read is aio_read() from POSIX AIO interface. 进行异步读取的唯一其他API是来自POSIX AIO接口的aio_read() But as I understand, aio_read() based on signals. 但据我了解, aio_read()基于信号。 The only difference is: 唯一的区别是:

  • SIGIO signal tells you that new data is available for reading, and you need to read it manually SIGIO信号告知您有新数据可读取,您需要手动读取
  • aio_read() gives you read data (as read() does, only asynchronously). aio_read()为您提供读取数据(与read()一样,仅异步)。

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. 我不建议您使用它,因为该API似乎并不广泛,并且与信号驱动方法相比没有任何好处。

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 Signal是操作系统的概念,用于内核在发生某些事件时通知您的用户空间应用程序

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 (eg new data is available for reading). 换句话说,当您的串行端口硬件产生中断时,它会在内核中处理,内核会产生SIGIO信号,从而将新事件通知您的用户空间应用程序(例如,可以读取新数据)。

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() 驱动程序代码流: 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() 行学科代码流: n_tty_receive_buf2() -> n_tty_receive_buf_common() -> __receive_buf()
  • __receive_buf() does: __receive_buf()可以:

     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() 它发送SIGIO信号,如下所示: 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 [1] 串行编程方法:异步输入示例

[2] The TTY demystified [2] TTY神秘化

[3] LDD3: Asynchronous Notification [3] LDD3:异步通知

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM