Ну вот есть у тебя абстракция write, пользуй ее и не ломай голову
как она внутри устроена. Конкретная реализация write будет зависеть
от OS, а конкретная реализация драйвера UART - от конкретного
железа. А если не UART, а Ethernet - там будут другие реализации и
другие драйвера. Будет ли там условная переменная, два семафора или
отдельный сопроцессор с блекджеком и программистками - это вопрос
конкретной реализации, а не фундаментальных принципов. Про запрет прерываний - именно так и сделана критическая секция в FreeRTOS и прочих всяких TNeo.
/* Critical section handling. */
#define portENABLE_INTERRUPTS() __asm( "cli" )
#define portDISABLE_INTERRUPTS() __asm( "sei" )
/*
* Disable interrupts before incrementing the count of critical section nesting.
* The nesting count is maintained so we know when interrupts should be
* re-enabled. Once interrupts are disabled the nesting count can be accessed
* directly. Each task maintains its own nesting count.
*/
#define portENTER_CRITICAL() \
{ \
extern volatile UBaseType_t uxCriticalNesting; \
\
portDISABLE_INTERRUPTS(); \
uxCriticalNesting++; \
}
/*
* Interrupts are disabled so we can access the nesting count directly. If the
* nesting is found to be 0 (no nesting) then we are leaving the critical
* section and interrupts can be re-enabled.
*/
#define portEXIT_CRITICAL() \
{ \
extern volatile UBaseType_t uxCriticalNesting; \
\
uxCriticalNesting--; \
if( uxCriticalNesting == 0 ) \
{ \
portENABLE_INTERRUPTS(); \
} \
}
Про реализацию атомиков в libgcc - вопрос не ко мне, я без понятия как и чего там.
Понял что тебе не нравится, я этот момент не описал, считая самоочевидным. Любой поток, запихав свои данные в буфер проверяет, сколько там осталось места. Если осталось приличное количество - постит семафор. Описанная тобой беда-беда возникает ровно в одном случае: в буфере есть чуть-чуть свободного места, куда в принципе могли бы влезть какие-то из отправляемых сообщений. Если такая ситуация случается часто - нужно просто увеличить буфер. Если редко - да и пес с ним.