Wednesday, August 30, 2006

IDL to Java Mapping

CORBA IDL Series

Common Concepts

All the IDL types when mapped to Java, output two classes
  • Holder
  • Helper
Holder

Holder classes are used to simulate the C++ reference passing style. Consider for example, some generated method as below

void foo(Bar b) {
b = new Bar();
b.init(...); // probably reading from nw
return b;
}

Now, in this scenario, the calling function can never get the reference to the created Bar object.
In CORBA, the above scenario could be using network to read the value of a new Bar object and then make available the value to the calling function - typical out or inout parameters. To take care of this, Holder objects are used. For every Bar object there will be a BarHolder object which can read the Bar object from the wire (as it implements org.omg.CORBA.portable.Streamable interface). A typical scenario could be like -

void callingFunction() {
BarHolder holder = new BarHolder();
foo(holder);
Bar b = holder.value;
...
}
void foo(BarHolder holder, InputStream is) {
holder._read(is); // read from nw; part of Streamable interface
return holder;
}

Helper

Helpers have some common functionality for all IDL mapped types (but for Boxed Valuetypes). These are

  • Provide read functionality from org.omg.CORBA.portable.InputStream. Infact holders finally call this for the contents they are holding.
  • Provide write functionality to the org.omg.CORBA.portable.OutputSteam
  • Return the typecode of the IDL type
  • Return the repository Id of the IDL type
For interfaces, they have further functionality to provide the following

  • Repository Id and typecode of the IDL type
  • Extraction and Insertion operators to CORBA Any
  • Narrow and safe narrows from base types. Please note that for Abstract interfaces, the narrow and safe narrow functionality is provided from java.lang.object. For those interfaces which inherit from abstract interfaces, narrow and safe narrow allow from both java.lang.object and org.omg.CORBA.Object and for pure interfaces, only org.omg.CORBA.Object is allowed
  • VisiBroker prorpritory bind operations are provided for both abstact and interfaces (but not for local interfaces)
  • Convinience factory operations for Valuetypes
org.omg.CORBA.portable.IDLEntity

All the main java mapping classes for IDL types implement or extend from this class. This class is used in for the reverse mapping (Java to IDL mapping) while marshaling. Typically, the marshal engine will detect that an entity is an instance of this interface and call the helper classes to marshal and unmarshal.

This class inherits from java.io.Serializable.

Mapping from IDL basic types to Java

boolean -> boolean
char -> char
wchar -> char
octet ->byte
string -> java.lang.String
wstring -> java.lang.String
short and unsigned short -> short
long and unsigned long -> int
long long and unsigned long long -> long
float -> float
double -> double
fixed -> java.math.BigDecimal

Constants

Constants can be defined either in the module scope or an interface scope. Mapping for these both is different in Java.

Constants in Module namespace

These are mapped to public interface with the same name as the constant and containing a field named "value" holding the constants value.

Constants in Interface namespace

The mapping is to fields in either the Java operations interface for non-abstract or the sole Java interface for the abstract interface.

Example

// IDL
module Bar {
interface Foo {
const long FooLong = 1;
};
const long BarLong = 2;
}

// Java
package Example;
public interface FooOperations {
int FooLong = 1;
}
public interface BarLong {
int value = 2;
}

Mapping for Structs and Unions

Structs are mapped to a final Java class with the same name as the IDL struct with the following charecteristics

  • Implements org.omg.CORBA.portable.IDLEntity interface
  • All the IDL struct members are public fields in the mapped class.
  • All value and null constructors are also provided. Strings are given "" as default value.
Helpers and Holders are also generated

Unions are also mapped to a final Java class with the same name as the IDL union with the following characteristics

  • Implements org.omg.CORBA.portable.IDLEntity interface
  • accessor method for the descriminator with the name discriminator(). No modifier
  • accessor and modifier for each branch
  • modifier for each branch with more than one case label
  • modifier for the branch corresponding to the default case label if present
  • default modifier if needed
Helpers and Holders are also generated.

Example

// IDL
struct StructType {
long field1;
string field2;
};

enum EnumType { first, second, third, fourth, fifth};
union UnionType switch(EnumType) {
case first: long win;
case second: short place;
case third:
case fourth: octet show;
default: boolean other;
};

//Java
final public class StructType implements org.omg.CORBA.portable.IDLEntity {
public int field1;
public string field2 = "";
public StructType() {}
public StructType(int f1, string f2) {...}
}

final public class UnionType implements org.omg.CORBA.portable.IDLEntity {
public UnionType() {...}
public EnumType descriminator() {...}
public int win() {...}
public void win(int i) {...}
public short place() {...}
public void place(short s) {...}
public byte show() {...}
public void show(byte b) {...}
public void show(EnumType d, byte value) {...}
public boolean other() {...}
public void other(boolean b) {...}
public void other(EnumType e, boolean b) {...}
}

Enums

An enum is mapped to a Java final class implementing IDLEntity interface as below. Helper and Holder classes are generated.

// IDL
enum EnumType {first, second}

// Java
public final class EnumType implements org.omg.CORBA.portable.IDLEntity {
protected EnumType(int i) { ...}
public int value() {...}
public static EnumType from_int(int v) {...}

public static final int FIRST = 0;
public static final EnumType first = new EnumType(FIRST);

public static final int SECOND = 0;
public static final EnumType second = new EnumType(SECOND);
}

Sequence and Arrays

An IDL sequence is mapped to a Java array with the same name. In the mapping, everywhere the sequence type is needed, an array of the sequence type is used. Bounds checking is done at the time of marshalling CORBA::MARSHAL exception will be raised.

An array is mapped just like bound sequence.

Helper and Holder classes are generated.

Exceptions

Their mapping is similar to structs. All CORBA user exceptions inherit from org.omg.CORBA.UserException which is a checked exception and implements org.omg.CORBA.portable.IDLEntity. Helper and Holder classes are also generated.

System exceptions are unchecked exceptions which inherits from java.lang.SystemException.

Interfaces

Apart from a Helper and a Holder class, an Interface is mapped to -
  • Operations interface with the same name as the IDL interface suffixed by "Operations"
  • Signature interface inheriting the operations interface, org.omg.CORBA.portable.IDLEntity and org.omg.CORBA.Object interfaces.
VisiBroker in total generates 7 classes for an interface. These are (1) Helper (2) Holder (3) Operations Interface (4) Signature Interace (5) Stub (6) Skeleton (7) Tie

Portability

Since java code is often downloaded over the web and used with ORBs that are different from where it was developed, a need for portability layer which the generated stubs and skeletons can use arises. If the stubs and skeletons were coupled with the ORB, then for the downloaded code to work, the entire ORB aswell will also be needed to be downloaded.

To achieve this approach, two styles can be implemented by the ORB vendors -
  • Streaming style Stubs and Skeletons - Uses the various stream classes
  • DII/DSI style Stubs and Skeletons - Uses the DII/DSI Request objects and anys.
Portability Architecture

Tha major components to this architecture are -
  • Portable Streamable layer - provided by org.omg.CORBA.portable.Streamable interface. This interface helps in providing the notion of complex streamable objects are implemented in the helper classes.
  • Portable Stream classes - org.omg.CORBA.portable.OutputStream wraps the logic of outputting CORBA IDL types to a stream. This object can be got by using ORB.create_output_stream() method. From this object, org.omg.CORBA.portable.InputStream object can be got which allows to read CORBA data from a stream. These two classes inherit from java.io.OutputStream and java.io.InputStream. Package org.omg.CORBA_2_3.portable provides two more input and output classes.
  • Portable Skeletons and Stubs
  • Portable Stub and Servant Delegate

Client Side Mapping

  • Stub inherits from org.omg.CORBA.portable.ObjectImpl and implements the Signature interface.
  • Signature interface inherits from the Operations interface and org.omg.CORBA.portable.Object
  • org.omg.CORBA.portable.ObjectImpl implements org.omg.CORBA.Object
  • org.omg.CORBA.portable.ObjectImpl uses org.omg.CORBA.portable.Delegate
In VisiBroker,
  • Stub inherits from com.inprise.vbroker.CORBA.portable.ObjectImpl and implements the Signature interface.
  • Signature interface inherits from the Operations interface and org.omg.CORBA.portable.Object
  • com.inprise.vbroker.CORBA.portable.ObjectImpl extends org.omg.CORBA_2_3.portable.ObjectImpl and implements com.inprise.vbroker.CORBA.Object
  • com.inprise.vbroker.CORBA.Object extends org.omg.CORBA.Object
  • org.omg.CORBA_2_3.portable.ObjectImpl extends org.omg.CORBA.portable.Object
  • com.inprise.vbroker.CORBA.portable.ObjectImpl uses com.inprise.vbroker.orb.DelegateImpl which inherits from com.inprise.vbroker.CORBA.portable.Delegate which inturn extends org.omg.CORBA_2_3.portable.Delegate further extending org.omg.CORBA.portable.Delegate
Stubs could be either Stream based or DII based. If they are stream based, they would use the Steam classes. Otherwise, they would use DII. All the operations are generally delegated to the Delegate objects internally. Stubs make calls on their super classes which at some point get delegated to the delegate objects.

Server Side Mapping

On the server side, an implementation can be provided either by using
  • Inheritance
  • Delegation
For this purpose, classes named POA.java and POATie.java are created. POA.java is the actual skeleton class and inherits from org.omg.PortableServer.Servant class. This is the mapping of the native Servant defined in PortableServer.idl.

If a Stream based skeleton is being used, then the skeleton inherits from org.omg.PortableServer.Servant and futher implements org.omg.CORBA.portable.InvokeHandler. This class then uses the various stream classes for reading and writing data. Otherwise, org.omg.CORBA.DynamicImplementation is the base class.

org.omg.PortableServer.portable.Servant uses org.omg.PortableServer.Delegate to delegate all its functionality. Generally, this is initialized by calling ORB.set_delegate() which then creates a delegate object and calls back setting the delegate on this servant. In VisiBroker, this delegate object is com.inprise.vbroker.poa.ServantDelegate.

Monday, August 28, 2006

VBJ Implementation: ORB.init


  • org.omg.CORBA.ORB.init(...)
  • This class reads the property "org.omg.CORBA.ORBClass" and instantiates the
    the class using newInstance

  • Calls set_parameters(...)
  • This call comes to com.inprise.vbroker.orb.ORB class
  • initializes the com.inprise.vbroker.properties.PropertyManager and is cached in ORB
  • Processes the command-line and VM properties using com.inprise.vbroker.properties.CommandLineProcessor
  • PropertyManager has a ChainProperties class which keeps an assortment of properties of different types such as command line properties, system properties (VM), properties loaded from file and some others.
  • Command-line and VM properties are added to the ChainProperties
  • Command-line parameters starting with -OA or -vbroker are added directly into the command line property set in the chainProperties. While adding, any templates are expanded. Any properties starting with -ORB are migrated to new properties and added in.
  • Similarly, the system properties (VM) are processed and added to the ChainProperties in the PropertyManager
  • Call initialize()
  • The properties passed in during ORB.init() are now processed as above and added to PropertyManager
    which is internally cached in ORB properties set.
  • File ORB.properties is then read and all the properties are again processed and added to the
    PropertyManager into DEF_PROPS bucket
  • Properties are then read from the file pointed by ORBpropStorage and loaded into FILE_PROPS bucket.
  • If the property vbroker.orb.propOrdering is set, then the buckets in the ChainProperties is ordered
    to be compliant to be with the specified ordering
  • Initialize Web Service
  • Initialize Logger
  • Instantiates a new com.inprise.vbroker.interceptor.Init; This class implements com.inprise.vbroker.interceptor.ServiceLoader interface
    and has init, init_completed and shutdown API
  • Calls init()
  • Instantiates a new com.inprise.vbroker.interceptor.InterceptorManagerImpl object
    which implements InterceptorManagerControl which has API to get_manager
  • Constructor initialize the following interceptor managers
  • "Bind" -> BindInterceptorManagerExt
  • "POALifeCycle" -> POALifeCycleInterceptorManagerExt
  • "ServiceResolver" -> ServiceResolverInterceptorManagerExt
  • "PolicyCreation" -> PolicyInterceptorManagerExt
  • "URL" -> URLInterceptorManager
  • "ObjectKeyAliasing" -> ObjectKeyAliasInterceptorManager
  • "Connection" -> ConnectionInterceptorManager
  • "POACreation" -> POACreationInterceptorManager
  • "GlobalServerRequest" -> ServerRequestInterceptorManagerExt
  • "GlobalIORCreation" -> IORCreationInterceptorManagerExt
  • "EventQueue" -> EventQueueManager
  • Add another ServiceResolver for resolving the Control with following name
    "VisiBrokerInterceptorControl"
  • Create a new instance of ProtocolManager of type com.inprise.vbroker.ProtocolEngine.ManagerImpl
    Initialize the various Protocol Engine bits. The factories are all anonymous objects that create the
    objects that are shown after th "->"
  • Server Engine factory -> com.inprise.vbroker.ProtocolEngine.ServerEngineImpl
  • Threadpool Dispatcher factory -> com.inprise.vbroker.orb.TPDispatcherImpl
  • NIO Threadpool Dispatcher factory -> com.inprise.vbroker.orb.TPDispatcherImplNio
  • Thread Session Dispatcher factory -> com.inprise.vbroker.orb.TSDispatcherImpl
  • Main Thread Dispatcher factory -> com.inprise.vbroker.orb.MTDispatcherImpl
  • Connection Pool factory -> com.inprise.vbroker.orb.ConnectionPool
  • Socket Server Connection Manager factory -> com.inprise.vbroker.orb.SocketSCM
  • NIO Socket Server Connection Manager factory -> com.inprise.vbroker.orb.SocketSCMnio
  • URL manager is cached and the following URL resolvers are installed. URL resolver have
    this notion of supported protocols which they can resolve.
  • com.inprise.vbroker.IIOP.IIOPLocResolver
  • com.inprise.vbroker.IIOP.CorbaLocResolver
  • com.inprise.vbroker.IIOP.IIOPNameResolver
  • Install all Service loaders listed in the property vbroker.orb.systemLibs.application property.
    In this case, the property value points to service loaders.
  • Install all the Service loaders listed in the property vbroker.orb.dynamicLibs.
    The funny thing in this case is that vbroker.orb.dynamicLibs is a prefix and there could
    be number of vbroker.orb.dynamicLibs.<xxx> properties for example vbroker.orb.dynamicLibs.naming
    and the value of these properties will be the actual service loaders. Also, for each of these,
    the value could be comma separated list.
  • Each of the Service Loaders thus found is then initialized by calling Init().
  • com.inprise.vbroker.IIOP.Init
  • com.inprise.vbroker.LIOP.Init
  • com.inprise.vbroker.qos.Init
  • com.inprise.vbroker.ds.Init
  • com.inprise.vbroker.URLNaming.Init
  • com.inprise.vbroker.dynamic.Init
  • com.inprise.vbroker.ir.Init
  • com.inprise.vbroker.naming.Init
  • com.inprise.vbroker.ServerManager.Init
  • com.inprise.vbroker.IOP.Init
  • com.inprise.vbroker.CONV_FRAME.Init
  • com.inprise.vbroker.PortableInterceptor.Init
  • com.inprise.vbroker.rmi.CORBA.Init
  • com.borland.vbroker.notify.Init
  • com.borland.vbroker.CosTime.Init
  • com.inprise.vbroker.naming.Initilize
  • ORB initializers are now installed
  • Property org.omg.PortableInterceptor.ORBInitializerClass is
    used as prefix. All the property names starting with prefix is got
    and the actual Initializer class are the suffix after removing the
    prefix and added to a collection
  • If the property vbroker.orb.enableNativeMessaging is true, then
    the init class com.borland.vbroker.NativeMessaging.Init is added to
    the collection.
  • com.borland.security.core.Init is always added to the collection.
  • All the classes are now loaded and instantiated and added to the
    _services and _orbInitializer lists.
  • Add com.inprise.vbroker.poa.POABidder as client engine
  • Register value factories for VisiObjectReferenceTemplate and VisiTransientObjectReferenceTemplate.
  • Call the pre_init and post_init operations on the ORB initializers.
  • Cache all the property settings in the ORB such as vbroker.orb.tcIndirection.
    There are lot of such properties cached here.
  • Call complete_init() on all the ServiceLoaders now.

Tuesday, August 15, 2006

Unix Signal Handling

Some good links -
http://www.tldp.org/LDP/lpg/node138.html
http://www.enderunix.org/simsek/articles/signals.pdf
http://www.cs.ucsb.edu/~almeroth/classes/W99.276/assignment1/signals.html
http://www.opengroup.org/onlinepubs/007908799/xsh/sigaction.html

What are Signals

Signals are a means by which a process or a thread can be notified of a particular event. This is very similar to hardware interrupts where a partiular hardware state is notified to the operating system by raising an interrupt and the OS then performs the interrupt handler. The difference in this case being that instead of hardware telling the OS, it is the OS telling the process.

There are typically two kinds of signals -

  • Synchronous signals - also referred to as Traps. This typically occurs as a result of the current execution stream, where an unrecoverable error such as illegal memory access or divide by zero causes a trap into a kernel trap handler which then get delivered to the currently executing thread.
  • Asynchronous signals - these are as a result of external and unrelated events, such as another process sending using the kill to send this process a signal, or the user performing job control operations such as putting the process to sleep. In this case, the signal is delivered to the process and if an explicit disposition is instructed, then it is delivered to the first unblocked thread available.
All signals have a name and a number.

Following conditions generate signals -

  • Job-Control operations such as Ctrl-C, Ctrl-Z etc
  • Traps such as divide by zero, invalid memory reference
  • kill() system call
  • Kernel sending expicit signals on certain events to which the process should respond such as SIGPIPE is generated when process writes to pipe
When a signal occurs due to whatever reasons, it is said to be generated. At this time, determination is made whether the signal needs to be delivered to a specfic thread or to the process. Synchronous signals obviously go to the thread that caused it, while the asynchrounous signals go to the process. Every signal has a signal disposition. Signal disposition is actually the way in which the process would handle it. The signal disposition could be

  • Ignore the signal
  • Call a specific signal handler in the process
  • Apply the default action
By default, the default action would be applied. Typically, depending on the signal, the process could

  • Exit
  • Dump Core
  • Stop (suspend)
  • Ignore
An important point here is that the disposition for a signal is shared by all the threads in the process. When a signal's disposition is applied, the signal is said to be delivered. A signal can also be accepted by the process by having some thread wait for the signal by using the sigwait() function. Between the time the signal is generated and delivered or accepted, the signal is said to be pending.

It is also possible for a process to block the signal from being delivered and when that happens, the signal remains in the pending state until either the signal is ignored or unblocked. In a single threaded applications, this is done by applying a mask for the process. In a multi-threaded application, each thread can have the mask. Typically, the threads inherit the mask of the parent thread. When all the threads have blocked the signal, then the signal will remain pending until it is either unblocked or ignored. In a multi-threaded process, the signal will be delivered to the first thread found which is not blocking the signal.

Summarizing the above -

  • When a signal occurs, we say signal is generated
  • We define an action for required signals - signal disposition
  • When an action is taken for a signal, this means signal is delivered
  • If a signal is between generation and delivery, this means signal is pending
  • We can block a signal for a process. If the process doesn't ignore the blocked signal, the signalwill be pending
  • A blocked signal can be generated more than once before the process unblocks the signal. The kernel can deliver the signal once or more. If it delivers signals more than once, we say the signal is queued. If the signals are delivered only once, it is not queued
  • Each process (in single threaded application) or a thread (in a multi-threaded application) has a signal mask. Signal masks define blocked signals for a process or thread. It is just a bitarray which includes one bit for each signal. If the bit is on, that means related signal will be blocked
  • POSIX defines a data structure which holds one bit for each possible signal. This data structure isnamed signal set. It is an integer like data type. An integer is at least 32 bit value in 32 bitarchitectures. So we can hold status information for 32 different signals. We can define signalmask as a signal set. We can manage them by using some custom functions. POSIX defines somefunctions to modify any given signal set.
API for signal processing

Because of the history of Unix systems progression, there are a lot of APIs that help process signals. Broadly speaking, there are three major sets of API -
  • System V Systems Environment
  • BSD 4.3 Environment
  • POSIX environment
Unreliable Signals

This is the signal environment for the traditional System V systems. Some of the major limitations of this environment are -

  • Signal handlers are reset to SIG_DFL prior to being called - "one-shot" signal behavior
  • Recursive signal handling is always allowed
  • System calls interrupt when a signal is delivered
The above behavior cause the unreliable behavior.

These "one-shot'' signals must re-install the signal handler within the signal handler itself, if the program wishes the signal to remain installed. Because of this, there is a race condition in which the signal can arrive again before the handler is re-installed, which can cause the signal to either be lost or for the original behavior of the signal to be triggered (such as killing the process). Therefore, these signals are "unreliable'' because the signal catching and handler re-installation operations are nonatomic.

Under unreliable signal semantics, system calls are not restarted automatically when interrupted by a signal. Therefore, in order for a program to account for all cases, the program would need to check the value of errno after every system call, and reissue the system call if its value is EINTR.

Along similar lines, unreliable signal semantics don't provide an easy way to get an atomic pause operation (put the process to sleep until a signal arrives). Because of the unreliable nature of reinstalling signal handlers, there are cases in which a signal can arrive without the program realizing this.


int flag = 0;
main()
{
...
signal(SIGINT, handler);
...
while(flag == 0) {
pause();
}
...
}

handler()
{
signal(SIGINT, handler);
flag = 1;
}

In the above example, signals could be lost after entering the handler function, but before
the signal handler could be re-installed. Another race-condition would be when the flag is false and hence we enter the while-loop, but before we enter pause(), the signal occurs (Pause is used to wait for a signal to occur and returns as soon as a signal occurs). In this case the pause() misses the signal and potentially could wait forever if the signal does not occur again.

The interface to the traditional signal handling environment is:


int (*signal)(int signal_number, int (*function)(int));

Change the signal handler for the indicated signal. Signal_number indicates which signal, function is the new handler.


int pause(void);

Wait for a signal to arrive.

Two standard signal handlers exist:

  • SIG_DFL - Take the default action for this signal.
  • SIG_IGN - Ignore this signal.

BSD 4.x Signal Environment

In this environment, unreliability was removed by using the following enhancements -

  • Signals are blocked for the duration of a signal handler (i.e. recursive signals are not normally allowed)
  • A "signal mask" can be set to block most signals during critical regions
  • Signal handlers normally remain installed during and after signal delivery
  • A separate signal handler stack can be used if desired. Most system calls are restarted following delivery of a signal.
The interface here was still the old System V interface with the following additions -


int sigvec(int signal_number, struct sigvec *new_sigvec, struct sigvec *old_sigved);

Change or query signal handlers. Signal_number indicates which signal. New_sigvec defines the handler environment or may be null if you don't want to change the handler. If non-null, old_sigvec is filled with the current handler information.


int sigstack(char *stack);
Change the stack which will be used during signal delivery.

int siginterrupt(int signal_number, int interrupt);
Alter the SV_INTERRUPT property of a signal handler. If interrupt is zero, system calls will be restarted after signal delivery. If it is non-zero they will return EINTR.


int sigsetmask(int new_mask);

Change the set of masked/held signals. New_mask is a bit pattern describing the new set of signals. The old signal mask is returned.


int sigblock(int signal_mask);

Add a new set of signals to the current signal mask.


int sigpause(int signal_mask);

Wait for a signal using the given signal mask.

The sigvec structure defines the signal-handling semantics:


struct sigvec {
// signal handler
int (*sv_handler)(void);
// signals to mask during signal delivery
int sv_mask;
// signal delivery options
int sv_flags;
};

The sv_flags field describes the options you could use to alter signal handling. Standard options are:
SV_ONSTACK Use a special stack rather than the normal hardware stack. The stack to use must be specified using sigstack().
SV_RESETHAND Use V7-style "reset to default handler" signal handling semantics.
SV_INTERRUPT Allow system calls to be interrupted by signal delivery. Not all of these options are supported by "BSD-compatible" systems and

POSIX Environment


int sigaction(int signal_number, struct sigaction *new_handler, struct sigaction *old_handler);

Set or query the signal handling environment of a given signal.


int sigprocmask(int how, sigset_t *new_set, sigset_t *old_set);

Set or query the signal mask. If new_set is null, no change is made. If old_set is null, nothing is returned.


int sigemptyset(sigset_t *set);

Clear a signal mask.


int sigfillset(sigset_t *set);

Fill (add all possible signals to) a signal mask.


int sigaddset(sigset_t *set, int signal_number);

Add a signal to a signal mask.


int sigdelset(sigset_t *set, int signal_number);

Remove a signal from a signal mask


int sigismember(sigset_t *set, int signal_number);

See if a signal is a member of a signal mask.


int sigpending(sigset_t *set);

Return the set of signals that have been delivered but which were blocked.

The sigaction structure describes the signal handling environment:


struct sigaction {
void (*sa_handler)(int);
// new signal mask
sigset_t sa_mask;
// options
int sa_flags;
};

The same default signal handlers are used in POSIX as in V7.

Common options for sigaction() include:
SA_OLDSTYLE SA_RESETHAND V7 unreliable signal semantics.
SA_INTERRUPT Allow system calls to be interrupted by signal delivery.
SA_RESTART Allow system calls to be restarted after signal delivery.
SA_ONSTACK Use a special stack during signal handling.
SA_NOCLDSTOP Disable SIGCLD/SIGCHLD for stopped (versus terminated) processes. Only SA_NOCLDSTOP is required by the POSIX specification. Other options are often missing or have different names.

All Signals

SIGHUP 1 Exit Hangup
SIGINT 2 Exit Interrupt
SIGQUIT 3 Core Quit
SIGILL 4 Core Illegal Instruction
SIGTRAP 5 Core Trace/Breakpoint Trap
SIGABRT 6 Core Abort
SIGEMT 7 Core Emulation Trap
SIGFPE 8 Core Arithmetic Exception
SIGKILL 9 Exit Killed
SIGBUS 10 Core Bus Error
SIGSEGV 11 Core Segmentation Fault
SIGSYS 12 Core Bad System Call
SIGPIPE 13 Exit Broken Pipe
SIGALRM 14 Exit Alarm Clock
SIGTERM 15 Exit Terminated
SIGUSR1 16 Exit User Signal 1
SIGUSR2 17 Exit User Signal 2
SIGCHLD 18 Ignore Child Status
SIGPWR 19 Ignore Power Fail/Restart
SIGWINCH 20 Ignore Window Size Change
SIGURG 21 Ignore Urgent Socket Condition
SIGPOLL 22 Ignore Socket I/O Possible
SIGSTOP 23 Stop Stopped (signal)
SIGTSTP 24 Stop Stopped (user)
SIGCONT 25 Ignore Continued
SIGTTIN 26 Stop Stopped (tty input)
SIGTTOU 27 Stop Stopped (tty output)
SIGVTALRM 28 Exit Virtual Timer Expired
SIGPROF 29 Exit Profiling Timer Expired
SIGXCPU 30 Core CPU time limit exceeded
SIGXFSZ 31 Core File size limit exceeded
SIGWAITING 32 Ignore All LWPs blocked
SIGLWP 33 Ignore Virtual Interprocessor Interrupt for Threads Library
SIGAIO 34 Ignore Asynchronous I/O