data synchronization without using semapore in C

2019-02-20 21:57发布

问题:

I need to have data synchronization in my code. Currently I am accessing a global value inside interrupt and also in a local function which may corrupt the data if interrupt call is frequent. I need to avoid this scenario. I am not using operating system in my code, so I cannot use a semaphore. Using a similar locking method as semaphore might solve my problem.

Any help would be appreciated

回答1:

Interrupts work differently than threads or processes - if a thread waits for a semaphore, it is simply not scheduled until either the semaphore gets available or, if given, the wait timeout elapses. In the mean while, other threads can be scheduled, one of those potentially giving back the semaphore.

This is not the case with interrupt service routines - these won't be interrupted by any thread scheduling (if at all, then only by other interrupts), but are executed until they return. So if a ISR gets to wait for a semaphore (or a similar mechanism, as you asked for), we are in a deadlock as the thread holding it can't be scheduled any more either to give the semaphore back...

So you need a totally different mechanism!

The usual way to do this is to disable the interrupt as long as your function needs to access the common data, and re-enable it afterwards (and you potentially need to do this within the ISR itself, too).

How? Well, OS/hardware specific - as long as you do not provide further details, I'm out here...

Just some hints yet: Keep the period of disabled interrupts as short as possible and make sure that the commonly accessed data is declared volatile!



回答2:

It's probably as simple as this in your main code:

disable_interrupts();
value += 1;
enable_interrupts();

So you make sure the interrupt cannot fire while you're using the value in the main code.



回答3:

What you need is atomic access to the data. If it is a single variable and you can guarantee that access is atomic, then that is enough. However, this involves disassembling the C code and see what you ended up with. And even if the machine code ended up as atomic (single instruction), it won't be portable.

If you have a modern compiler with C11 support, you can declare the shared variable as _Atomic and that will solve the issue.

Another alternative is to simply shut off the particular interrupt during variable access in the caller. This will however disrupt real-time performance and you might miss out interrupts.

The universally "best" solution might be to invent a semaphore by yourself. Example:

// volatile to prevent dangerous compiler optimizations; does not solve re-entrancy
volatile uint32_t data;
volatile bool guard;

void ISR (void)
{
  if(!guard)
  {
    data = SOME_REGISTER;
  }
}

void main (void)
{
  ...
  guard = true;
  uint32_t local = data;
  guard = false;
}

In the above example no atomic access is guaranteed at all, not even to the guard variable. However, it is no longer necessary, because at the point where main() is about to read the data, the guard is guaranteed to be set. If the interrupt would kick in during the read, it wouldn't corrupt the data.

The only downside of this solution is that you will be missing out updating data when the guard is set. If this is an issue, you will have to implement some manner of temporary storage.

(Note that this code does not result in "memory barriers", so on complex multi-core processors, this method might not work and volatile will not necessarily result in a memory barrier. On ordinary microcontrollers it will work just fine though.)