iSeries distinguishes between hardware exceptions, POSIX signals (sometimes called asynchronous signals), and ANSI C signals. POSIX signals use the APIs kill(), sigaction(), pthread_kill(), alarm(), pause(), and others for signal interaction. ANSI C signals use the APIs raise(), signal(), and abort() for signal interaction.
Many other systems, by default, generate a POSIX signal whenever a software or hardware exception occurs (such as using a pointer that is not valid, or an error caused by dividing by zero), and on those systems, a POSIX signal may be equivalent and indistinguishable from an ANSI C signal. If the signal is not handled, this results in the termination of the process.
The operating system does not generate a signal for these hardware or software problems, but instead, generates an exception message. The exception message moves up the call stack, allowing each stack frame (function on the stack or invocation entry) a chance to handle the exception. Each function invocation may choose whether or not to handle the exception. If the exception is not handled, the message continues to the next stack frame.
When the exception message reaches certain boundaries on the call stack (such as a main() entry point, usually called control boundaries), certain events take place. These events include changing the exception to a different type, terminating the process, terminating the activation group, or terminating the thread. If an exception that is not handled occurs in a secondary thread and moves all the way to the first invocation in the thread without being handled, the resulting action is to terminate the thread. During this movement, if the exception hits a control boundary and is not handled, it may terminate the process.
The integrated language environment (ILE) C was present on the system before the POSIX signals implementation. Therefore, the ILE C uses the robust iSeries exception model to implement ANSI C signals (raise(), signal(), abort()). The ILE C also provides the generation of an ANSI C signal when it detects a hardware exception. Thus, using the signal() API, you can monitor and handle hardware exceptions.
A signal is never automatically generated for an exception message. iSeries hardware and software exceptions cannot be detected using asynchronous signal mechanisms. In other words, if you use sigaction() for the SIGSEGV signal, you will not detect that signal when a pointer that is not valid is used. If you use signal(), you will detect SIGSEGV when your code uses an invalid pointer.
If the preferred signal model is the asynchronous signal model, you can use iSeries exception handlers or ANSI C signal handlers to generate a asynchronous signal when those events occur.
The following example shows how an error caused by dividing by zero and the use of an invalid pointer might be changed into an asynchronous signal. The following example uses ANSI C signal handlers to perform the signal mapping.
See Code disclaimer information for information pertaining to code examples.
#define _MULTI_THREADED #include <stdio.h> #include <qp0z1170.h> #include <time.h> #include <signal.h> #include <pthread.h> #include "check.h" void myAnsiSignalMapperHdlr(int sigNumber); void *threadfunc1(void *parm); void *threadfunc2(void *parm); void *threadfunc1(void *parm) { char *p=NULL; printf("Thread1: Unhandled exception (pointer fault) about to happen\n"); *p = `!'; printf("Thread1: After exception\n"); return NULL; } void *threadfunc2(void *parm) { int i1=0, i2=1, i3=0; printf("Thread2: Unhandled exception (divide by zero) about to happen\n"); i1 = i2 / i3; printf("Thread2: After exception\n"); return NULL; } void myAnsiSignalMapperHdlr(int sigNumber) { /* In a multithreaded environment, this is slightly difficult. We have to */ /* re-enable the ANSI C handler immediately, because that is the way it */ /* is defined. (A better alternative may be direct monitor exception */ /* handlers which are always valid in the function which they are */ /* registered, and with direct monitors, we can catch the hardware */ /* exception before it is converted to an ANSI C signal */ signal(SIGALL, myAnsiSignalMapperHdlr); /* Since ANSI C signals and hardware exceptions will only be handled in */ /* the same thread that caused them, we will send the POSIX signal to */ /* the calling thread (The signal will be delivered before returning from */ /* pthread_kill(). */ printf("Mapping ANSI signal to POSIX signal %d\n", sigNumber); pthread_kill(pthread_self(), sigNumber); return; } void fpViolationHldr(int sigNumber) { printf("Thread 0x%.8x %.8x " "Handled floating point failure SIGFPE (signal %d)\n", pthread_getthreadid_np(), sigNumber); /* By definition, returning from a POSIX signal handler handles the signal*/ } void segFaultHdlr(int sigNumber) { printf("Thread 0x%.8x %.8x " "Handled segmentation violation SIGSEGV (signal %d)\n", pthread_getthreadid_np(), sigNumber); /* By definition, returning from a POSIX signal handler handles the signal*/ } int main(int argc, char **argv) { int rc=0; pthread_t threadid; struct sigaction actions; void *status; printf("----------- Setup Signal Mapping/Handling -------------\n"); printf("- Register ANSI C signal handler to map ALL\n" " ANSI C signals & hardware exceptions to POSIX signals\n"); signal(SIGALL, myAnsiSignalMapperHdlr); printf("- Register normal POSIX signal handling mechanisms\n" " for floating point violations, and segmentation faults\n" "- Other signals take the default action for asynchronous signals\n"); memset(&actions, 0, sizeof(actions)); sigemptyset(&actions.sa_mask); actions.sa_flags = 0; actions.sa_handler = fpViolationHldr; rc = sigaction(SIGFPE,&actions,NULL); checkResults("sigaction for SIGFPE\n", rc); actions.sa_handler = segFaultHdlr; rc = sigaction(SIGSEGV,&actions,NULL); checkResults("sigaction for SIGSEGV\n", rc); printf("----------- Start memory fault thread -------------\n"); printf("Create a thread\n"); rc = pthread_create(&threadid, NULL, threadfunc1, NULL); checkResults("pthread_create()\n", rc); rc = pthread_join(threadid, &status); checkResults("pthread_join()\n", rc); printf("----------- Start divide by 0 thread -------------\n"); printf("Create a thread\n"); rc = pthread_create(&threadid, NULL, threadfunc2, NULL); checkResults("pthread_create()\n", rc); rc = pthread_join(threadid, &status); checkResults("pthread_join()\n", rc); printf("Main completed\n"); return 0; }
----------- Setup Signal Mapping/Handling ------------- - Register ANSI C signal handler to map ALL ANSI C signals & hardware exceptions to POSIX signals - Register normal POSIX signal handling mechanisms for floating point violations, and segmentation faults - Other signals take the default action for asynchronous signals ----------- Start memory fault thread ------------- Create a thread Thread1: Unhandled exception (pointer fault) about to happen Mapping ANSI signal to POSIX signal 5 Thread 0x00000000 00000022 Handled segmentation violation SIGSEGV (signal 5) Thread1: After exception ----------- Start divide by 0 thread ------------- Create a thread Thread2: Unhandled exception (divide by zero) about to happen Mapping ANSI signal to POSIX signal 2 Thread 0x00000000 00000023 Handled floating point failure SIGFPE (signal 2) Thread2: After exception Main completed
The following example shows how a divide by zero error, and a dereference of a pointer that is not valid might be mapped to generate a POSIX (asynchronous) signal. This example uses exception handlers to perform the signal mapping.
See Code disclaimer information for information pertaining to code examples.
#define _MULTI_THREADED #include <stdio.h> #include <stdlib.h> #include <qp0z1170.h> #include <time.h> #include <signal.h> #include <except.h> #include <qusec.h> /* System API error Code structure */ #include <qmh.h> /* Message Hanlder common defs */ #include <qmhchgem.h> /* Change exception message */ #include <pthread.h> #include "check.h" void myHardwareExceptionMapper(_INTRPT_Hndlr_Parms_T *exception); void *threadfunc1(void *parm); void *threadfunc2(void *parm); void *threadfunc1(void *parm) { char *p=NULL; /* Watch for all ESCAPE type exceptions. Other types may be used for */ /* job log messages or C++ exceptions or other control flow in the process*/ /* Adjust the message type as required by your application. */ #pragma exception_handler (myHardwareExceptionMapper, 0, _C1_ALL, _C2_MH_ESCAPE) printf("Thread1: Unhandled exception (pointer fault) about to happen\n"); *p = `!'; printf("Thread1: After exception\n"); #pragma disable_handler return NULL; } void *threadfunc2(void *parm) { int i1=0, i2=1, i3=0; /* Watch for all ESCAPE type exceptions. Others types may be used for */ /* job log messages or C++ exceptions or other control flow in the process*/ /* Adjust the message type as required by your application. */ #pragma exception_handler (myHardwareExceptionMapper, 0, _C1_ALL, _C2_MH_ESCAPE) printf("Thread2: Unhandled exception (divide by zero) about to happen\n"); i1 = i2 / i3; printf("Thread2: After exception\n"); #pragma disable_handler return NULL; } void myHardwareExceptionMapper(_INTRPT_Hndlr_Parms_T *exInfo) { int sigNumber; Qus_EC_t errorCode = {0}; /* system API error structure */ printf("Handling system exception\n"); /* The exception information is available inside the exInfo structure */ /* for this example, we are going to handle all exceptions and then map */ /* them to an \Qappropriate' signal number. We are allowed to decide the */ /* signal mapping however is appropriate for our application. */ if (!memcmp(exInfo->Msg_Id, "MCH3601", 7)) { sigNumber = SIGSEGV; } else if (!memcmp(exInfo->Msg_Id, "MCH1211", 7)) { sigNumber = SIGFPE; } else { printf("Unexpected exception! Not Handling!\n"); abort(); } /* Even if the exception is \Qexpected', we are going to handle it and try */ /* to deliver it as a POSIX signal. Note that we SHOULD NOT HANDLE */ /* exceptions that are unexpected to us. Most code cannot tolerate */ /* getting back into it once the exception occured, and we could get into */ /* a nice exception loop. */ /* See the system API reference for a description of QMHCHGEM */ QMHCHGEM(&exInfo->Target, 0, &exInfo->Msg_Ref_Key, QMH_MOD_HANDLE, (char *)NULL, 0, &errorCode); if (errorCode.Bytes_Available != 0) { printf("Failed to handle exception. Error Code = %7.7s\n", errorCode.Exception_Id); return; } printf("Mapping Exception %7.7s to POSIX signal %d\n", exInfo->Msg_Id ,sigNumber); /* At this point the exception is handled. If the POSIX signal handler */ /* returns, then the signal will be handled, and all will be complete */ pthread_kill(pthread_self(), sigNumber); return; } void fpViolationHldr(int sigNumber) { printf("Thread 0x%.8x %.8x " "Handled floating point failure SIGFPE (signal %d)\n", pthread_getthreadid_np(), sigNumber); /* By definition, return from a POSIX signal handler handles the signal */ } void segFaultHdlr(int sigNumber) { printf("Thread 0x%.8x %.8x " "Handled segmentation violation SIGSEGV (signal %d)\n", pthread_getthreadid_np(), sigNumber); /* By definition, returning from a POSIX signal handler handles the signal*/ } int main(int argc, char **argv) { int rc=0; pthread_t threadid; struct sigaction actions; void *status; printf("----------- Setup Signal Mapping/Handling -------------\n"); printf("- The threads will register iSeries Exception handler to map\n" " hardware exceptions to POSIX signals\n"); printf("- Register normal POSIX signal handling mechanisms\n" " for floating point violations, and segmentation faults\n" "- Other signals take the default action for asynchronous signals\n"); memset(&actions, 0, sizeof(actions)); sigemptyset(&actions.sa_mask); actions.sa_flags = 0; actions.sa_handler = fpViolationHldr; rc = sigaction(SIGFPE,&actions,NULL); checkResults("sigaction for SIGFPE\n", rc); actions.sa_handler = segFaultHdlr; rc = sigaction(SIGSEGV,&actions,NULL); checkResults("sigaction for SIGSEGV\n", rc); printf("----------- Start memory fault thread -------------\n"); printf("Create a thread\n"); rc = pthread_create(&threadid, NULL, threadfunc1, NULL); checkResults("pthread_create()\n", rc); rc = pthread_join(threadid, &status); checkResults("pthread_join()\n", rc); printf("----------- Start divide by 0 thread -------------\n"); printf("Create a thread\n"); rc = pthread_create(&threadid, NULL, threadfunc2, NULL); checkResults("pthread_create()\n", rc); rc = pthread_join(threadid, &status); checkResults("pthread_join()\n", rc); printf("Main completed\n"); return 0; }
Output
----------- Setup Signal Mapping/Handling ------------- - The threads will register iSeries Exception handler to map hardware exceptions to POSIX signals - Register normal POSIX signal handling mechanisms for floating point violations, and segmentation faults - Other signals take the default action for asynchronous signals ----------- Start memory fault thread ------------- Create a thread Thread1: Unhandled exception (pointer fault) about to happen Handling system exception Mapping Exception MCH3601 to POSIX signal 5 Thread 0x00000000 00000024 Handled segmentation violation SIGSEGV (signal 5) Thread1: After exception ----------- Start divide by 0 thread ------------- Create a thread Thread2: Unhandled exception (divide by zero) about to happen Handling system exception Mapping Exception MCH1211 to POSIX signal 2 Thread 0x00000000 00000025 Handled floating point failure SIGFPE (signal 2) Thread2: After exception Main completed
Top | Pthread APIs | APIs by category |