The UNIX operating system supports a wide range of signals. UNIX signals are software interrupts that catch or indicate different types of events. Windows , on the other hand, supports only a small set of signals that is restricted to exception events only. Consequently, converting UNIX code to Win32 requires the use of new techniques replacing the use of some UNIX signals.
The Windows signal implementation is limited to the following signals (Table 9.3).
Signal | Meaning |
---|---|
SIGABRT | Abnormal termination |
SIGFPE | Floating-point error |
SIGILL | Illegal instruction |
SIGINT | CTRL+C signal |
SIGSEGV | Illegal storage access |
SIGTERM | Termination request |
Note | When a CTRL+C interrupt occurs, Win32 operating systems generate a new thread to handle the interrupt. This can cause a single-thread application, such as one ported from UNIX, to become multithreaded, which may in unexpected behavior. |
When an application uses other signals not supported in Windows, you have two choices:
Use additional libraries that provide required signals, such as those providedby Microsoft Services for UNIX at: http://www.microsoft.com/Windows2000/sfu .
Use a comparable Windows mechanism, such as Windows Messages.
This section focuses on the Windows mechanisms that you can use to replace theuse of some UNIX signals. Table 9.4 shows the recommended mechanisms that you can use to replace common UNIX signals. There are three main mechanisms:
Native signals
Event objects
Messages
Signal Name | Description | Link to reference material |
---|---|---|
SIGABRT | Abnormal termination | SIGABRT |
SIGALRM | Time-out alarm | SetTimer “ WM_TIMER - CreateWaitableTimer |
SIGCHLD | Change in status of child | WaitForSingleObject |
SIGCONT | Continue stopped process | WaitForSingleObject |
SIGFPE | Floating point exception | SIGFPE |
SIGHUP | Hangup | NA |
SIGILL | Illegal hardware instruction | SIGILL |
SIGINT | Terminal interrupt character | WM_CHAR |
SIGKILL | Termination | WM_QUIT |
SIGPIPE | Write to pipe with no readers | WaitForSingleObject |
SIGQUIT | Terminal Quit character | WM_CHAR |
SIGSEGV | Invalid memory reference | SIGSEGV |
SIGSTOP | Stop process | WaitForSingleObject |
SIGTERM | Termination | SIGTERM |
SIGTSTP | Terminal Stop character | WM_CHAR |
SIGTTIN | Background read from control tty | NA |
SIGTTOU | Background write to control tty | NA |
SIGUSR1 | User defined signal | SendMessage “ WM_APP |
SIGUSR2 | User defined signal | SendMessage “ WM_APP |
Note | Only POSIX signals are considered in this table (that is, Seventh Edition, System V,and BSD signals are not). |
This section discusses how you can use the three mechanisms listed in Table 9.4to convert the parts of your code that use signals into the Windows environment.
Another mechanism that can be useful when converting some UNIX uses of signals to Windows is event kernel objects. For more information on these objects, see the CreateEvent example in Logging System Messages later in this chapter.
In the following example, the simple case of catching SIGINT to detect CTRL-C is demonstrated. As you can see from the two source listings, support for handling native signals in UNIX and Win32 is very similar.
#include <unistd.h> #include <stdio.h> #include <signal.h> /* The intrpt function reacts to the signal passed in the parameter signum. This function is called when a signal occurs. A message is output, then the signal handling for SIGINT is reset (by default generated by pressing CTRL-C) back to the default behavior. */ void intrpt(int signum) { printf("I got signal %d\n", signum); (void) signal(SIGINT, SIG_DFL); } /* main intercepts the SIGINT signal generated when Ctrl-C is input. Otherwise, sits in an infinite loop, printing a message once a second. */ int main() { (void) signal(SIGINT, intrpt); while(1) { printf("Hello World!\n"); sleep(1); } }
#include <windows.h> #include <signal.h> #include <stdio.h> void intrpt(int signum) { printf("I got signal %d\n", signum); (void) signal(SIGINT, SIG_DFL); } /* main intercepts the SIGINT signal generated when Ctrl-C is input. Otherwise, sits in an infinite loop, printing a message once a second. */ void main() { (void) signal(SIGINT, intrpt); while(1) { printf("Hello World!\n"); Sleep(1000); } }
Note | By default, signal terminates the calling program with exit code 3, regardless of the value of sig. For more information, see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_signal.asp . |
With the exception of requiring an additional header file, and the different signature of the sleep function, these two examples are identical. Unfortunately, this is the extent of the similarities in signal handling between the two platforms.
UNIX uses signals to send alerts to processes when specific actions occur. A UNIX application would use the kill function to activate signals internally. As discussed earlier, Win32 provides only limited support for signals. As a result, you have to rewrite your code to use another form of event notification in Win32.
The following example illustrates how you would convert UNIX code to Windows messages or event objects. It shows a simple main that forks a child process, which issues the SIGALRM signal. The parent process catches the alarm and outputs a message when it is received.
#include <unistd.h> #include <stdio.h> #include <signal.h> static int alarm_fired = 0; /* The alrm_bell function simulates an alarm clock. */ void alrm_bell(int sig) { alarm_fired = 1; } int main() { int pid; /* Child process waits for 5 secs before sending SIGALRM to its parent. */ printf("alarm application starting\n"); if((pid = fork()) == 0) { sleep(5); kill(getppid(), SIGALRM); exit(0); } /* Parent process arranges to catch SIGALRM with a call to signal and then waits for the child process to send SIGALRM. */ printf("waiting for alarm\n"); (void) signal(SIGALRM, alrm_bell); pause(); if (alarm_fired) printf("Ring...Ring!\n"); printf("alarm application done\n"); exit(0); }
In the first Win32 example that follows , a form of Microsoft Windows Messages is used to signal the parent process. In the example, the SetTimer function is used to signal the parent process that an alarm has been activated. Although code could have been created to do the timing, using the SetTimer function greatly simplifies this example.
Another advantage of using SetTimer is that the callback function is invoked in the same thread that calls SetTimer . No synchronization is necessary.
If the requirements are simple, consider using a thread to act as a timer thread, which simply calls Sleep to create the desired delay. At the end of the delay, a callis made to a timer callback function. The problem with this approach is that the callback function is called from a different thread than your primary thread. If the callback function requires resources that are thread specific, you will need to use one of the appropriate synchronization mechanisms discussed in Threads later in this chapter.
Additional code has been added to the example so that an application using this code can catch any standard Windows message as well as application- and user-defined messages. You can use these messages to engineer solutions to other signals that are not directly supported by the native signal implementation in Win32.
#include <windows.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> static int alarm_fired = 0; /* The alrm_bell function simulates an alarm clock. */ VOID CALLBACK alrm_bell(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { alarm_fired = 1; printf("Ring...Ring!\n"); } void main() { printf("alarm application starting\n"); /* Set up a 5 second timer which calls alrm_bell */ SetTimer(0, 0, 5000, (TIMERPROC)alrm_bell); printf("waiting for alarm\n"); MSG msg = { 0, 0, 0, 0 }; /* Get the message, & dispatch. This causes alrm_bell to be invoked. */ while(!alarm_fired) if (GetMessage(&msg, 0, 0, 0)) { if (msg.message == WM_TIMER) printf("WM_TIMER\n"); DispatchMessage(&msg); } printf("alarm application done\n"); exit(0); }
Notice in this example that the WM_TIMER message is issued and captured by the GetMessage function. If you remove the call to DispatchMessage , the alrm_bell function would never be called, but the WM_TIMER message would be received. With this simple application, you can capture a variety of Windows messages. Moreover, if you want to trigger the callback function before the specified time, you can use the PostMessage (WM_TIMER) call. This is analogous to using the kill function to send a signal in UNIX.
Some events that UNIX handles through signals are represented in Win32 as objects. Functions are available to integrate these event objects. An example of these functions is WaitForSingleObject .
In the example code that follows, a timer object is used to signal when a timed interval has elapsed. Again, this example provides the same functionality as the preceding UNIX SIGALRM example.
Note | While this illustration encompasses the process in a single thread, this is not a requirement. The timer object can be tested and waited for in other threads if necessary. |
#define _WIN32_WINNT 0X0500 #include <windows.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> void main() { HANDLE hTimer = NULL; LARGE_INTEGER liDueTime; liDueTime.QuadPart = -50000000; printf("alarm application starting\n"); // Set up a 5 second timer object hTimer = CreateWaitableTimer(NULL, TRUE, "WaitableTimer"); SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0); // Now wait for the alarm printf("waiting for alarm\n"); // Wait for the timer object WaitForSingleObject(hTimer, INFINITE); printf("Ring...Ring!\n"); printf("alarm application done\n"); exit(0); }
Win32 does not support sigaction . The UNIX example that follows shows how sigaction is typically used in a UNIX application. In this example, the handler for the SIGALRM signal has been set. How this code can be converted to use Windows Messages was shown earlier. You could also use Windows Messages here if you prefer.
Note | To terminate this application from the keyboard, press CTRL+\. |
#include <unistd.h> #include <stdio.h> #include <signal.h> void intrpt(int signum) { printf("I got signal %d\n", signum); } int main() { struct sigaction act; act.sa_handler = intrpt; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT, &act, 0); while(1) { printf("Hello World!\n"); sleep(1); } }