2008-12-23 53 views
1

我在Windows上完成了相当多的编程,但现在我必须编写第一个Linux应用程序。在Linux应用程序中的建筑建议

我需要使用UDP与硬件设备对话。我必须每秒发送60个数据包,大小为40个字节。如果我在1秒内发送少于60个数据包,就会发生不好的事情。 数据包的数据可能需要一段时间才能生成。但是如果数据没有准备好发送出去,可以发送上次发送的相同数据。 计算机是一个命令行的唯一设置,只会运行此程序。

我对Linux不太了解,所以我希望能够得到一个大概的想法,你如何设置一个应用程序来满足这些要求。 我希望得到如下答案:

制作2个线程,一个用于发送数据包,另一个用于计算。

但我不确定这是如此简单(也许是)。也许用一些从共享内存发出数据包的守护进程更可靠,然后让另一个应用程序进行计算?如果它是一个多进程解决方案,你会推荐什么通信机制? 有什么方法可以让我的应用比普通或类似的应用更具优先级?

PS:越防弹越好!

+0

既然你不知道UDP数据包是否到达(这是一种不可靠的协议),怎么办你知道你发送的60 +/-数据包到达了吗?如果一秒钟发送61个数据包会发生什么? 59怎么样?其他号码? – 2008-12-28 02:47:12

回答

0

我发布了这个答案来说明一个完全不同的“明显”方法,希望有人发现它正是他们所需要的。我没想到它被选为最佳答案!请谨慎对待此解决方案,因为存在潜在的危险和并发问题...

可以使用setitimer()系统调用在指定的毫秒数后向您的程序发送SIGALRM(报警信号)。信号是异步事件(有点像消息),它会中断正在执行的程序,让信号处理程序运行。

当您的程序开始时,OS安装了一组默认的信号处理程序,但您可以使用sigaction()安装自定义信号处理程序。

所以你只需要一个线程;使用全局变量,以便信号处理程序可以访问必要的信息并发送新的数据包或根据需要重复上一个数据包。

这里是为了你的利益的例子:

#include <stdio.h> 
#include <signal.h> 
#include <sys/time.h> 
int ticker = 0; 

void timerTick(int dummy) 
{ 
    printf("The value of ticker is: %d\n", ticker); 
} 

int main() 
{ 
    int i; 

    struct sigaction action; 
    struct itimerval time; 

    //Here is where we specify the SIGALRM handler 
    action.sa_handler = &timerTick; 
    sigemptyset(&action.sa_mask); 
    action.sa_flags = 0; 

    //Register the handler for SIGALRM 
    sigaction(SIGALRM, &action, NULL); 

    time.it_interval.tv_sec = 1;  //Timing interval in seconds 
    time.it_interval.tv_usec = 000000; //and microseconds 
    time.it_value.tv_sec = 0; //Initial timer value in seconds 
    time.it_value.tv_usec = 1; //and microseconds 

    //Set off the timer 
    setitimer(ITIMER_REAL, &time, NULL); 

    //Be busy 
    while(1) 
     for(ticker = 0; ticker < 1000; ticker++) 
      for(i = 0; i < 60000000; i++) 
       ; 
} 
+0

我认为一般的建议是保持信号处理程序非常简单? I/O(printf和发送UDP数据包)当然应该避免? – 2008-12-23 07:44:53

1

正如你所建议的两个线程将工作。如果你在它们之间有一个pipe(),那么你的计算线程可以在它们生成时提供数据包,而你的通信线程使用select()来查看是否有新的数据。如果不是,那么它只是从缓存中发送最后一个。

我可能比简单的问题有点......

3

我做了一个类似的项目:一个嵌入式Linux计算机上的一个简单的软件,以常规速度发出CAN消息。

我会去两个线程的方法。如果另一个线程在计算这些块时速度较慢,请给发送线程稍高的优先级,并再次发送相同的数据块。在大多数系统(包括嵌入式系统)上,每秒UDP数据包相当宽松,所以我不会在优化线程和发送数据包之间的数据共享上花费太多的汗水。

其实我会说:保持简单!我真的是系统中唯一的应用程序,并且您对该系统有合理的控制权,从复杂的IPC方案和其他技巧中无法获得。保持简单将帮助您以更少的缺陷和更短的时间生成更好的代码,这实际上意味着更多的测试时间。

1

只要执行计算的负担不是太大,使用一对线程的建议听起来好像会起作用。

而不是像Cogsy所建议的那样使用pipe(),我倾向于使用一个互斥来锁定你用来包含计算线程输出的一块内存 - 将它用作线程之间的传输区域。

当您的计算线程准备好输出到缓冲区时,它将获取互斥锁,写入传输缓冲区并释放互斥锁。

当您的传输线程准备好发送数据包时,它会“尝试”锁定互斥锁。 如果它获得锁定,请复制传输缓冲区并发送它。 如果它没有获得锁定,请发送最后一个副本。

您可以通过使用“nice”并指定一个负调整数字来给予它更高的优先级来控制进程的优先级。请注意,您需要以超级用户身份执行此操作(以root身份或使用'sudo')才能指定负值。


编辑:忘了补充 - this是一个关于linux上的pthreads的好教程。还介绍了互斥体的使用。

1

我不太明白你的60包/秒的要求有多难。每秒60个数据包的突发是否满足要求?或者每个数据包之间需要一个1/60秒的时间间隔?

这可能会出现一些问题,但另一个重要问题是如何配置Linux机器。我自己会使用实时 Linux内核并禁用所有不需要的服务。除此之外,无论您选择何种架构,您的应用程序在某个时候都会错过一个数据包,这确实存在风险。

任何方式,两个线程应该工作。

0

两个线程会的工作,你需要确保你通过更新通过这样的发送线程没有看到它一半锁定您的共享数据结构。

每秒60听起来不太复杂。

如果您真的关心调度,请将发送线程的调度策略设置为SCHED_FIFO和mlockall()其内存。这样,没有什么能够阻止它发送一个数据包(虽然如果其他东西同时在线上发送,他们仍然可能会晚出)

设备必须有一定的容差 - 60每秒数据包没有问题,但设备的容差是多少?每秒20?如果设备如果没有收到设备将会失败,我会按照它要求的速率三次发送它们。

0

我会远离线程并使用进程和(可能)信号和文件。既然你说“不好的事情”可能发生,如果你不发送,你需要避免锁定和竞争条件。这对于单独的进程和保存到文件的数据更容易。

沿着一个进程将数据保存到一个文件,然后重命名并重新开始。另一个过程是每秒拾取一次当前文件并发送其内容。

与Windows不同,您可以可以在打开文件时复制(移动)文件。

0

遵循长期的Unix最佳实践:保持简单和模块化,分离操作,让操作系统为您尽可能多地完成工作。

这里的许多答案都在正确的轨道上,但我认为他们可以更简单:

  • 使用两个独立的过程,一个创建数据并将其写入标准输出,和一个从stdin读取数据并发送。让基本I/O库处理进程之间的数据流缓冲,并让OS处理线程管理。

  • 首先使用定时器循环和伪造数据缓冲区构建基本发送方,然后以正确的频率发送给设备。

  • 接下来使发件人从标准输入读取数据 - 您可以重定向文件中的数据,例如, “sender < textdata”

  • 构建下一个数据生产者并将其输出管送至发件人,例如, “制作人|发件人”。

现在,您可以根据需要创建新的生产者,而不会干扰发件人一方。这个答案假定单向沟通。

保持答案尽可能简单会让你获得更多的成功,特别是如果你还不是很流利的基于Linux/Unix的系统。这是一个学习新系统的好机会,但不要过度使用它。当工具可用时,很容易跳到复杂的答案,但为什么使用推土机,当一个简单的t刀是充足的。互斥量,信号量,共享内存等都是有用和可用的,但会增加您可能并不需要的复杂性。

0

我同意这两种线程方法。我也有两个静态缓冲区和共享枚举。发送线程应该有这个逻辑。

loop 
    wait for timer 
    grab mutex 
    check enum {0, 1} 
    send buffer 0 or 1 based on enum 
    release mutex 
end loop 

另一个线程会有这样的逻辑:

loop 
    check enum 
    choose buffer 1 or 0 based on enum (opposite of other thread) 
    generate data 
    grab mutex 
    flip enum 
    release mutex 
end loop 

这样发送者总是有它发送数据的整个时间的有效缓冲。只有生成器线程可以更改缓冲区指针,并且只能在发送未进行时执行。此外,枚举翻转永远不会花费太多的周期来延迟较长的优先级较高的发送者线程。

0

谢谢大家,我会用大家的建议。我希望我能选择比1更多的答案!

对于那些好奇。我没有设备的来源,它是一个专有锁定系统。我还没有做足够的测试来看看每秒60个数据包有多挑剔。那就是他们所有有限的文档都说“每秒60包”。由于设备的性质,数据包的爆发将是一件坏事。我想我可以通过发送超过60秒的时间来弥补偶尔错过的数据包。