【问题标题】:Linux serial communication with interrupts带有中断的 Linux 串行通信
【发布时间】:2016-09-02 10:02:04
【问题描述】:

我有两个设备通过串口相互连接。 一个作为主机运行,另一个作为从机运行。

主设备是一个基于 ARM 的套件,运行 linux。

目前的情况是master发送命令然后轮询com端口直到slave回复。

现在我不想使用轮询。我需要处理器执行其他任务,直到从机回复。

我知道解决方案是使用中断,但找不到更多详细信息。 我找到了一些使用信号的解决方案。它以非阻塞模式读取 tty,然后在数据准备好时发送 io 信号。

那么,串行通信中的中断和信号有什么区别呢? 使用中断时我应该编写设备驱动程序或内核模块吗? 为了不使用轮询,还有其他有效的解决方案吗?

【问题讨论】:

  • “我需要处理器来完成其他任务......”——这是任何高效程序的给定条件,也是嵌入式系统所必需的。 “还有其他有效的解决方案吗...” -- 你从使用 blocking read() 开始,可能是在非规范模式下.让操作系统为您完成调度工作,而不必为异步 I/O 和信号而烦恼。如果您的程序需要,请使用线程。见stackoverflow.com/questions/25996171/…

标签: linux linux-kernel serial-port signals interrupt-handling


【解决方案1】:

为了不使用轮询,还有其他有效的解决方案吗?

多线程方法

前段时间我有同样的任务,我发现解决它的最好方法是使用pthreads。基本上,我做了下一步:

  • 生产者线程(主线程):等待串口有新数据;一旦有新数据可用——将其写入circular bufferproduce数据)并通过pthread_cond_signal()唤醒消费者线程
  • 消费者线程:等待生产者线程唤醒我(使用pthread_cond_wait()函数);一旦醒来——从循环缓冲区中读取新数据(消费数据)并正确处理它
  • 在我的例子中,串口是在non-blocking mode中打开的,因为我想在poll()函数中提供超时等待新数据。如果您不需要这个 - 您可以在 阻塞模式 下打开串行端口,然后使用阻塞 read() 调用(由 @sawdust 在评论中提出)

这样你就不会浪费 CPU 时间等待新数据的到来,同时你也不需要弄乱信号(使用信号有很多complications,所以我决定避免它)。

AIO API

唯一的其他执行异步读取的 API 是来自POSIX AIO 接口的aio_read()。但据我了解,aio_read() 基于信号。唯一的区别是:

  • SIGIO信号告诉你有新数据可以读取,需要你手动读取
  • aio_read() 为您提供读取数据(就像 read() 所做的那样,只是异步的)。

我不建议您使用它,因为此 API 似乎并不普及,并且与信号驱动方法相比没有任何好处。

那么,串行通信中的中断和信号有什么区别呢?

  • 中断是低级概念,所以只在内核中处理
  • 信号是操作系统的概念,当某些事件发生时,内核用于通知您的用户空间应用程序

换句话说,当你的串口硬件产生中断时,它在内核中处理,内核产生SIGIO信号,通知你的用户空间应用程序有新事件(例如有新数据可供读取)。

在使用中断时我应该编写设备驱动程序或内核模块吗?

通常是的,您只能在内核中处理中断。但在这种情况下,你不需要。它已经在内核中为您完成,更具体地说,在line discipline 代码中。它为您生成信号(发生中断时),您可以在用户空间应用程序中使用该信号。

如果您需要一些详细信息:

  • 驱动代码流程:tty_flip_buffer_push() -> tty_schedule_flip() -> queue_work(...) -> flush_to_ldisc() -> receive_buf() -> tty_ldisc_receive_buf() -> .receive_buf2()
  • 行规代码流:n_tty_receive_buf2() -> n_tty_receive_buf_common() -> __receive_buf()
  • __receive_buf() 会:

    if (read_cnt(ldata)) {
        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
        wake_up_interruptible_poll(&tty->read_wait, POLLIN);
    }
    
  • 发送SIGIO信号,像这样:kill_fasync() -> kill_fasync_rcu() -> send_sigio() -> send_sigio_to_task() -> do_send_sig_info() -> send_signal()

    李>

因此,当串口驱动发生相应的中断时,您可以依靠内核向您传递消息(通过信号)。

另见

[1]Serial Programming HOWTO: Asynchronous Input Example

[2]The TTY demystified

[3]LDD3: Asynchronous Notification

【讨论】:

  • 有没有关于如何使用中断的编程示例?
  • 嗯,内核代码中充满了它们。看看here(请参阅参考部分)。这些都是内核运行tty_flip_buffer_push() 的地方。例如,基于 OMAP 的平台的中断由 here 处理。但坦率地说,我不明白你想在内核端实现什么?所有需要在内核中完成的东西都已经实现了,所以你可以在用户空间依赖它,对吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 1970-01-01
  • 2012-11-22
  • 2017-08-13
  • 2012-10-19
  • 2014-03-23
相关资源
最近更新 更多