Architectural proposals in a Linux application

I've done quite a bit of programming on Windows, but now I need to write my first Linux application.

I need to talk to a hardware device using UDP. I have to send 60 packets per second of 40 bytes. If I send less than 60 packets in 1 second, bad things happen. Packet data may take some time. But if the data is not ready to send over the wire, you can send the same data that was last sent. The computer is a command line only and will only run this program.

I don't know much about Linux, so I was hoping to get a general idea of ​​how to configure an application to meet these requirements. I was hoping for an answer like:

Create 2 threads, one for sending packets and one for computation.

But I'm not sure if it's easy (maybe it is). Maybe it would be more reliable to do some kind of daemon that just send packets from shared memory or something and then another application will do the computation? If this is some kind of multi-process solution, what communication mechanism would you recommend? Is there a way that I can give my application more priority than usual or something similar?

PS: The more bulletproof the better!

+1


source to share


10 replies


I posted this answer to illustrate a completely different approach to the "obvious", in the hopes that someone will find that this is exactly what they want. I didn't expect it to be chosen as the best answer! Handle this solution with care because there are potential dangers and concurrency issues ...

You can use the setitimer () system call to send a SIGALRM (alarm) to your program after a certain number of milliseconds.Signals are asynchronous events (bit like messages) that interrupt the executable program to run the signal handler.

A set of software handlers is installed by default by the OS when your program starts, but you can install a custom signal handler using sigaction () .



So, you only need one thread; use global variables so the signal handler can get the information it needs and send a new packet or repeat the last packet as needed.

Here's an example for your benefit:

#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


source


I did a similar project: simple software on an embedded Linux machine sending CAN messages at normal speed.

I would go for two streams. Give the sending thread a slightly higher priority and force it to send the same block of data again if another thread is slow in computing those blocks.



60 UDP packets per second is pretty relaxed on most systems (including built-in ones), so I won't spend a lot of time optimizing the communication between streams and sending packets.

In fact, I would say: keep it simple ! I am really the only application on the system and you have reasonable control over this system, you have nothing to gain from the complex IPC scheme and other tricks. Ease of use helps you improve your code with fewer defects and in less time, which actually means more time to test.

+3


source


The two streams you suggested will work. If you have pipe () in between, then your computational stream can provide packets as they are generated, while the comms stream uses select () to see if there is any new data. If not, then it just sends the last one from the cache.

I may have simplified the problem a bit ...

+1


source


The suggestion to use a couple of threads sounds like it would do the trick as long as the burden of doing the computation isn't too great.

Instead of using pipe()

as suggested by Cogsy, I would tend to use a mutex to lock the chunk of memory that you use to hold the output of your computation thread - using it as a cross-threading scope.

When your computation thread is ready to exit to the buffer, it will grab the mutex, write to the transfer buffer, and release the mutex.

When your mediation thread was ready to send a packet, it "tried" to lock the mutex. If it receives a lock, take a copy of the transmit buffer and send it. If he doesn't get the lock, please send the latest copy.

You can control the priority of your process by using "nice" and specifying a negative number for the setting to give it a higher priority. Note that you will need to do this as superuser (either root or using "sudo") to specify negative values.


edit: Forgot to add - this one is a good tutorial on pthreads on linux. The use of mutexes is also described.

+1


source


I didn’t quite understand how difficult it is to meet your 60 packets / sec needs. Does a packet require 60 packets per second? Or is there a sharp interval of 1 / 60th of a second between each requested packet?

This may be a little off topic, but another big issue is how to set up a Linux box. I would use the realtime Linux kernel myself and disable all unnecessary services. Another reasonable risk is the real risk that your application will miss a packet at some time, no matter which architecture you choose.

Either way, the two streams should work well.

+1


source


Two threads will work, you will need to make sure you lock the shared data structure so that the submit thread doesn't see it halfway through the update.

60 per second doesn't seem too hard.

If you are really concerned about scheduling, set the thread dispatch scheduling policy to SCHED_FIFO and mlockall () to your memory. This way, nothing will be able to stop it from sending the packet (they can still come out late, although when sending other things on the wire at the same time)

There should be some deviation from the device - 60 pps is good, but what is device portability? 20 per second? If the device fails, if it doesn't receive it, I would ship them three times faster than it requires.

0


source


I would stay away from threads and use processes and (possibly) signals and files. Since you say that "bad things" can happen if you don't post, you need to avoid blocking and race conditions. And this is easier to do with separate processes and data stored in files.

Something along the line of one process saves the data to a file and then renames it and starts it over. And another process collects the current file and sends its contents once a second.

Unlike Windows, you can copy (move) around a file while it is open.

0


source


Keep up with the latest Unix practices: keep it simple and modular, undo, and let the OS do as much of the work for you as possible.

Many of the answers here are on the right track, but I think they could be even simpler:

  • Use two separate processes, one to create data and write it to stdout, and another to read data from stdin and send it. Let the underlying I / O libraries handle buffering the data stream between processes and let the OS handle the flow control.

  • First, create a basic sender using a timer loop and a fake data buffer and send it to the device at the correct frequency.

  • Then make the sender read data from stdin - you can redirect data from a file, e.g. "sender <textdata"

  • Create a data producer further and output its output to the sender, for example. "manufacturer | sender".

You now have the ability to create new producers as needed without going into the sender's side. This answer assumes a one-way communication.

By keeping the answer as simple as possible, you will get more success, especially if you are not very fluent in Linux / Unix-based systems yet. This is a great opportunity to learn a new system, but don't overdo it. It's easy to jump to complex answers when tools are available, but why use a bulldozer when a simple spatula is enough. Mutex, semaphores, shared memory, etc. are useful and accessible, but add complexity that you might not need.

0


source


I agree with this two-threaded approach. I would also have two static buffers and a generic enum. The sending stream must have this logic.

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

      

Another thread will have this logic:

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

      

This way, the sender always has a valid buffer for the entire time it sends data. Only the generator thread can change the buffer pointer, and it can only do so if the send fails. In addition, renaming a flip should never take so many cycles to delay a higher priority sender stream for very long.

0


source


Thanks everyone, I will use every advice. I would like to pick more answers than 1!

For the curious. I have no source for the device, its a patented locking system. I haven't done enough testing to see how legible 60 pps is. What all of their limited docs say is "60 packets per second". However, due to the nature of the device, packet bursts will be bad. I think I can get away with sending over 60 seconds to make up for the occasional dropped packets.

0


source







All Articles