Wednesday 2 October 2013

[Operating Systems #2] ASynchronous Signals handling using signal and sigaction APIs

Please refer previous post #1: Handling child process. Since, current post is continuation of the post #1.

We already see the use of wait() function. This function call blocks the parent process, reads the exit value of child process and instructs the kernel's process manager to destroy the child process.

Wait() function is synchronous call, it will suspend/block the execution of parent process i.e. parent can't continue its operation/execution until child terminates.

Now, we will see another API "Signals" which uses Asynchronous method.
This method allows the parent process to register a function call back. This function will be executed when the child process terminates. So, the child and parent process can be executed simultaneously without any parent blocking.

Here, we have two different APIs to do this job.

  1. Signal API
  2. Sigaction API

Signal API :
=========

Signals are asynchronous calls, means these will block current execution of process and requires immediate response (registered function call be will be called automatically when the signal interrupt is received).

Once the signal is received, the process can able to perform three different tasks
  1.  Calls default signal handler
  2.  Calls its own defined signal handler
  3.  Ignores the signal
The below Sample code will describe the use of 2nd case (process calls its own defined signal handler).

For 1st case, pass second argument as SIG_DFL in signal() function call. Kernel process manager will call the default function for the signal generated.

For 3rd case (Ignore the signal), use 2nd argument as SIG_IGN in signal() function.

-------------------------------------------------------------------------------------------
Sample code:
----------------

  1. #include<unistd.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<signal.h>

  5. #define CHILD 0

  6. void sighand(int signum)
  7. {
  8. printf("I am in signal handler: %d\n", signum);
  9. }
  10. int main()
  11. {
  12. pid_t child_pid;

  13. child_pid = fork();
  14. if (child_pid == CHILD) {
  15. /* child process */
  16. printf("child: %d, parent: %d\n", getpid(), getppid());
  17. sleep(10);
  18. exit(100);
  19. } else {
  20. /* parent process */
  21. signal(SIGCHLD, sighand);
  22. printf("Successfully registered sighand for SIGCHLD: %d\n", SIGCHLD);
  23. printf("parent: %d\n", getpid());
  24. while(1) {
  25. printf("in parent\n");
  26. sleep(2);
  27. }
  28. }
  29. return 0;
  30. }

output:
=======
Successfully registered sighand for SIGCHLD: 17
parent: 2274
in parent
child: 2275, parent: 2274
in parent
in parent
in parent
in parent
in parent
I am in signal handler: 17
in parent
in parent
in parent
.
.

observation:
=========
signal is not a blocking call like wait().
Since in wait() case, parent process is blocked until child terminated.
But here in signal() case, parent process is executing continuously and received asynchronous interrupt (SIGCHLD) when child terminates. sighand() is called in response to this interrupt.
ps -Af (Before SIGCHLD interrupt received)
-----------------
rrajk      2285  2039  0 10:59 pts/0    00:00:00 ./fork4_signal
rrajk      2286  2285  0 10:59 pts/0    00:00:00 ./fork4_signal

ps -Af (After SIGCHLD received:)
----------------
rrajk      2285  2039  0 10:59 pts/0    00:00:00 ./fork4_signal
rrajk      2286  2285  0 10:59 pts/0    00:00:00 [fork4_signal] <defunct>

Since child process terminated, but parent process is still executing then child process put into defunc state.
------------------------------------------------------------------------------------------

Sigaction API:
============

Signal API is an ANSI C Standard API.
Sigaction API is a POSIX standard API.

We use sigaction APIs for changing the signal disposition in better way compared to signal.
Using sigaction, we can block/unblock required set of signals with ease of operations.

Sigaction() prototype declaration
--------------------------------------
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
  void (*sa_handler)(int);
  void (*sa_sigaction)(int, siginfo_t *, void *);
  sigset_t sa_mask;
  int sa_flags;
  void (*sa_restorer)(void);
};

sigaction is alternative signal API for changing signal disposition.
Sample code:
            struct sigaction sa;
            memset(&sa, 0, sizeof(sa));
  
          /* Install signal Handler */
            sa.sa_handler = handler;
            if (sigaction(SIGALRM, &sa, NULL) < 0)
                        printf(“Sigaction failed\n”);

When a signal is generated and it is being currently handled, another occurrence of the same signal shall be queued (queue size = 1) until handler returns. If more than 1 signal is generated in this case, they will be lost.

But, occurrence of Real Time signals are never lost, they will be queued.

sa.sa_flags:
==========
           sa.sa_flags = SA_NODEFER;
           Signals are not queued, they directly send to the registered process. No delay in delivery. No queue also.

sa.sa_mask:
==========
If one signal is in its handler, and if another signal is generated then 1st signal is terminated immediately and 2nd signal handler will start its execution.
Ex: CTRL+C SIGTERM and CTRL+\ SIGQUIT

Sigaction provide a mechanism to block this 2nd signal until 1st signal completed its handler function.
Sample code:
            sigset_t sigmask;
            sigemptyset(&sigmask);

            sigaddset(&sigmask, SIGQUIT); /* block SIGQUIT if a signal is already in its handler */
            sigaddset(&sigmask, SIGTERM);

            sa.sa_handler = handler;
            sa.sa_mask = sigmask;

            if (sigaction(SIGINT, &sa, NULL) < 0)
                        printf(“sigaction is failed\n”);

Sigprocmask:
=============
Applications can also block/unblock signal delivery while executing the primary functionality in the main thread.
            Int sigprocmask(int how, const sigset_t *set, sigset_t *old_set);

how: SIG_BLOCK or SIG_UNBLOCK

Sample code:
            struct sigaction sa;
            sigset_t set;

            memset(&sa, 0, sizeof(sa));
            sigemptyset(&set);

            sigaddset(&set, SIGQUIT);
            sigaddset(&set, SIGTERM);

            /* ovrride signal mask set */
            sigprocmask(SIG_BLOCK | SIG_SETMASK, &set, NULL);

           /* Append to signal mask list */

            sigprocmask(SIG_BLOCK, &set, NULL); 

Examples will be posted in next post [Operating Systems #3].

No comments:

Post a Comment

You might also like

Related Posts Plugin for WordPress, Blogger...