2013-08-21 53 views
1

问题定义:多线程嵌入式linux应用程序状态机设计

我们正在为运行Linux的工业嵌入式系统设计一个应用程序。

该系统由外部事件驱动。到系统的输入可以是任何以下的:

  1. 很少输入到系统中的数字IO线的形式(连接 像急停处理器的的GPIO)。
  2. 该系统运行一个网络服务器,允许通过网络浏览器控制系统为 。
  3. 系统运行TCP服务器。任何PC或HMI设备都可以通过TCP/IP发送命令。

系统需要使用Modbus通过UART驱动或控制RS485从站设备。该系统还需要控制少数IO线路,如冷却器开/关等。我们认为,状态机对于定义此应用程序至关重要。核心应用程序应是一个多线程应用程序,它应具备以下线程...

  1. 主线程
  2. 线程控制RS485奴隶。
  3. 线程处理来自Web界面的事件。
  4. 处理数字I/O事件的线程。
  5. 线程来处理的,我们使用的Pthread条件信号&等待通过TCP/IP(套接字)

对于线程间通信的命令。按照我们的初始设计方法(主线程中的一个状态机),任何到系统的输入事件(web或tcp/ip或数字I/O)都应该传递给主线程,并且应该与相应的线程进行通信该事件是注定的。典型的情况是通过Web界面获取RS485从站的状态。在这种情况下,Web界面线程应该将事件中继给主线程,主线程将改变状态,然后将事件传送到控制RS485从站回应的线程&。主线程应将响应发送回Web界面线程。

问题:

  1. 如果每个线程都有自己的状态机,从而减少主线程的 复杂性?在这种情况下,我们是否仍然需要 在主线程中拥有状态机?
  2. 任何线程处理输入事件可以直接与 线程通信,该线程处理绕过主线程的事件?例如 网络接口线程可以直接与线程 控制RS485从站通信?
  3. 使用pthread状态信号是否正常&等待线程间 通信还是有更好的方法吗?
  4. 如何让一个线程等待来自外部的事件&响应 来自其他线程?对于例如Web界面线程通常会等待 处理POSIX消息队列上的事件,以便进行来自Web服务器CGI bin的进程间通信 。 CGI bin通过这个消息队列将接口线程发送到web 。处理此事件时,Web界面线程将等待来自其他线程的响应。在这种情况下,它无法处理来自网络界面的任何新事件,直到它完成处理之前的 事件并返回到POSIX消息队列的等待状态。

抱歉太大的解释......我希望我已经以最好的方式提出了我的解释,让别人理解和帮助我。

如果需要,我可以提供更多输入。

+1

似乎原始问题中描述的方法重新发明活动对象(actor)设计模式。我建议看一下开源QP主动对象框架(http://www.state-machine.com/qp)。在http://www.state-machine.com/linux上有一个POS机的端口,带有p-threads。 –

+0

当然,Mr.Miro先生,我会考虑这个选项......感谢一大堆 – hprasath

回答

3

我总是试图处理这样的需求是使用一个状态机,由一个'SM'线程运行,这可能是主线程。这个线程在一个'EventQueue'输入生产者 - 消费者队列中等待超时。该超时用于运行内部增量队列,在需要时可以向状态机提供超时事件。

所有其他线程通过将消息推送到EventQueue来将其事件传递给状态引擎,SM线程以串行方式处理它们。

如果SM中的一个动作例程决定它必须做某事,它不能同步等待任何事情,因此它必须通过将请求消息推送到任何线程/系统可执行的输入队列来请求动作。我的消息类(OK,* C中的struct)通常包含一个'command'枚举,'result'枚举,一个数据缓冲区指针(在需要传输批量数据的情况下),一个错误 - 消息指针,(如果没有错误,则返回null),以及允许任何类型的请求异步排队并返回完整结果(无论成功还是失败)所需的其他状态。

这个消息传递,一个SM设计是我发现的唯一一个能够以灵活,可扩展的方式完成这些任务,而不会陷入恶梦世界的死锁,不受控制的通信以及不可重复的不可逾越的交互。

关于任何设计应该问的第一个问题是'好的,如果有一些奇怪的问题,系统如何调试?'。在我上面的设计中,我可以直接回答:'我们记录了在SM线程中出队的所有事件 - 它们都是连续出现的,所以我们始终确切地知道基于它们采取了哪些操作。如果提出任何其他设计,请询问上述问题,如果没有立即提供好的答案,它将永远无法工作。

所以:

  1. 如果一个线程,或螺纹子系统,可以使用单独的状态机来完成其自身的内部功能,OK,很好。这些SM应该从系统的其他部分看不见。

  2. 不!

  3. 使用pthread条件信号&等待实现生产者 - 消费者阻塞队列。

  4. 每个线程/子系统一个输入队列。所有输入都以消息的形式进入该队列。每条消息中的命令/状态标识消息以及应该如何处理。

BTW,我会100%做到这一点在C++中,除非猎枪在头:)

+0

+1的可调试性。有见识的分解。 – andy256

+0

马丁詹姆斯先生,我会考虑你提到的所有问题。我将不得不做一些工作,然后用新设计回复你。谢谢.. – hprasath

+0

@Martin James,我在你的设计方法中有一些澄清。如果主状态机或主线程将异步地向子系统线程分派输入事件,主线程如何将结果作为主线程将忙于等待输入事件。我也不明白你提到的内部增量队列吗?你能详细说明一下吗? – hprasath

0

我已经实现了最初为克隆西门子ES122C终端(EC115EC270)写了一个传统的嵌入式库控制器。这个库和操作系统或多或少包含你描述的内容。原来的硬件是基于80186 CPU。西门子的操作系统,RMOS和我们的FXMOS(不要谷歌它从未发布过)拥有基本控制器工作所需的所有东西。它具有先占式多任务处理,任务到任务通信,信号量,定时器和I/O事件,但没有内存保护。 我把这些东西移植到RaspberryPi(即Linux)上。

我使用pthreads来模拟我们的遗留“任务”,因为我们没有内存保护,所以线程在语义上最接近。 其余的实现绕过epoll API。这意味着所有产生事件的东西。事件发生时,计时器到期,另一个线程发送数据,连接TCP套接字,更改IO引脚状态等......这要求所有事件源都在文件描述符中进行转换。 Linux提供了几个系统调用,它们的确如此: 用于任务任务通信我使用的是经典的Unix管道。 用于定时器事件我使用了timerfd API。用于TCP通信的 我使用普通套接字。 用于串口I/O的简单打开,正确的设备/dev/???工程。 信号对我来说不是必需的,但Linux在必要时提供'signalfd'。我有epoll_wait缠绕来模拟原来的语义。

我的工作就像一个魅力。

TL; DR

走在epoll的API一个深沉的样子它做什么,你可能需要。

编辑:是的,马丁詹姆斯的建议是非常好的,特别是4.每个线程应该只有在通过epoll_wait等待一个事件的循环。