tag 标签: construct

相关博文
  • 热度 11
    2014-2-28 19:36
    1221 次阅读|
    0 个评论
    As I have said before, everyone knows about C's assert macro. But I have found that few embedded people make much use of it. Everyone also knows about snprintf(), yet it, too, is somewhat elusive. Do you use sprintf() or snprintf()? Most of the embedded code I read makes liberal use of sprintf(), yet it can be a dangerous construct. It's all too easy to overrun the string being written. The snprintf() function makes the developer specify the max length of the string. Yet in casual surveys over the last year, nearly none of the embedded folk I asked knew of the existence of snprintf(). The C11 standard is 700 mind-numbing pages long. All of the many C books I have are replete with examples and advice, and none approach the standard's length. Clearly they cherry pick the best parts of the language. None covers snprintf(), though I do believe this function was added in C99 and some of those books, like the second edition of KR, predate that version of the language. The snprintf() function will discard all characters longer than the specified length, so is an especially attractive option when working with variably-sized elements. It can eliminate big classes of buffer overrun bugs. The function improves maintainability. While the original developer may know with great certainty that sprintf() cannot overrun the string, later changes by others may result in the buffer's length being shortened or the output being increased in size. Due to NDAs (non-disclosure agreements) I can't say much about most of the code I read. Let's look at some publicly available software. Freescale's MQX RTOS, which is sprinkled with more than a little coding horror, does make use of snprintf(), though it is a function that is provided by MQX. sprintf() is much more common, though, and a lot of those functions are called with the % format specifier, which might be trouble (though no doubt the designers were scrupulous in their analysis). What about Linux? The 3.12.6 release uses snprintf() about 6000 times, compared to 8500 for sprintf(). A superficial reading of some of this shows that in many cases it's obvious the sprintf() is perfectly safe; that's much less apparent for others. No doubt great care was taken to ensure buffer overruns are impossible in every circumstance. I hope. Though it might be tempting to use snprintf() to append text to a string, a statement of the form: snprintf(s, "stuff to append", s); ... will produce an undefined result. Don't do it. I often put a wrapper around snprintf() to check if the string was truncated to fit in the buffer; if it was, an error is invoked (that could be an assertion or other run-time exception handler). This is an example of proactive debugging: writing code that catches bugs before a symptom appears. Great composers expose themselves to lots of great music. Great writers read a lot of great books. And great programmers should read a lot of code to learn about the language, ways to structure the software, and the like. When you come across a construct that might be unfamiliar, like snprintf(), dig deeper.  
  • 热度 9
    2011-6-9 17:57
    1435 次阅读|
    0 个评论
    I wish we lived in an atomic world. No, I am not yearning for an Iranian bomb. Rather, I am referring to the fact that unavoidable non-atomic accesses to shared resources causes much grief and poor code. I read a lot of code, and find much that handles non-atomic accesses in this manner: long global_var; void do_something(void) { // Handle a non-atomic access to "global_var" #pragma disable interrupts somehow // Do something non-atomic to global_var #pragma enable interrupts somehow } This construct suffers from a number of problems, not the least of which is that it's not generally reuseable. If the function is called from some place with interrupts off, it returns with them on, disrupting the system's context. Usual solutions involve saving and restoring the interrupt state. But that, too, is fraught with peril. Optimizers are aggressive today, and in some cases can reorder statement execution to the point where interrupts aren't disabled at the right point. The result: all that atomic-work may not work. I've asked several compiler vendors for their take, since they understand the optimizations the compilers do better than anyone. The most interesting and complete response came from Greg Davis of Green Hills Software, and he has graciously allowed me to reprint it here: "What we recommend for Green Hills customers is to use intrinsic functions for disabling and restoring interrupts. What this looks like is: #include int global_var; void foo(void) { // Disable interrupts and return "key" // that expresses current interrupt state unsigned int key = __DIR(); // Code that handles global_var in a non-atomic way // Restore interrupts to state expressed by "key" __RIR(key); }" These Green Hills intrinsics for __DIR() and __RIR() generate different assembly code depending on the architecture and CPU that you are compiling for, but their interface is the same. The compiler considers the system-instructions that these intrinsics generate to be non-swappable, so the code that manipulates global_var will not be swapped across them. With GNU, people tend to prefer to use inline assembly. These assembly statements are typically embedded in inline functions or macros with GNU statement expressions. An implementation of something like this on an ARM7TDMI might look like: static inline unsigned int disable_interrupts_reentrant(void) { unsigned int ret; asm volatile( "mrs %0,cpsr\n" "orr r1,%0,192\n" "msr cpsr_cxsf,r1\n" : /* output */ "=r" (ret) : /* input */ : /* clobbers */ "r1", "memory" ); return ret; } static inline void restore_interrupts(unsigned int state) { asm volatile( "and r1,%0,192\n" "mrs r0,cpsr\n" "bic r0,r0,192\n" "orr r0,r0,r1\n" "msr cpsr_cxsf,r0\n" : /* output */ : /* input */ "r" (state) : /* clobbers */ "r0", "r1", "memory" ); } int global_var; void foo(void) { unsigned int key = disable_interrupts_reentrant(); // Code that handles global_var in a non-atomic way restore_interrupts(key); } At least to my understanding, the combination of the declaring the assembly to be volatile and putting the "memory" in the clobbers list should ensure that memory accesses in the critical section stay in the critical section. Both of the above approaches involve compiler-specific extensions. The best approach I'm aware of that isn't compiler specific is to move the code into another file so it just looks like a function call to the compiler: extern unsigned int disable_interrupts_reentrant(void); extern void restore_interrupts(unsigned int state); int global_var; void foo(void) { unsigned int key = disable_interrupts_reentrant(); // Code that handles global_var in a non-atomic way restore_interrupts(key); } and then to define the functions in a separate assembly file. The exact assembly syntax may vary between implementations, but it may look something like this on a traditional UNIX-style assembler. .text .globl disable_interrupts_reentrant disable_interrupts_reentrant: ; Inputs: none ; Outputs: r0 (return register) contains a key for the ; current interrupt state mrs r0, cpsr orr r1, r0, 192 msr cpsr_cxsf, r1 bx lr .type disable_interrupts_reentrant,@function .size disable_interrupts_reentrant, .- disable_interrupts_reentrant .globl restore_interrupts restore_interrupts: ; Inputs: r0: prior interrupt state ; Outputs: None and r1, r0, 192 mrs r0, cpsr bic r0, r0, 192 orr r0, r0, r1 msr cpsr_cxsf,r0 bx lr .type restore_interrupts,@function .size restore_interrupts,.-restore_interrupts Since compilers need to assume that external functions read and write all global variables, there's no chance for the code that handles global_var to fall outside of the critical section." Thanks, Greg, for the insight. I hope this information is useful to folks.
  • 热度 9
    2011-4-22 11:50
    1479 次阅读|
    0 个评论
    I wish we lived in an atomic world.   No, I am not yearning for an Iranian bomb. Rather, I am referring to the fact that unavoidable non-atomic accesses to shared resources causes much grief and poor code.   I read a lot of code, and find much that handles non-atomic accesses in this manner:   long global_var; void do_something(void) {   // Handle a non-atomic access to "global_var" #pragma disable interrupts somehow // Do something non-atomic to global_var #pragma enable interrupts somehow }   This construct suffers from a number of problems, not the least of which is that it's not generally reuseable. If the function is called from some place with interrupts off, it returns with them on, disrupting the system's context.   Usual solutions involve saving and restoring the interrupt state. But that, too, is fraught with peril. Optimizers are aggressive today, and in some cases can reorder statement execution to the point where interrupts aren't disabled at the right point. The result: all that atomic-work may not work.   I've asked several compiler vendors for their take, since they understand the optimizations the compilers do better than anyone. The most interesting and complete response came from Greg Davis of Green Hills Software, and he has graciously allowed me to reprint it here:   "What we recommend for Green Hills customers is to use intrinsic functions for disabling and restoring interrupts. What this looks like is:   #include int global_var; void foo(void) {   // Disable interrupts and return "key"   // that expresses current interrupt state unsigned int key = __DIR(); // Code that handles global_var in a non-atomic way // Restore interrupts to state expressed by "key"   __RIR(key); }" These Green Hills intrinsics for __DIR() and __RIR() generate different assembly code depending on the architecture and CPU that you are compiling for, but their interface is the same. The compiler considers the system-instructions that these intrinsics generate to be non-swappable, so the code that manipulates global_var will not be swapped across them. With GNU, people tend to prefer to use inline assembly. These assembly statements are typically embedded in inline functions or macros with GNU statement expressions. An implementation of something like this on an ARM7TDMI might look like: static inline unsigned int disable_interrupts_reentrant(void) {   unsigned int ret;   asm volatile(   "mrs %0,cpsr\n"   "orr r1,%0,192\n"   "msr cpsr_cxsf,r1\n"   : /* output */ "=r" (ret)   : /* input */   : /* clobbers */ "r1", "memory"   );   return ret; } static inline void restore_interrupts(unsigned int state) {   asm volatile(   "and r1,%0,192\n"   "mrs r0,cpsr\n"   "bic r0,r0,192\n"   "orr r0,r0,r1\n"   "msr cpsr_cxsf,r0\n"   : /* output */   : /* input */ "r" (state)   : /* clobbers */ "r0", "r1", "memory"   ); } int global_var; void foo(void) {   unsigned int key = disable_interrupts_reentrant(); // Code that handles global_var in a non-atomic way restore_interrupts(key); } At least to my understanding, the combination of the declaring the assembly to be volatile and putting the "memory" in the clobbers list should ensure that memory accesses in the critical section stay in the critical section. Both of the above approaches involve compiler-specific extensions. The best approach I'm aware of that isn't compiler specific is to move the code into another file so it just looks like a function call to the compiler: extern unsigned int disable_interrupts_reentrant(void); extern void restore_interrupts(unsigned int state); int global_var; void foo(void) {   unsigned int key = disable_interrupts_reentrant(); // Code that handles global_var in a non-atomic way restore_interrupts(key); } and then to define the functions in a separate assembly file. The exact assembly syntax may vary between implementations, but it may look something like this on a traditional UNIX-style assembler. .text   .globl disable_interrupts_reentrant disable_interrupts_reentrant:   ; Inputs: none   ; Outputs: r0 (return register) contains a key for the   ; current interrupt state   mrs r0, cpsr   orr r1, r0, 192   msr cpsr_cxsf, r1   bx lr   .type disable_interrupts_reentrant,@function   .size disable_interrupts_reentrant, .-   disable_interrupts_reentrant .globl restore_interrupts restore_interrupts:   ; Inputs: r0: prior interrupt state ;   Outputs: None   and r1, r0, 192   mrs r0, cpsr   bic r0, r0, 192   orr r0, r0, r1   msr cpsr_cxsf,r0   bx lr   .type restore_interrupts,@function   .size restore_interrupts,.-restore_interrupts Since compilers need to assume that external functions read and write all global variables, there's no chance for the code that handles global_var to fall outside of the critical section."   Thanks, Greg, for the insight. I hope this information is useful to folks.