▪️Functions used

You are allowed to use several functions in this project. You already know some of them like write, ft_printf (yours!), malloc, free and all the functions from your libft. However, other important functions that have never been used before will be essential to the success of this project. Let's look at them together.

signal()

sighandler_t signal(int signum, sighandler_t handler);

The signal function in C is a way to specify a function, called a signal handler, to be called when a specific signal is received by a running program. A signal is a message from the operating system to a program indicating that some event has occurred. The signal function allows you to specify a function to be called when a particular signal is received, so that you can take some action in response to the signal.

signal() example
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void signal_handler(int signum) 
{
  printf("Received SIGINT!\n", signum);
  exit(0);
}

int main() 
{
  // Set the signal handler for the SIGINT and SIGTERM signals
  // to the signal_handler function
  
  signal(SIGINT, signal_handler);
  signal(SIGTERM, signal_handler);

  while (1) {
    // Do some work here...
  }

  return 0;
}

In this example, we've set the signal handler for both the SIGINT and SIGTERM signals to the signal_handler function. The SIGINT signal is generated when the user presses CTRL+C, and the SIGTERM signal is generated when the program is terminated by the operating system (e.g. by running the kill command). When either of these signals is received, the signal_handler function will be called, and it will print a message to the console and exit the program.

This example also demonstrates a common use case for the signal function: setting a signal handler to gracefully terminate a program when a certain signal is received. By setting a signal handler for the SIGINT and SIGTERM signals, we can ensure that the program will exit cleanly when it is terminated, rather than leaving resources in an undefined state.

sigemptyset()

int sigemptyset(sigset_t *set);

The sigemptyset function is used to initialize a signal set to the empty set, which means that it does not contain any signals. Signal sets are used by some functions, such as sigaction, to define the signals to be processed. The sigemptyset function takes a pointer to a set of signals as an argument and empties this set by adding no signal to it. This function is often used in conjunction with the sigaddset function, which adds a specified signal to a signal set.

sigaddset()

int sigaddset(sigset_t *set, int signum);

This function allows to add a signal to a set of signals. The sigaddset function takes two arguments: a pointer to a set of signals and the number of the signal to add to the set.

Here is an example of using sigaddset to add the signal SIGINT to a set of signals and sigemptyset:

sigemptyset() & sigaddset() example
#include <signal.h>

int main(void)
{
    sigset_t signal_set;

    // Initialize an empty signal set
    sigemptyset(&signal_set);

    // Add SIGINT to the signal set
    sigaddset(&signal_set, SIGINT);

sigaction()

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

The sigaction function in C is used to specify the action to be taken when a specific signal is received by a process. It is defined in the signal.h header file.

The signum argument specifies the signal for which the action is being specified. The act argument is a pointer to a struct sigaction that specifies the action to be taken when the signal is received. The oldact argument is a pointer to a struct sigaction that is used to retrieve the previous action for the specified signal.

sigaction() example
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void signal_handler(int signum) {
  printf("Received signal %d\n", signum);
}

int main(void) {
  struct sigaction action;
  action.sa_handler = signal_handler;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;

  sigaction(SIGINT, &action, NULL);

  while (1) {
    // Do some work
  }

  return 0;
}

In this example, the signal_handler function is specified as the action to be taken when the SIGINT signal is received. When the signal is received, the signal_handler function will be called, which will print a message to the console. The sigemptyset function is used to initialize the signal mask, which specifies the signals that should be blocked while the signal handler is executing. The sa_flags field is set to 0, which specifies the default behavior for the signal action.

kill()

int kill(pid_t pid, int sig);

In C, the kill function is a system call that sends a signal to a process. It is defined in the signal.h header file.

The pid argument specifies the process ID of the process you want to communicate with. The sig argument specifies the signal to be sent to the process. There are various signals that can be sent, each corresponding to a different purpose. For example, the SIGKILL signal is used to terminate processes that are unresponsive or stuck in an infinite loop.

kill() example

Here is an example of using the kill function to terminate a process with the SIGKILL signal:

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

int main() {
  pid_t pid = getpid();  // get the process ID of the current process
  int result = kill(pid, SIGKILL);  // send the SIGKILL signal to the process
  if (result == 0) {
    printf("Process terminated successfully.\n");
  } else {
    perror("Error terminating process");
  }
  return 0;
}

Note that using the kill function to terminate a process should generally be avoided, as it can leave resources allocated to the process in an undefined state. Instead, it is usually better to allow the process to terminate gracefully by providing it with an opportunity to clean up and release resources before exiting.

getpid()

pid_t getpid(void);

In C, the getpid function returns the process ID of the current process. It is declared in the unistd.h header file.

What are the process ID for?

In an operating system, a process is an instance of a program that is being executed. A process ID (PID) is a unique identifier assigned to each process by the operating system when it is created.

Process IDs are useful for a variety of purposes in coding. Here are a few examples:

  1. Identifying and tracking processes: As mentioned earlier, the process ID is a unique identifier for a process, so it can be used to identify and track specific processes within the system.

  2. Sending signals to processes: The kill function in C allows you to send a signal to a process, and you can specify the process to send the signal to using the process ID. This can be useful for controlling and interacting with processes from your code.

  3. Process communication: Process IDs can be used as a way for processes to communicate with each other. For example, one process might create a new process using the fork function, and then pass the child process's ID back to the parent process so that the parent can communicate with the child.

  4. Debugging: Process IDs can be helpful in debugging, as they can be used to identify which processes are causing problems or behaving unexpectedly.

Overall, process IDs are a useful tool for managing and interacting with processes in your code.

getpid() example
#include <stdio.h>
#include <unistd.h>

int main(void) {
  pid_t pid;

  pid = getpid();
  printf("The process ID is %d\n", pid);

  return 0;
}

This program will print the process ID of the current process to the console. The process ID is a unique identifier assigned to each process by the operating system. It is used to identify and track processes within the system.

pause()

int pause(void);

pause() is a function in the C standard library that causes the calling process to sleep until a signal is received. The process remains blocked until a signal handler is executed or the signal is ignored

pause() example
#include <stdio.h>
#include <unistd.h>

int main(void) {
    printf("Entering pause...\n");
    pause();
    printf("Exiting pause.\n");
    return 0;
}

sleep()

unsigned int sleep(unsigned int seconds);

sleep() is also a function in the C standard library that causes the calling process to sleep for a specified number of seconds.

sleep() example
#include <stdio.h>
#include <unistd.h>

int main(void) {
    printf("Sleeping for 3 seconds...\n");
    sleep(3); // The program waits 3 seconds
    printf("Done sleeping.\n");
    return 0;
}

usleep()

int usleep(useconds_t usec);

usleep() is a function in the C standard library that causes the calling process to sleep for a specified number of microseconds.

usleep() example
#include <stdio.h>
#include <unistd.h>

int main(void) {
    printf("Sleeping for 500000 microseconds...\n");
    usleep(500000);
    printf("Done sleeping.\n");
    return 0;
}

exit()

void exit(int status);

exit() is a function in the C standard library that terminates the calling process immediately. It takes an integer argument that specifies the exit status of the process. A value of 0 indicates successful termination, while non-zero values indicate an error.

exit() example
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("Exiting with status 0...\n");
    exit(0);
}

And... that's it. You should be able to make some tests alone right now ;) If you don't know how to do it, see you in the next step.

Last updated