Essential Embedded Concepts: Stack Watermark, Copy Techniques, Sync/Async, Memory Barriers
This article explains key embedded software concepts, including how stack watermark detection tracks maximum stack usage, the differences between shallow and deep copying of data structures, the distinction between synchronous and asynchronous processing, and the role of memory barriers in enforcing operation ordering on weakly consistent architectures.
This article compiles several important theoretical concepts in embedded software development.
What is Stack Watermark Detection?
Stack watermark detection is a dynamic technique for monitoring a task's stack usage. At task initialization, the entire stack space is filled with a known pattern such as 0xDEADBEEF. As the task runs, the stack pointer overwrites these preset values. By periodically checking the remaining untouched pattern locations, the historical maximum stack usage can be precisely calculated.
Compared with static code analysis, this method reflects the impact of dynamic behaviors such as interrupt nesting and recursive calls. Two practical considerations are:
The marker pattern must avoid colliding with legitimate data.
Detection should occur when the task is idle or being switched to avoid interfering with real‑time performance.
What are Shallow Copy and Deep Copy?
Shallow Copy copies only the values of the object's fields (i.e., the pointer values) without duplicating the data the pointers reference. After a shallow copy, the original and the copy share the same memory addresses for pointer members.
Deep Copy copies both the object's fields and recursively duplicates all data referenced by pointers, resulting in independent memory allocations for the copy.
Key differences are illustrated in the following diagram:
Shallow copy example:
// Direct pointer assignment (dangerous)
void shallow_copy(Student* dest, const Student* src) {
dest->id = src->id;
dest->name = src->name; // Shares the same memory address
}
Student s1;
s1.id = 100;
s1.name = malloc(6);
strcpy(s1.name, "Alice");
Student s2;
shallow_copy(&s2, &s1);
printf("Shallow copy result:
");
printf("s1.name=%p: %s
", s1.name, s1.name);
printf("s2.name=%p: %s
", s2.name, s2.name);
// Modifying s1 affects s2
strcpy(s1.name, "Bob");
printf("After modification:
");
printf("s1.name: %s
", s1.name);
printf("s2.name: %s
", s2.name);Deep copy example:
// Fully independent data copy
void deep_copy(Student* dest, const Student* src) {
dest->id = src->id;
dest->name = malloc(strlen(src->name) + 1); // New allocation
strcpy(dest->name, src->name); // Copy content
}
Student s3;
s3.id = 200;
s3.name = malloc(6);
strcpy(s3.name, "Carol");
Student s4;
deep_copy(&s4, &s3);
printf("Deep copy result:
");
printf("s3.name=%p: %s
", s3.name, s3.name);
printf("s4.name=%p: %s
", s4.name, s4.name);
// Modifying s3 does not affect s4
strcpy(s3.name, "Dave");
printf("After modification:
");
printf("s3.name: %s
", s3.name);
printf("s4.name: %s
", s4.name);
// Remember to free allocated memory
free(s1.name);
free(s3.name);
free(s4.name);What are Synchronous and Asynchronous Processing?
Synchronous Processing executes tasks in order; a subsequent task blocks until the current one finishes. Example: a blocking UART transmission.
void uart_send_sync(uint8_t *data, uint16_t len) {
for (int i = 0; i < len; i++) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // Wait for TX empty
USART_SendData(USART1, data[i]);
}
}Asynchronous Processing starts a task and returns immediately, with results delivered via interrupts, callbacks, or events, allowing other tasks to continue. Example: handling an ADC conversion in an interrupt handler.
void ADC_IRQHandler() {
if (ADC_GetITStatus(ADC1, ADC_IT_EOC)) {
uint16_t result = ADC_GetConversionValue(ADC1); // Read result
process_adc_result(result); // Callback processing
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
}
}Key differences are shown in the diagram below:
What is a Memory Barrier?
A memory barrier is a low‑level mechanism that enforces ordering of memory operations. On weakly consistent memory models, compilers or processors may reorder instructions; a memory barrier prevents such reordering, ensuring that operations occur in the intended sequence. This is crucial for multi‑core communication and peripheral register access.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
