0% found this document useful (0 votes)
5 views30 pages

Effective Systems Programming in C

Uploaded by

Mouhamed Dieng
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views30 pages

Effective Systems Programming in C

Uploaded by

Mouhamed Dieng
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

CSE 410: Systems Programming

Effective Systems Programming

Ethan Blanton
Department of Computer Science and Engineering
University at Buffalo
Introduction System Variations Deep C Debugging Conclusions

Effective Systems Programming

Effective systems programming is about knowing your system.


We’ve spent a lot of time talking about POSIX.
POSIX covers a lot of system
Not all systems are POSIX, however1

Most systems will use many the same paradigms, if not details.
Take the time to learn the system.

1
sadly
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Corners of C

C has deep, dark corners that we have not explored.


Some of them are valuable, many of them are dangerous.
Many undefined and implementation-dependent behaviors are
in those corners.
And that’s not to even mention C11!

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Debugging
As you’ve seen this semester debugging systems is difficult.
We have looked at:
Compiler warnings/errors
Printing to stderr
gdb
valgrind
optionally compiling code

There are more tools available!


Learn them.
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Some Common System Variations


Word sizes
Signedness of char
Availability of floating point operations
Path separator for filenames
Availability of fork()
Unavailability of threads or processes
Availability of mmap()

There are many, but you’ll see these often, particularly on:
Embedded systems
Non-POSIX systems with a POSIX compatibility layer
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Word Sizes

If your code has to be portable, use sized integers everywhere!

int32_t
uintptr_t
ptrdiff_t

Even if it isn’t required for correctness, it communicates


intention.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Signedness of char

The signedness of char comes up surprisingly often.


It’s often related to casting int to char or vice-versa.
Examples:
Range checks for raw bytes
Loops with comparison to zero

To be safe, use int8_t or uint8_t for binary data.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Floating Point
All C compilers2 will compile floating point.
However:
It may not be IEEE 754
It may be software emulated

The former you might not notice (x86-64 isn’t!), the latter you will.
Most systems programs should avoid floating point entirely.
Use integer or fixed point math when possible.

2
Real C compilers that I’m aware of, that is
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Path Separators

All POSIX systems use forward slash as their path separator.


Some other systems use other characters.3
C doesn’t define this and doesn’t deal with it.
Portable programs have to do a lot of work for this.
Consider using a compatibility library like GLib if this is a
concern.

3
Inexplicably
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Fork, threads, and processes


Embedded systems, in particular, may not have fork().
Some non-POSIX systems do not have fork().
Many such systems:
Will have POSIX threads
May have posix_spawn()

If you don’t have any of these, C has a (painful) answer:


setjmp()
longjmp()

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Memory Mappings

Most non-POSIX systems will not have mmap().


This includes many embedded systems with a POSIX layer.
Sometimes this means there is no virtual memory.
Sometimes it means you need to use a different interface.
Shared memory may be available through some other interface.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Variadic Functions
We only briefly mentioned variadic functions.
This is a way to write a function with a variable number of
arguments.
The declaration syntax is simple:
void func ( type firstarg , ...) ;

Every variadic function must accept at least one named


argument.
There are restrictions on the type of the last named argument.
The number of arguments must be determined at run time.
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Variable Argument Lists


# include < stdarg .h >

typedef /* system - dependent */ va_list ;


void va_start ( va_list ap , parameter ) ;
type va_arg ( va_list ap , type ) ;
void va_end ( va_list ap ) ;

A function must:
Call va_start() first
Call va_arg() zero or more times
Call va_end() before returning

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Example Variadic Function

/* Print all string arguments to fp , end on NULL */


void example ( FILE * fp , ...) {
va_list ap ;
char * arg ;

va_start ( ap , fp ) ;
while (( arg = va_arg ( ap , char *) ) != NULL ) {
fprintf ( fp , " %s \ n" , arg ) ;
}
va_end ( ap ) ;
}

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Preprocessor Macro Arguments


Preprocessor macro arguments can be manipulated with #.

A single # turns an argument into a string:


# define logptr ( x ) printf ( " % s : % p \n " , #x , x )

int var ;
logptr (& var );
Output:

& var : 0 x7fff06ee7e6c

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Preprocessor Macro Arguments


Preprocessor macro arguments can be manipulated with #.
Two # concatenate C tokens:
# define printvar ( x ) printf ( " var % d: % d\ n " , x , var ## x )

int var1 = 42;


int var2 = 31337;
printvar (1) ;
printvar (2) ;
Output:

var1 : 42
var2 : 31337
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Variadic Macros
In addition to variadic functions, C99 has variadic macros.
They are dangerous but powerful.
Dangerous because they make code even harder to
understand than regular macros
Powerful because they enable calling variadic functions,
iterations on lists, etc.
# define DEBUG ( format , ...) \
fprintf ( stderr , " %s :% d " format , __FILE__ , \
__LINE__ , ## __VA_ARGS__ )

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Bit-field Integers
Adjacent integers in structs can be bit fields.
Bit fields have explicitly specified width in bits.
struct Bitfields {
unsigned int onenibble :4;
unsigned int byteandahalf :12;
};

This struct might be as small as two bytes.


The precise behavior and layout of bitfields is
implementation-defined.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Named and Ordered Initialization


This isn’t “deep C”, but it’s very useful for readability!
Arrays can used numbered initializers
Structures can use named initializers
/* All entries except 2 , 3 , and 7 are 0 */
int array [100] = { [2] = 3 , [3] = 5 , [7] = 13 };

/* Any fields except question and answer are 0 */


struct Something s = {
. question = " Life , the Universe , and Everything " ,
. answer = 42
}

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Goto
A controversial feature in any language, C has goto.
When used judiciously, it can be very powerful.
(Most kernels are full of gotos!)

Its syntax is simple:


i =10;
loop :
i - -;
if ( i > 0) goto loop ;

That’s a terrible goto, don’t do it.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Judicious Goto
Goto is often used for cleanup:

int fd = open ( " somefile " , O_RDONLY ) ;


char * buf = malloc ( BUFSIZE ) ;

if ( do_something ( fd , buf ) < 0) goto cleanup ;


if ( something_else ( fd , buf ) < 0) goto cleanup ;
/* ... */

cleanup :
close ( fd ) ;
free ( buf ) ;

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

System Logs
System logging functions can be valuable.
POSIX systems have syslog() for this purpose:
# include < syslog .h >

void openlog ( const char * ident , int option , int


facility ) ;
void syslog ( int priority , const char * format , ...) ;
void closelog ( void ) ;

This sends log messages to a logging daemon.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Generating Syslogs
For example, the following code:
openlog ( " example " , 0 , LOG_USER ) ;
syslog ( LOG_DEBUG , " the widget is frobnicated " ) ;
closelog () ;

This prints to /var/log/syslog:


Dec 4 21:13:28 westruun example : the widget is
frobnicated

You would normally openlog() once, then closlog() before


exiting.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Obvious Markers
It is common to use obvious values as markers.
These markers can be easily found by eye examining memory.
Examples:
0xfeedface
0xdeadbeef
0x01020304
0x00badbad
0xdeadc0de

In addition, 0xaaaaaaaa and 0x55555555 are alternating 1/0.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Forced Crashes

There are many ways to force a C program to dump core:


*NULL = 0;
abort();
Send SIGABRT to a process with kill
Press C-\ at the terminal

This can be handy when an error condition is rare.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Assertions
A particular form of forced crash is an assertion.
# include < assert .h >

void assert ( expression ) ;

If expression evaluates to false, the program crashes.


Use assertions to test preconditions and postconditions.
Don’t use assertions to check user input.
Turn off all but the most critical assertions unless debugging.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Use the Compiler

The compiler knows a lot about C.


Make it work for you, not against you:
Compile with -Wall -Werror (and maybe -Wextra)
Use structs and unions, not macros and pointer math
Use functions, not macros
Use enums, not #defines
Make typedefs
Silence warnings before digging too deep!

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

Your Editor
Find a good editor, and trust it.
If it thinks something is hinky, figure out why.
For example:
It wants to indent funny
It colors a variable name unexpectedly
It can’t find a completion

This may mean things like:
You’ve misplaced braces
You’re shadowing a system variable
etc.
© 2018 Ethan Blanton / CSE 410: Systems Programming
Introduction System Variations Deep C Debugging Conclusions

Congratualations

Congratulations, you’re systems programmers now.


I hope you’ve had a great semester.
Please fill out course evaluations.
See you Wednesday, December 12, at 08:00.

© 2018 Ethan Blanton / CSE 410: Systems Programming


Introduction System Variations Deep C Debugging Conclusions

License

Copyright 2018 Ethan Blanton, All Rights Reserved.


Reproduction of this material without written consent of the
author is prohibited.
To retrieve a copy of this material, or related materials, see
[Link]

© 2018 Ethan Blanton / CSE 410: Systems Programming

You might also like