Section 12.4. Writing Signal Handlers

   


12.4. Writing Signal Handlers

Although a signal handler looks like a normal C function, it is not called like one. Rather than being run as part of a program's normal call sequence, signal handlers are called by the kernel. The key difference between the two cases is that a signal handler can be called at almost any time, even in the middle of a single C statement! There are only a few restrictions on when the system will call a signal handler on which you can rely:

  1. The semantics of some signals restrict when they will be sent. SIGCHLD, for example, is not normally sent to a program that has no children.[17] Most signals are like SIGHUP, however, and are sent at unpredictable times.

    [17] Although users can send SIGCHLD to any processes they own, programs are not expected to respond reasonably to unexpected signals.

  2. If the process is in the middle of handling a particular signal, the signal handler is not reinvoked to handle the same signal unless the SA_NODEFER option was specified. The process can also block additional signals when a signal processor is running through the sa_mask member of struct sigaction.

  3. The process can block signals while running a part of code through use of sigprocmask(). Page 215 has an example of using this facility to allow atomic updates to data structures.

Because signal handlers can be run at almost any time, it is important to write them so that they do not make unwarranted assumptions about what the rest of the program is doing at the time and so that they do not rearrange things in a way that could confuse the rest of the program when it starts running again.

One of the most important things to watch is modifying global data. Unless this is done carefully, race conditions result. The easiest way to keep updates of global data safe is simply to avoid them. The next best method is blocking all signal handlers that modify a particular data structure whenever the rest of the code is modifying it, ensuring that only one code segment is manipulating the data at a time.

Although it is safe for the signal handler to read a data structure when it has interrupted another reader of that structure, all other combinations are unsafe. It is no more safe for the signal handler to modify a data structure that the rest of the program is reading than it is for the signal handler to read a data structure the rest of the program is writing. Some specialized data structures have been designed to allow concurrent access, but those data structures are well beyond the scope of this book.

If you must access global data from a signal handler (which most signal handlers end up doing), keep the data structure simple. Although it is pretty easy to safely modify a single data element, such as an int, more complicated structures usually require blocking signals. Any global variables that a signal handler may modify should be declared with the volatile keyword. This tells the compiler that the variable may be changed outside the normal flow of the program and it should not try to optimize accesses to the variable.

The other thing to be careful of in signal handlers is calling other functions, as they may modify global data as well! The C stdio library tends to do this quite a bit and should never be used from a signal handler. Table 12.2 lists functions that are guaranteed to be safe to call from a signal handler;[18] all other system functions should be avoided.

[18] The table lists functions that may not be present on some, or even any, Linux systems. We have included all of the functions that POSIX specifies as safe to call from signal handlers for completeness.

Table 12.2. Reentrant Functions

abort()

accept()

access()

aio_error()

aio_return()

aio_suspend()

alarm()

bind()

cfgetispeed()

cfgetospeed()

cfsetispeed()

cfsetospeed()

chdir()

chmod()

chown()

close()

connect()

creat()

dup()

dup2()

execle()

execve()

_exit()

fchmod()

fchown()

fcntl()

fdatasync()

fork()

fpathconf()

fstat()

fsync()

getegid()

geteuid()

getgid()

getgroups()

getpeername()

getpgrp()

getpid()

getppid()

getuid()

kill()

link()

listen()

lseek()

lstat()

mkdir()

mkfifo()

open()

pathconf()

pause()

pipe()

poll()

posix_trace_event()

pselect()

raise()

read()

readlink()

recv()

recvfrom()

recvmsg()

rename()

rmdir()

select()

sem_post()

send()

sendmsg()

sendto()

setgid()

setpgid()

setsid()

setsockopt()

setuid()

shutdown()

sigaction()

sigaddset()

sigdelset()

sigemptyset()

sigfillset()

sigismember()

signal()

sigpause()

sigpending()

sigprocmask()

sigqueue()

sigset()

sigsuspend()

sleep()

socket()

socketpair()

stat()

symlink()

sysconf()

tcdrain()

tcflow()

tcflush()

tcgetattr()

tcgetpgrp()

tcsendbreak()

tcsetattr()

tcsetpgrp()

time()

timer_getoverrun()

timer_gettime()

timer_settime()

times()

umask()

uname()

unlink()

utime()

wait()

wait3()

wait4()

waitpid()

write()



       
    top
     


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net