The signals are an IPC method and they allow interaction between two or more running processes. This mechanism consists of a sending process and one or more receiving processes. The sender sends a predefined signal consisting of an integer between 0 and 64 to the PID of the receiver. Once received, the receiver interrupts its task, performs the action, and resumes the execution at the point where it was interrupted.
In this Linuxhint article, you’ll learn how to use the kill() function to send a signal to one or more processes. We’ll look at a theoretical part that shows the syntax of the function and describes how it works, its input and output arguments, the data type used, and the method called. Then, we will apply what we learned in a practical example that shows how to insert the necessary headers, declare the variables, query the PID of a process, and send signals to it using the kill() function in the C programming for Linux.
Syntax of the Kill() Function:
Description of the Kill() Function in the C Language
The kill() function sends a signal to a process, thread, or group of threads that runs on the system. This function sends the signal that is specified in sig to the process whose identifier is specified in the “pid” input argument. The sign of the value that is contained in the “pid” argument determines whether the signal is sent to a process or a group of threads by following these rules:
1) If the value of PID has a positive sign, the signal is sent to the process that is specified by its PID.
2) If the value of PID has a negative sign less than -1, the signal is sent to all processes of the process group that is specified by PID.
3) If the value in PID is 0, the signal is sent to all processes in the group of the sending process.
If kill() succeeds, 0 is returned. If an error occurs, -1 is returned. In this case, the error can be identified by retrieving its code from the “errno” global variable. In the following discussion, a special section will explain how to detect and identify the errors that may occur when using this function.
The kill() function is part of the signal handling system call function set and is defined in the “signal.h” header. To use it, we must include this file in the compilation as follows:
How to Send a Signal with the Kill() Function
In this example, we create a very simple console application that uses the kill() function to send a signal to any process in the system.
To create this application, we take an empty “.c” file and include the “signal.h”, “stdio.h”, and “unistd.h” headers. Then, we open an empty main() function and define the “sig” and “pid” integers. Both variables are the input arguments for the kill() function.
The scanf() function retrieves the signal number and PID that we enter via the console and stores them in its integer format variable. Once this data is entered, the program calls the kill() function when the enter key is pressed, passing these two variables as input arguments.
We wrap all of this in a “while” infinite loop and have a console application that sends each signal to a specific process. The following is the code for this example calling method of kill():
To test this application, we need a receiver. We create a simple process consisting of an “infinite” while loop that prints its PID to the command console every 2 seconds along with the “Seconds in process:” message followed by the elapsed seconds of the process. Let’s look at the code for the receiving process:
To send and receive the signal, the processes of the sender and the receiver must be running in the system. Therefore, we open two command consoles, compile and run the two codes, each in a different interpreter.
Once the two processes are running, the receiver displays its PID on the screen of console 1, while the transmitter waits for us to enter the signal number and the receiver’s PID on console 2.
The following image shows the result of sending signal 9 to the PID of the receiving process which, in this case, is 9126. As we can see, the receiving process is stopped with the SIGKILL signal:
How to Detect and Identify the Errors when Using the Kill() Function
The kill() function is a system function that interacts with two or more processes. This can cause errors at runtime either because of insufficient permissions, PIDs, processes that don’t exist, etc.
The most practical way to detect the errors when using this function is to use an “if” condition in its call. If the function returns the -1 error condition, the program enters the “if” condition.
When an error is detected, it can be identified by retrieving its code from the “errno” global variable. To do this, we need to include the “errno.h” header in our code which defines the “errno” variable and the definitions of each error, as well as their numerical representation. Next, we look at the definition of the three possible errors that can be raised by the kill() function.
Error | errno value | Description |
EPERM | 1 | Not have permission |
ESRCH | 3 | No such process |
EINVAL | 22 | Invalid argument or signal |
The simplest way to identify an error is to set a switch condition that specifies the value of the “errno” variable as a jump condition, and a definition of the possible error in each case. In this way, the program goes to the “if” condition and then to the switch where it jumps to the case corresponding to the constant of the error that occurred.
In this way, possible errors that occur at runtime can be identified. Next, we replace the call to the kill() function from the previous example with this code.
#include <errno.h>
#include <stdio.h>
#include <signal.h>
void main()
{
int sig;
int id;
printf("Enter PID\n");
scanf ("%i", &id );
printf("Enter signal\n");
scanf ("%i", &sig );
if ( kill (id, sig )==-1)
{
switch (errno){
case EPERM:
printf("Error 1ː Not have permission\n");
break;
case 3:
printf("Error 3ː No such process \n");
break;
case EINVAL:
printf("Error 22ː Invalid argument or signal\n");
break;
}
}
}
Next, let’s look at the compilation and execution of this code. We can see that the kill() function throws an error when it tries to send a signal to a non-existent PID, so the program gets into the switch via the “if” condition. Inside the switch, the program jumps to the case corresponding to the value of the “errno” variable and displays the specific error message which, in this case, is ISRCH or the integer 3 which are the same thing.
Conclusion
In this Linuxhint article, we showed you how to use the kill() function to send signals between the Linux processes. We looked at the syntax of this function and described how it works, the call method, and the data types that its input and output arguments handle. We also showed you, with a practical example, how to declare the necessary variables and call the kill() function to send a signal to a process and see its effects in real time. In order for you to fully master this function, we explained in a section how to detect and identify the errors that can occur when using this function.