Linux logo       


Penguin Programming - Info


    

Linux/UNIX Programming Tips & Tricks

To auto-destroy zombies:

The kernel will automatically destroy zombies when a process ignores SIGCHLD.
And a peculiar side-effect is that wait() will wait for ALL children to exit.

To ensure temporary files are deleted in case of a crash:

A UNIX programming trick is to open and then immediately unlink a temp file.
Because the file is still referenced by the open file descriptor,
the program can write to the file even though it's unlinked.

To force printf() to immediately display a string:

Use fprintf():
fprintf( stdout, ... ); fflush( stdout );

To measure the time consumed by a program:

Run your program using "time": time myprogram
Or, programmatically, call times() which returns tms.tms_utime.
tms_utime is the amount of ticks the process consumed.

To print an urgent message even if stdout and stderr are redirected:

open() and write() to /dev/tty.

To speed compiling:

make will run jobs concurrently when -j n is passed (n=4 is adequate).
The -j flag can be put in your environment: export MAKEFLAGS=-j4
But some makefiles will fail if made concurrently (eg "make install" for Linux kernel).
distcc to distribute execution of gcc across a network.

To build smaller executables using GNU gcc/ld:

Pass -s to strip symbol information (and for gcc -Os to reduce code size):
gcc -s -Os
ld -s

Linux/UNIX Programming Pitfalls

A child should exit by calling _exit() not exit().

In a program that intends to start another program concurrently,
if a child calls exec*() but exec*() fails, and the child incorrectly calls exit(),
then the parent process will be killed along with the exiting child.

read()/write() might do less than what was requested.

Usually (but not necessarily), read()/write() will fully satisfy
a large request (>BUFSIZ) to a regular file on the local filesystem.
But, read()/write() have a tendency to read/write less than what was requested
when applied to transient objects such as a socket or a pipe.

C Programming Pitfalls

sizeof() means how many bytes (not elements) and includes trailing null byte of a string.

char is signed, and will be promoted to a signed integer (not unsigned).

Bit-field structs aren't portable.

A number beginning with 0 (zero) is interpreted as octal, not decimal.

int  limit = 0100;    /* oops, limit is assigned 64 (decimal) */

Literal strings might be compiled into a read-only segment.

One case where this pitfall manifests is by passing a literal string
as a parameter to a function that tries to modify the string.

Equality operators have precedence over bitwise operators.

if ( x & MASK == VALUE )    /* WRONG: equivalent to (x & (MASK==VALUE)) */
if ( (x & MASK) == VALUE )  /* right */

Well-known pitfalls:

Dangling pointer:
char* foo( void )
{
    char str[100];
    [..]
    return str;    /* WRONG, points within temporary stack */
}
The body of a macro and every parameter should be parenthesized to prevent surreptitious precedence bugs:
#define MAX(a,b) a > b ? a : b /* WRONG */
/* WRONG: result=1 (0xffff vs. y because '>' has more precedence than '&') */
x = 1; y = 2; result = MAX(x & 0xffff, y);
TRUE is #defined as 1 but TRUE is logically any non-zero value:
if ( result == TRUE )    /* WRONG */
if ( result )            /* correct, if result != 0 */

goto pitfall:

An example of a bug caused by goto. When "match" is false,
the intent is to break the inner loop, and continue the outer loop.
for (i=0; i<MAX; ++i)
{
no_match:

   for (j=0; j<MAX; ++j )
   {
      if ( !match )
         goto no_match;
   }
}
The above code is incorrect. goto skips the incrementing of i
and causes the outer loop to become stuck!
A correction is to jump to the bottom of the outer loop:
for (i=0; i<MAX; ++i)
{
   for (j=0; j<MAX; ++j )
   {
      if ( !match )
         goto no_match;
   }

no_match:
}

C++ Programming Pitfalls

RTTI typeid.name() won't return what you would expect

This line ought to print "int", but g++ 2.9 and 3.2 will defy reason by printing "i" (the letter i).
Class names will be returned mangled (eg "P4MyClass" for a MyClass object).
Unfortunately, what typeid().name() returns was left implementation-specific,
and IMO its use should be restricted to printing debug messages.
This is one of the few cases where GNU gcc/g++ doesn't do The Right Thing (tm).
cout << typeid(int).name() << "\n";

The corollary is that to identify a type, instead use the equality operators
that typeid overloads to compare two type_info objects.
// WRONG:
if ( strcmp( typeid(pObject).name(), "MyClass" ) == 0 ) ...

// Correct:
if ( typeid(pObject) == typeid(MyClass) ) ...

C++ Programming Tricks

const arrays as class members

Like it or not, the C++ language syntax doesn't support them.
The compiler requires const members to be initialized.
But the initializer list syntax won't take an array.
ARM pg 290: "There is, however, no member initialization syntax for non-static const arrays.". An alternative is to declare the array as a private non-const member.
Then access it thru a reference member that references it as a const array.
The compiler will still catch the mistake of trying to write to the array
via the const reference (which is the purpose of having a const array).
class MyClass
{
public:
                MyClass( void ) : arr(arr_) { };
    const int   (&arr)[100]; // declare a reference to a const array
private:
    int         arr_[100];
};

Type signatures

Type signatures can automatically catch memory corruption problems.
Otherwise, such problems are among the most difficult to track down using a debugger.
A type signature is an additional member in a data struct that
holds a unique integer which identifies the type.
Functions that are passed a pointer to a data struct can assert on its type signature
to ensure the data struct is of the expected type and its memory wasn't corrupted.
Type signatures can be implemented as C macros which only expand in debug builds
(and thus won't incur any overhead in release builds).

Embedded Programming

To build smaller executables using GNU gcc/ld:

Pass -s to strip symbol information (and for gcc -Os to reduce code size):
gcc -s -Os
ld -s

Uploading a program to a target board using GNU gdb:

In order to do this, the firmware on the target board must include GDB's "remote protocol" stub.
Typically, a null modem (cross-over) cable should connect the serial ports of the host and target.
Start gdb on the host:
gdb -nw
Boot or reset the target board, then give these commands
to upload the program from the host to the target:
(gdb) set remotebaud 38400
(gdb) target remote /dev/ttyS0
(gdb) load myprogram

Motorola S-Records:

The S-Record format, often required to upload code to Motorola 68K/PPC target boards,
is analogous to ELF and Intel HEX.
It is supported by the GNU toolchain.
To link a file in S-Record format, pass "-oformat srec" to the linker ld.

Makefiles

Autogenerating dependencies in a GNU makefile

A powerful ability of GNU make that assists in automatically generating dependencies is that the same target can specified multiple times.

The first target tells make how to compile the .o file.
The second target tells make the dependencies of the .o file.

main.o:
    gcc -c <so forth>

main.o: main.h common.h

The second target is an autogenerated depedency. In practice, it will be written to an autogenerated file which will be included by the makefile, eg:

main.o:

-include deps   # "deps" is a file autogenerated by gcc -MM or makedepend

How is "deps" autogenerated? What will GNU make do if it doesn't exist?

Another ability of GNU make is that "-include <file>" implies dependency. For "-include deps", if "deps" doesn't exist, GNU make will execute its rule. Additionally:

main.o:

deps:
    <gcc -M or makedepend>

-include deps

gcc -M or makedepend

Either gcc or makedepend can autogenerate dependencies. Either can be passed a list of source files (to generate dependencies all-at-once).

Below are two examples. The first was written first and is based on GNU make documentation. The second is derived from Palomino and it uses separate source and object directories. gcc or makedepend could be substituted in either example.

Using gcc -M to autogenerate dependencies

1. Make object file depend on a "dependency file", conventionally suffixed as ".d".
   Ie: *.o --> *.d -->  *.c

2. Create suffix rules similar to:

   %.d: %.c
       $(CC) -MM $< > $@

   .c.o:
       $(CC) -c -o $@ $<

3. Add this line to a makefile to include all of the dependency files:

   include ${C_SRCS:.c=.d}

................................................................................
# Skeletal GNUmakefile that autogenerates dependencies using gcc.
# This is simple but the disadvantage is that is doesn't use
# separate source and object directories.

CC = g++

CC_SRCS = main.cc

all: main.o

clean:
    rm -f *.o *.d

%.d: %.cc
    $(CC) -MM $< > $@

.cc.o:
    $(CC) -c -Wall -o $@ $<

include ${CC_SRCS:.cc=.d}
................................................................................

Using makedepend to autogenerate dependencies

................................................................................
# Skeletal GNUmakefile (derived from Palomino) that autogenerates dependencies
# using makedepend and uses separate source and object directories.

CC_SRCS     = main.cc
HH_SRCS     = header.hh
OUT_DIR     = out
SRC_DIR     = src
SRC_EXT     = .cc
INC_FLAGS   = -I$(SRC_DIR)
CC          = g++
CC_FLAGS    = -Wall

# Order dependent.
CC_OBJS         := $(addprefix $(OUT_DIR)/,$(CC_SRCS:$(SRC_EXT)=.o))
CC_SRCS         := $(addprefix $(SRC_DIR)/,$(CC_SRCS))
HH_SRCS         := $(addprefix $(SRC_DIR)/,$(HH_SRCS))

all:  $(OUT_DIR)  $(CC_OBJS)

$(OUT_DIR):
    mkdir -p out

clean:
    rm -f $(OUT_DIR)/*.o $(OUT_DIR)/deps

# Build an object file from every source file.
$(OUT_DIR)/%.o: $(SRC_DIR)/%$(SRC_EXT)
    $(CC)  -c -o  $@  $(CC_FLAGS)  $(subst $(OUT_DIR)/,$(SRC_DIR)/,$<)

# Skip depend rule if clean (-include would otherwise make it).
# Pass -I flags (INC_FLAGS) to makedepend or it will omit those directories.
# -include depends on $(OUT_DIR)/deps which make will make.
ifeq (,$(findstring clean,$(MAKECMDGOALS)))
depend $(OUT_DIR)/deps:  $(CC_SRCS)  $(HH_SRCS)
    @mkdir -p $(OUT_DIR)
    makedepend $(INC_FLAGS) -Y -f- $(CC_SRCS) 2>/dev/null | sed 's/$(SRC_DIR)\//$(OUT_DIR)\//' > $(OUT_DIR)/deps
-include $(OUT_DIR)/deps
endif
................................................................................


index   home