IPC Interprocess Communication: Signals

Interprocess communication

IPC (Inter-Process Communication) inter-process communication provides various methods of inter-process communication. There are several methods in Linux C programming
(1) Half-duplex Unix pipes
(2) FIFOs (named pipes)
(3) Message queue
(4) Semaphore
(5) Shared memory
(6) Network Socket

signal

What is a signal? The signal is to provide a method for the program to handle asynchronous events, which is realized by using software interrupts. Signals cannot be customized, all signals are predefined by the system. Processes communicate through signals.
? Who generates the signal?
(1) The shell terminal generates a corresponding signal according to the current error (segment fault, illegal instruction, etc.) Ctrl + c
For example: (Same as communication, there needs to be a sender and a receiver)
Socket communication or pipeline communication, if the reading end has been closed, then perform a write operation (or send data),
will cause the process performing the write operation to receive a SIGPIPE signal (indicating a broken pipe)
The default behavior of this signal: terminate the process.
(2) In the shell terminal, use the kill or killall command to generate a signal
(3) Processes communicate through signals

main1.cc

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

void myhandle(int sig) {<!-- -->//signal code
printf("Catch a signal : %d\\
", sig);
}
int main(void) {<!-- -->
//If this signal occurs, call this signal for processing
signal(SIGINT, myhandle);//Set signal capture function //SIGINT signal Ctrl + C
while (1) {<!-- -->
sleep(1);
printf("sleep 1 sencond.");
}
return 0;
}

ps -ef | grep single.out
kill -9 59579(id) can kill all processes

./a.out &
kill -HUP 13733 /* Send SIGHUP to the process with PID 13733 */
3) In the program code, call the kill system call to generate a signal
? What are the signals
———————————————–
Signal name Description
———————————————–
SIGABORT Process terminated abnormally
SIGALRM timeout alarm
SIGFPE floating-point arithmetic exception
SIGHUP connection hang up
SIGILL illegal instruction
SIGINT terminal interrupt (Ctrl + C will generate this signal)
SIGKILL * Terminates the process
SIGPIPE writes data to a pipe that has no reading process
SIGQUIT terminal exit (Ctrl + \ will generate this signal)
SIGSEGV invalid memory segment access
SIGTERM Terminate
SIGUSR1 * user-defined signal 1
SIGUSR2 * user-defined signal 2
————————————–> If the above signal is not caught, the process will receive it Will end later!
SIGCHLD Child process stopped or exited
SIGCONT * resumes execution of a suspended process
SIGSTOP * Stop execution (i.e. “pause”)
SIGTSTP interrupt pending
SIGTTIN background process attempted a read operation
SIGTTOU background process attempted to write
———————————————–
? Signal processing
ignore this signal
Capture the signal and specify the signal processing function for processing
Perform system default actions, most of which are to terminate the process
? Signal capture
Signal capture refers to specifying to execute a specified function after receiving a certain signal.
Note: SIGKILL and SIGSTOP cannot be caught, i.e. the actions in response to these two signals cannot be changed.
Signal installation
1) use signal
Usage: man 2 signal
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
Note: The return type of signal, and its second parameter, are both function pointer types

 The parameter 2 of signal can go to the following special values: It can also be a custom function
    SIG_IGN ignore signal
    SIG_DFL restore default behavior
    Example: main2.c changes the behavior of the terminal interrupt signal
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void myhandle(int sig)
{<!-- -->
printf("Catch a signal : %d\\
", sig);
}

int main(void)
{<!-- -->
signal(SIGINT, myhandle);
while (1) {<!-- -->
sleep(1);
}
return 0;
}

The process cannot be terminated at this point!
It can only be terminated by sending another signal to the process through other terminals
#ps ax | grep ./a.out //query the process number
#kill -HUP process number
Restore default behavior of signals main3.c

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

void myhandle(int sig)
{<!-- -->
static int cnt = 0;
printf("Catch a signal : %d\\
", sig);

signal(SIGINT, SIG_DFL); //same as signal(sig, SIG_DFL);
}

int main(void)
{<!-- -->
signal(SIGINT, myhandle);

while (1) {<!-- -->
sleep(1);
}
return 0;
}

When using SIG_DFL, it can be restored only when using SIG_DFL immediately after calling the custom behavior for the first time. If it is captured multiple times in a row, it is uncertain. SIG_IGN ignore
2) Use sigaction (strongly recommended for project actual combat)
The difference between sigaction and signal: sigaction is more “robust” than signal, it is recommended to use sigaction
Usage: man 2 sigaction
structure struct sigaction
struct sigaction {
void (sa_handler)(int); // signal response function /
sigset_t sa_mask; //
mask signal set /
int sa_flags; /
When sa_flags contains SA_RESETHAND, after receiving the signal and calling the specified signal processing function to execute, reset the response behavior of the signal to the default behavior SIG_DFL */

}
Replenish:
When sa_mask contains a certain signal A, during the execution of the signal processing function, if the signal A occurs,
Then block the signal A (that is, do not respond to the signal temporarily) until the execution of the signal processing function ends.
That is, after the signal processing function is executed, respond to the signal A
Example: main4.c

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

void myhandle(int sig)
{<!-- -->
printf("Catch a signal : %d\\
", sig);
}

int main(void)
{<!-- -->
struct sigaction act;

act.sa_handler = myhandle;
sigemptyset( &act.sa_mask);
act.sa_flags = 0;

sigaction(SIGINT, & act, 0);

while (1) {<!-- -->

}
return 0;
}

Change the response action with sigaction
main5.c rewrite main2.c with sigaction

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

void myhandle(int sig)
{<!-- -->
printf("Catch a signal : %d\\
", sig);
}

int main(void)
{<!-- -->
struct sigaction act;

act.sa_handler = myhandle;
sigemptyset( &act.sa_mask);
//act.sa_flags = 0;
act.sa_flags = SA_RESETHAND;

sigaction(SIGINT, & act, 0);

while (1) {<!-- -->

}

return 0;
}

Restore default action with sigaction
Rewrite main3.c with sigaction
? Signal sending
How the signal is sent:
Use shortcut keys to generate signals in the shell terminal
Use kill, killall command.
Use kill function and alarm function
1) Use the kill function
Send the specified signal to the specified process
Usage: man 2 kill
Notice:
Sending a signal to a specified process requires “permissions”:
A normal user’s process can only send signals to other processes of that user
The root user can send signals to all user processes
kill failed
Return -1 on failure
Reason for failure:
Insufficient authority
signal does not exist
The specified process does not exist
Example: main6.c creates a child process, the child process outputs the string “child process work!” every second, the parent process waits for user input, if the user presses the character A, it sends a signal SIGUSR1 to the child process, the child process The output string is changed to uppercase; if the user presses character a, signal SIGUSR2 is sent to the child process, and the output string of the child process is changed to lowercase.

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

int workflag = 0;

void work_up_handle(int sig)
{<!-- -->
workflag = 1;
}

void work_down_handle(int sig)
{<!-- -->
workflag = 0;
}

int main(void)
{<!-- -->
pid_t pd;
char c;

pd = fork();
if (pd == -1) {<!-- -->
printf("fork error!\\
");
exit(1);
}
else if (pd == 0) {<!-- -->
char* msg;
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = work_up_handle;
sigemptyset( &act.sa_mask);
sigaction(SIGUSR1, & act, 0);

act.sa_handler = work_down_handle;
sigaction(SIGUSR2, & act, 0);

while (1) {<!-- -->
if (!workflag) {<!-- -->
msg = "child process work!";
}
else {<!-- -->
msg = "CHILD PROCESS WORK!";
}
printf("%s\\
", msg);
sleep(1);
}
}
else {<!-- -->
while (1) {<!-- -->
c = getchar();
if (c == 'A') {<!-- -->
kill(pd, SIGUSR1);
}
else if (c == 'a') {<!-- -->
kill(pd, SIGUSR2);
}
}
}

return 0;
}

Example: main7.c “alarm clock”, create a child process, the child process sends a SIGALR to the parent process after 5 seconds, after the parent process receives the SIGALRM signal, “alarm” (simulated by printing)

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

int wakeflag = 0;

void wake_handle(int sig)
{<!-- -->
wakeflag = 1;
}

int main(void)
{<!-- -->
pid_t pd;
char c;

pd = fork();
if (pd == -1) {<!-- -->
printf("fork error!\\
");
exit(1);
}
else if (pd == 0) {<!-- -->
sleep(5);
kill(getppid(), SIGALRM);
}
else {<!-- -->
struct sigaction act;
act.sa_handler = wake_handle;
act.sa_flags = 0;
sigemptyset( &act.sa_mask);

sigaction(SIGALRM, & act, 0);

pause(); // suspend the process until any signal is received

if (wakeflag) {<!-- -->
printf("Alarm clock work!!!\\
");
}
}
return 0;
}

2) Use the alarm function
Function: Send a SIGALRM signal to the process itself within the specified time.
Usage: man 2 alarm
Note: The unit of time is “seconds”
The actual alarm time is a little bit longer than the specified time.
If the parameter is 0, cancel the alarm clock that has been set.
If the alarm clock time has not yet arrived, call alarm again, the alarm clock will be retimed
Each process can use at most one alarm.
return value:
Failure: return -1
Success: returns the remaining time (in seconds) of the last alarm
Example: “alarm”
main8.c rewrites main7.c with alarm

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

int wakeflag = 0;

void wake_handle(int sig)
{<!-- -->
wakeflag = 1;
}

int main(void)
{<!-- -->
int ret;

struct sigaction act;
act.sa_flags = 0;
act.sa_handler = wake_handle;
sigemptyset( &act.sa_mask);
sigaction(SIGALRM, & act, 0);

printf("time =%ld\\
", time((time_t*)0));

ret = alarm(5);
if (ret == -1) {<!-- -->
printf("alarm error!\\
");
exit(1);
}

//Suspend the current process until any signal is received
pause();

if (wakeflag) {<!-- -->
printf("wake up, time =%ld\\
", time((time_t*)0));
}
return 0;
}
  1. Use raise kill to send a signal to others
    Send a signal to the process itself.
    Prototype: int raise (int sig)
    ? Send multiple signals
    While a process is executing the operation function corresponding to a certain signal (the installation function of the signal), if at this time, the process receives the same signal (signal with the same signal value) multiple times,
    Then: If the signal is an unreliable signal (<32), it can only respond once more.
    If the signal is a reliable signal (>32), it can respond many times more (without missing it). However, they all have to wait for the response function to be executed before responding next time.

    While a process is executing the operation function corresponding to a certain signal (the installation function of the signal), if at this time, the process receives another signal (a signal with a different signal value), then:
    If the signal is included in the sa_mask (signal mask set) of the current signal’s signaction, the signal will not be processed immediately. The signal processing function is not executed until the current signal processing function is executed.
    otherwise:
    Then immediately interrupt the current execution process (if it is in sleep, such as sleep, it will be awakened immediately) and execute this new signal response. After the new response is executed, return to the original signal processing function to continue execution.
    Example: main4_2.c

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

void myhandle(int sig)
{<!-- -->
printf("Catch a signal : %d\\
", sig);
int i;
for (i = 0; i < 10; i ++ ) {<!-- -->
sleep(1);
}
printf("Catch end.%d\\
", sig);
}

int main(void)
{<!-- -->
struct sigaction act, act2;

act.sa_handler = myhandle;
sigemptyset( &act.sa_mask);
sigaddset( & act.sa_mask, SIGUSR1);
act.sa_flags = 0;

sigaction(SIGINT, & act, 0);

act2.sa_handler = myhandle;
sigemptyset( &act2.sa_mask);
act2.sa_flags = 0;
sigaction(SIGUSR1, & act, 0);

while (1) {<!-- -->

}
return 0;
}
  1. signal set
    1). What is a signal set
    Signal set, represented by sigset_t type, is essentially an unsigned long integer.
    Used to represent a collection containing multiple signals.
    2). Basic operation of signal set
    sigemptyset empty signal set
    sigfillset fills all defined signals into the specified signal set
    sigdelset deletes the specified signal from the specified signal set
    sigaddset Add the specified signal from the specified signal set
    sigismember Determines whether the specified signal is in the specified signal set
    If yes, return 1
    If not, return 0
    Invalid signal, return -1
    See man for detailed usage
    3) The “signal mask word” of the process
    The “signal mask” of a process is a set of signals
    When sending a signal to the target process, if the signal is in the signal mask word of the target process,
    Then the target process will not catch the signal, that is, the processing function of the signal will not be executed.
    When the signal mask of the process no longer contains the signal, it will capture the already received signal (execute the corresponding function)
    Modify the “signal mask word” of the process
    use sigprocmask
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    parameter:
    how:
    SIG_BLOCK Add the signal in the parameter set to the signal mask word
    SIG_UNBLOCK deletes the signal in the parameter set from the signal mask word
    SIG_SETMASK Set the signal in the parameter set as a signal mask word
    oldset
    return the original signal mask word
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void myhandle(int sig)
{<!-- -->
printf("Catch a signal : %d\\
", sig);
printf("Catch end.%d\\
", sig);
}

int main(void)
{<!-- -->
struct sigaction act, act2;

act.sa_handler = myhandle;
sigemptyset( &act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, & act, 0);

sigset_t proc_sig_msk, old_mask;
sigemptyset( & proc_sig_msk);
sigaddset( & proc_sig_msk, SIGINT);

sigprocmask(SIG_BLOCK, &proc_sig_msk, &old_mask);
sleep(5);
printf("had delete SIGINT from process sig mask\\
");
sigprocmask(SIG_UNBLOCK, &proc_sig_msk, &old_mask);

while (1) {<!-- -->

}
return 0;
}
  1. get unhandled signal
    When signals occur in a process’s signal mask word, these signals will not be responded to by the process,
    These signals that have occurred but have not been processed can be obtained through the sigpending function

    Usage: man sigpending
    Return value: return 0 if successful
    Return -1 on failure

  2. blocking wait signal
    (1) pause
    Block the process until any signal occurs
    (2) sigsuspend
    Set the signal mask with the specified parameters, and then wait for the signal to occur while blocking.
    That is, only wait for signals outside the signal mask word