How to Kill a Child Process in C

Jinku Hu Feb 02, 2024
  1. Use the SIGKILL Signal to Terminate a Child Process in C
  2. Use the SIGTERM Signal to Terminate a Child Process in C
How to Kill a Child Process in C

This article will demonstrate multiple methods about how to kill a child process in C.

Use the SIGKILL Signal to Terminate a Child Process in C

There are multiple signals designed to terminate a process upon its delivery, but sending the SIGKILL signal is the most powerful and sure method to do it. Generally, a program can register special functions called signal handlers that are invoked automatically once the corresponding signal is delivered to the program. The user implements the handler function code that usually conducts some cleanup work for the program. Apart from function handlers, there can be default actions on delivered signals like blocking and ignoring. Although, the SIGKILL signal can’t be ignored, blocked, or handled by the given function. Thus, this method should be the last resort when trying to terminate a process.

The SIGKILL signal can be sent with a kill system call. Note though, the SIGTERM handler registered in the following code sample can’t catch the delivered SIGKILL, and it immediately kills the given child process.

The following program spawns a child process and registers the SIGTERM handler in it. Then, the child process executes an infinite loop unless the signal is delivered, which flips the while expression variable. Consequently, the SIGKILL signal delivered by the parent does not invoke the handler and terminates the child process instantly.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

volatile sig_atomic_t shutdown_flag = 1;

void cleanupRoutine(int signal_number) { shutdown_flag = 0; }

int main(void) {
  int wstatus;

  pid_t c_pid = fork();
  if (c_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (c_pid == 0) {
    printf("printed from child process - %d\n", getpid());

    int count = 0;

    struct sigaction sigterm_action;
    memset(&sigterm_action, 0, sizeof(sigterm_action));
    sigterm_action.sa_handler = &cleanupRoutine;
    sigterm_action.sa_flags = 0;

    // Mask other signals from interrupting SIGTERM handler
    if (sigfillset(&sigterm_action.sa_mask) != 0) {
      perror("sigfillset");
      exit(EXIT_FAILURE);
    }
    // Register SIGTERM handler
    if (sigaction(SIGTERM, &sigterm_action, NULL) != 0) {
      perror("sigaction SIGTERM");
      exit(EXIT_FAILURE);
    }

    while (shutdown_flag) {
      count += 1;
    }
    printf("count = %d\n", count);

    exit(EXIT_SUCCESS);
  } else {
    printf("printed from parent process - %d\n", getpid());
    int ret;

    sleep(5);

    ret = kill(c_pid, SIGKILL);
    if (ret == -1) {
      perror("kill");
      exit(EXIT_FAILURE);
    }

    if (waitpid(c_pid, &wstatus, WUNTRACED | WCONTINUED) == -1) {
      perror("waitpid");
      exit(EXIT_FAILURE);
    }
  }

  exit(EXIT_SUCCESS);
}

Use the SIGTERM Signal to Terminate a Child Process in C

Alternatively, a child process can be terminated using SIGTERM signal, which can be handled by the program. The next code sample repeats the previous program implementation except that it substitutes the SIGKILL signal with SIGTERM. The sigfillset function is used to prevent other signals from interrupting the registered handler function execution. The handler code modifies the global sig_atomic_t type variable that stops the while loop in the child process and prints the count variable’s value. Mind though, it’s always better to use the sigaction call above the signal function when registering the handlers, as the latter is not specified in detail by the POSIX standard.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

volatile sig_atomic_t shutdown_flag = 1;

void cleanupRoutine(int signal_number) { shutdown_flag = 0; }

int main(void) {
  int wstatus;

  pid_t c_pid = fork();
  if (c_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (c_pid == 0) {
    printf("printed from child process - %d\n", getpid());

    int count = 0;
    struct sigaction sigterm_action;
    memset(&sigterm_action, 0, sizeof(sigterm_action));
    sigterm_action.sa_handler = &cleanupRoutine;
    sigterm_action.sa_flags = 0;

    // Mask other signals from interrupting SIGTERM handler
    if (sigfillset(&sigterm_action.sa_mask) != 0) {
      perror("sigfillset");
      exit(EXIT_FAILURE);
    }
    // Register SIGTERM handler
    if (sigaction(SIGTERM, &sigterm_action, NULL) != 0) {
      perror("sigaction SIGTERM");
      exit(EXIT_FAILURE);
    }

    while (shutdown_flag) {
      count += 1;
    }
    printf("count = %d\n", count);

    exit(EXIT_SUCCESS);
  } else {
    printf("printed from parent process - %d\n", getpid());
    int ret;

    sleep(5);

    ret = kill(c_pid, SIGTERM);
    if (ret == -1) {
      perror("kill");
      exit(EXIT_FAILURE);
    }

    if (waitpid(c_pid, &wstatus, WUNTRACED | WCONTINUED) == -1) {
      perror("waitpid");
      exit(EXIT_FAILURE);
    }
  }

  exit(EXIT_SUCCESS);
}
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C Process