How to protect a global variable shared by isr and a regular function?

Let's say I have function 1 and isr routine , and they share and update the same flag without any lock in between. single-threaded system.

while will be a 3-hand assembly instruction, which means that this is not an atomic operation, is it normal to use a global variable between non isr and isr functions without any blocking or protection?

function 1:

 while (flag == false); flag = false; 

isr:

 do something flag=true 

I don’t remember that there is a Linux kernel mechanism for blocking between a sleeping and troubled context, for example. irq and kernel thread .


Thanks to @artless for his answer, here are some issues I'm not sure about:

  • Is there a way that I will not skip interruption at all?

  • How do memory barriers solve the problem, does it have an effect when the code runs on a single processor?

  • What is the expected behavior when using barriers between different contexts?

  • Can sleep while loop can solve synchronization problems?

+5
source share
4 answers

Using volatile often cited as a solution, but this is not entirely true. It often disguises the problem, since volatile will always make the code slower. If your only use is shown like this, then volatile will probably work.

It is probably best to use memory barriers with a single reader and a single record. Then it will be your code,

Highway:

 volatile int *p = &flag; while (*p == false); /* You must use volatile if you poll */ flag = false; asm volatile ("" : : : "memory"); /* gcc barrier */ 

ISR:

 /* do something */ flag=true asm volatile ("" : : : "memory"); /* gcc barrier */ 

Here, the barrier simply forces the compiler to execute the ARM str command at that point. The optimizer will not move the code before or after. You can also use swp or ldrex and strex depending on your ARM CPU. In addition, ring buffers are often used with ISRs and main lines because they do not need any special CPU support; only a compiler memory barrier.

See lock-free and, in particular, the search for lock-free and arm .

Edit: For additions,

Is there a way that I will not skip interruption at all?

It depends on the source of the interrupt. If this is a timer, and you know that the timer source can never be faster than XX , and no other interrupts are active in the system, then your current code will work. However, if the interruption comes from an external source, such as an Ethernet controller, a non-debuted keyboard, etc. Multiple interruptions are possible. Several times new interrupts occur even during the interrupt handler. There are different solutions depending on the source of the ISR. A ring buffer is typically used to queue work items from the ISR for the main row. For UART, a ring may contain actual character data. This could be a list of pointers, etc. It is difficult to synchronize the ISR with the main line when the connection becomes more complex; Therefore, I believe that the answer depends on the source of the interrupt. This is why every OS has so many primitives and infrastructure for this problem.

How do memory barriers solve the problem, does it have an effect when the code runs on a single processor?

Memory items do not completely eliminate the problem of missing interrupts; just like volatile not. They just make the window a lot smaller. They force the compiler to schedule loading or to store earlier. For example, the main loop of the line,

  1: ldr r0, [r1] cmp r0, #0 ; xxx bne 1b ; xxx mov r0,#1 ; xxx str r0, [r1] 

If a second interrupt occurs during xxx lines, your flag must be set twice, and you missed one interrupt. The barriers just make sure that the compiler puts ldr and str close to each other.

What is the expected behavior when using barriers between different contexts?

I see how the compiler locks memory, and the compiler does it before. This does not affect the context. There are various barriers; but they are mainly for multiprocessor projects.

Can sleeping in a while loop solve synchronization problems?

Not really, it's just more efficient use. The ARM WFI team can temporarily stop the CPU, and this will save energy. Usually this sleep () does on ARM. I think you need to change the relationship between ISR and mainline if this is a problem. It depends on the source of the ISR .

+9
source

It is better if you can declare a flag as: volatile int flag; or volatile bool flag;

0
source

This should help prevent a missed interrupt. It is based on surprisingly detailed answers from @artless_noise

Here the ISR publishes a semaphore (non-blocking call). Adding a barrier to ensure that recording is complete. The thread will be launched as many times as the semaphore will be published.

 sem = sem_open(argv[optind], flags, perms, 0); // Initialising semaphore to 0 function 1: while(sem_getvalue(sem) > 0) { flag = false; //Avoiding the barrier if this value isnt needed by ISR asm volatile ("" : : : "memory"); /* gcc barrier */ //perform action } isr routine: do something flag=true; asm volatile ("" : : : "memory"); /* gcc barrier */ sem_post(sem); 
0
source

Yes. If this is a single-threaded model, there is no need to block.

-one
source

Source: https://habr.com/ru/post/1495603/


All Articles