Ch20.11 – raise() & killpg()

 

Ch20.11 – raise() & killpg()
Linux System Programming Β· EmbeddedPathashala
πŸ“‘ Topic 11 of 19
🎯 Beginner–Intermediate
πŸ’» 3 Code Examples
❓ Interview Q&A
πŸ”‘ Key Terms
raise() killpg() Self-signal Process Group pthread_kill() Immediate Delivery
raise() β€” Sending a Signal to Yourself

raise(sig) is the simplest way for a process to send a signal to itself. In a single-threaded program it is exactly equivalent to kill(getpid(), sig).

In a multithreaded program there is a subtle difference: raise(sig) delivers the signal to the specific thread that called it, while kill(getpid(), sig) delivers to any thread in the process.

One important behaviour: when a process sends a signal to itself using raise(), the signal is delivered immediately β€” before raise() returns.

πŸ“ raise() vs kill(getpid(), sig)
Feature raise(sig) kill(getpid(), sig)
Single-threaded Identical behaviour Identical behaviour
Multithreaded Signal delivered to calling thread Signal delivered to any thread
C standard? Yes (C89) POSIX only (needs process IDs)
Delivery timing Immediate (before return) Immediate (before return)
πŸ’» Code Example 1 – raise() to Signal Yourself
/* raise() β€” send a signal to the calling process
   Compile: gcc -o raise_demo raise_demo.c
   Run: ./raise_demo */

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

static void handler(int sig)
{
    write(1, "Handler called via raise()\n", 27);
}

int main(void)
{
    /* Install handler for SIGUSR1 */
    signal(SIGUSR1, handler);

    printf("About to call raise(SIGUSR1)...\n");

    raise(SIGUSR1);   /* signal delivered before this returns */

    /* This line runs AFTER the handler returns */
    printf("Back in main() after raise(). Handler already ran.\n");

    /* raise(SIGTERM) β€” no handler, default = terminate */
    printf("Now raising SIGTERM β€” process will terminate.\n");
    raise(SIGTERM);

    /* Never reached */
    printf("This never prints.\n");
    return 0;
}
πŸ“ killpg() β€” Send Signal to Process Group

killpg(pgrp, sig) sends a signal to all processes in the process group with ID pgrp. It is exactly equivalent to kill(-pgrp, sig).

#include <signal.h>
int killpg(pid_t pgrp, int sig);
/* Returns 0 on success, -1 on error */

If pgrp is 0, the signal is sent to all processes in the caller’s own process group.

πŸ’» Code Example 2 – killpg() to Signal a Process Group
/* killpg() β€” signal a process group
   Compile: gcc -o killpg_demo killpg_demo.c
   Run: ./killpg_demo */

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int main(void)
{
    pid_t my_pgid = getpgrp();

    printf("My PID=%d, PGID=%d\n", getpid(), (int)my_pgid);
    printf("Sending SIGUSR1 to my process group...\n");

    /* This is the same as kill(-my_pgid, SIGUSR1) */
    if (killpg(my_pgid, SIGUSR1) == -1)
        perror("killpg");
    else
        printf("SIGUSR1 sent to process group %d\n", (int)my_pgid);

    return 0;
}
πŸ’» Code Example 3 – raise() for Controlled Self-Abort with SIGABRT
/* Use raise(SIGABRT) to produce a core dump on assertion failure
   Compile: gcc -o assert_demo assert_demo.c
   Run: ./assert_demo */

#include <stdio.h>
#include <signal.h>

/* Custom assertion macro using raise() */
#define MY_ASSERT(expr)                              \
    do {                                             \
        if (!(expr)) {                               \
            fprintf(stderr, "Assertion failed: %s " \
                    "at line %d\n", #expr, __LINE__);\
            raise(SIGABRT);  /* produces core dump */\
        }                                            \
    } while (0)

int main(void)
{
    int x = 5;
    int y = 0;

    MY_ASSERT(x > 0);    /* passes */
    printf("First assertion passed.\n");

    MY_ASSERT(y != 0);   /* fails β€” triggers SIGABRT β†’ core dump */
    printf("This never prints.\n");

    return 0;
}
Note: raise(SIGABRT) produces a core dump (same as calling abort()). This is useful for debugging β€” you can inspect the core file with gdb to see the stack trace at the point of failure.
❓ Interview Questions
Q1. What is the difference between raise(sig) and kill(getpid(), sig)?
In single-threaded programs they are identical. In multithreaded programs: raise(sig) delivers the signal to the calling thread specifically (using pthread_kill internally), while kill(getpid(), sig) sends it to the process and any thread may handle it.
Q2. When is the signal delivered relative to the raise() call returning?
The signal is delivered immediately β€” before raise() returns to the caller. So if you have a handler installed, it runs and returns before the line after raise() executes.
Q3. What does killpg(0, sig) do?
It sends the signal to all processes in the caller’s own process group (pgrp=0 is interpreted as the caller’s PGID). This is equivalent to kill(0, sig).
Q4. Why does the C standard include raise() but not kill()?
The C standard avoids operating system concepts like process IDs. raise() only references the calling process itself, so it can be specified without needing to define PIDs, making it portable to non-POSIX platforms.
Q5. Give a practical use case for raise().
A custom assertion failure: when a critical invariant is violated, the program calls raise(SIGABRT) to produce a core dump immediately, enabling post-mortem debugging of the exact state at the time of failure.
Next Topic β†’

strsignal(), psignal() and sys_siglist β€” Printing human-readable signal descriptions

Next: strsignal() & psignal() β†’ ← Previous

Leave a Reply

Your email address will not be published. Required fields are marked *