Categories: Programming Tags: Tips
Linux/Unix Programming Manual -21(Signal Processor Functions)
Signal processor function design
- The signal handler function sets the global flag variable and exits. The main program periodically checks the flag and takes action once it is set
- Signal handler functions perform some type of cleanup and then terminate the process or use a non-local jump to unpack the stack and return control to the main program location
- Reentrant:
- Multiple threads in the same process can safely call a function simultaneously (signal handlers can interrupt the execution of the program at any point in time, thus forming two independent but not concurrent execution threads in a process).
- Asynchronous signal security:
- If a function is reentrant or the signal handler cannot break it
- An unsafe function is unsafe only if the signal handler interrupts its execution and the handler itself calls the function
Two strategies for implementation
- Ensure that the signal handler function code itself is reentrant and only calls asynchronous signal-safe functions
- Blocks signal transmission when the main program executes unsafe functions or processes global data structures that may be updated by signal handler functions
Global variables and SIG_atomic_t
volatile
Avoid registers that optimize global variablessig_atomic_t
Ensure read and write atomicity- Global variables shared by the main program and signal handler functions are declared as follows
volatile sig_atomic flag
The method by which a signal processor function terminates
- Call _exit(), not exit()(clears I/O buffers unsafe)
- Call kill() to kill the process
- Signal handler functions perform non-local jumps:
- slightly
- The call abort() terminates the process and produces a core dump
- slightly
System call interrupts and restarts
The system call generates an EINTR error when the signal handler function interrupts a blocking system call
Linux/Unix Programming Manual -22(Advanced Features of Signals)
Core dump file
- A file containing the memory image when the process terminates (default process working directory /core)
- The file name is /proc/sys/kernel/core_pattern
Signal special case
SIGKILL
andSIGSTOP
The default behavior of thesignal()
andsigaction()
Always return an error when changing; It also cannot be blockedSIGCONT
If a process is stopped,SIGCONT
Always make it reply; Process receivedSIGCONT
The stop signal of the waiting state will be discarded. Otherwise, the waiting state will be discarded when the stop signal is receivedSIGCONT
discarded
Sleep status of interruptible and uninterruptible processes
The kernel often requires process hibernation, and there are two kinds of hibernation
- TASK_INTERRUPTIBLE: Waits for an event (terminal input, etc.) to wake up the process. Ps STAT :S
- TASK_UNINTERRUPTIBLE: The system does not send a signal to the process until it exits the state. Ps STAT:D
- This state is usually transient, but if a hardware failure or other problem is caused by (
SIGKILL
.SIGSTOP
) does not terminate a pending process, only by restarting it- TASK_KILLABLE: similar to the former, but aroused by the kill process signal
The timing and order of signal transmission
- Synchronization signals (signals generated by the process itself, calling kill(), raise(), and so on) are passed immediately
- The asynchronous signal occurs when the process next switches from kernel to user mode
- The process gets called again after the previous timeout
- System call complete
If multiple waiting signals are unblocked by sigprocmask(). These signals are immediately transmitted to the process
- In Linux, it is usually passed in ascending order by signal number
- At the same time, if the scheduler function causes a switch between kernel mode and user mode, it interrupts the function and calls the next signal processor function
Real-time signal
- Signal range extension
- Queued management. If multiple instances of a real-time signal are sent to a process, the signal will be transmitted multiple times
- Adjoint data can be specified for the signal
- Transmission sequence is guaranteed, waiting for recovery (signal number is small plus time to transmit earlier)
#define _POSX_C_SOURCE 199309
#include<signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
// Have the same permissions as kill(), but pid cannot be negative
union sigval{
int sival_int;
void *sival_ptr;
}
Copy the code
Wait for signal using mask (atomic operation)
#include<signal.h>
int sigsuspend(const sigset_t *mask);
/ / sigsuspend (& mask) is equal to
sigprocmask(SIG_SETMASK, &mask, &prevMask);
pause();
sigprocmask(SIG_SETMASK, &prevMask, NULL);
Copy the code
Call, capture, file descriptor to get signals omitted
Signal used as IPC
More restrictive
- The nature of signal asynchrony, reentrancy, race conditions, global variable Settings
- Standard signals cannot be queued, and the number of queued real-time signals is limited
- Carrying limited information
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
void sig_handler(int signum)
{
printf("in handler\n");
sleep(1);
printf("handler return\n");
}
int main(int argc, char **argv)
{
char buf[100];
int ret;
struct sigaction action.old_action;
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
Version 2: SA_RESTART attribute is set */
//action.sa_flags |= SA_RESTART;
sigaction(SIGINT, NULL, &old_action);
if(old_action.sa_handler ! = SIG_IGN) { sigaction(SIGINT, &action,NULL);
}
bzero(buf, 100);
ret = read(0, buf, 100);
if (ret == - 1) {
perror("read");
}
printf("read %d bytes:\n", ret);
printf("%s\n", buf);
return 0;
}
// CTR +c: ret=-1
Copy the code
Linux/Unix Programming Manual -23(Timer and Sleep)
Interval timer
#include<sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
// 0 success, -1 on error
int getitimer(int which, struct itimerval *curr_value);
// The curr_value value is consistent with the old_value value
struct itimerval{
struct timeval it_interval;
struct timeval it_value;
};
struct timeval{
time_t tv_sec;
suseconds_t tv_usec;
}
Copy the code
Which values
- ITIMER_REAL: indicates the timer for counting down the real time. SIGALARM signals are generated when the timer expires and sent to the process
- ITIMER_VIRTUAL: indicates the CPU time countdown timer in user mode. SIGVTALRM is generated when the timer expires
- ITIMER_PROF: rewind timer for the process time (the sum of user-mode and kernel-mode CPU time). SIGPROF is generated when it expires
New_value values
- It_value specifies the delay time
- It_interval If the two fields are 0, the timer is a one-time timer. Otherwise, after each timer expires, the timer will reset the specified interval and expire again
- If the IT_value field of new_value is 0 when setitimer is invoked, the existing timer is masked
SUSv4 deprecated getitimer() and setitimer()
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
// One-time timer, return the remaining time, alarm(0) mask all timers, depending on the operating system implementation whether to share the mask with setitimer
Copy the code
dormancy
- Can be combined by timer function
sigsuspend
implementationsleep
include<unistd.h>
unsigned int sleep(unsigned int seconds);
// Returns 0 if terminated, or the remaining seconds if terminated
Copy the code
POSIX clock. POSIX APIS are generally recommended
slightly
Linux/Unix Programming Manual -24(Process Creation)
- The system calls
fork()
: the child gets a copy of the parent’s stack, data segment, heap, and execution text segment, the same program text segment- Library function
exit(status)
System call_exit(status)
Return all resources occupied by the process to the kernel; Parent process passeswait()
Get the state- The system calls
wait(&status)
- If the child process fails
exit()
Terminate, suspends the parent process until the child process terminates- The child process status passed
status
Parameter is- Parent-child processes usually have only one
exit()
Exit, another use_exit()
exit- The system calls
execve(pathname, argv, envp)
Load a new program into current memory, discard the existing program text segment, and recreate the stack, data segment, and heap
fork()
After fork(), both processes return from fork(), the parent returns the pid of the child, the child returns 0, and -1 if the child cannot be created (possibly because the number of processes exceeded the upper limit of real_user_id or system level limit).
pid_t childPid;
switch (childPid = fork()){
case - 1:
/ *... * /
case 0:
/* child perform */
default:
/* parent perform */
}
Copy the code
File sharing between the parent and child processes
The fork() file descriptor is created similar to dUP (), pointing to the same open file handle, that is, the file offset is shared. The parent and child processes will not overwrite each other’s output, but will be out of order. The shell does not add & and the parent waits for the child to finish
fork()
Semantic memory
- Create copies of program segments, data segments, heap segments, and stack segments
- It’s wasteful, like
fork()
After theexec()
Reinitialize….- Optimized measures
- When the kernel marks each process’s code fragment as RO, fork() as the child’s build fragment, it builds a series of process-level page tables that all point to the same physical memory page frame as the parent process
- Data segment, heap segment, stack segment of each page, the kernel before copy-on-write (Redis BUG encountered): these segments of the page table to the parent process physical address of the same physical memory page, and these pages marked as read-only, then set up a copy of the page to modify
vfork()
- Avoid using
- This works immediately for child processes
exec()
designed- There is no need to copy the virtual memory paging table for the child process, sharing the parent process memory until exec() or _exit() is successfully called (file descriptor table is independent for each process)
- Pause the parent process before the child calls exec() and _exit()
- Can guarantee call
vfork()
The child process then gets CPU scheduling before the parent process
fork()
And then race conditions
/proc/sys/kernel/sched_child_runs_first
To zero,fork()
The parent process then schedules first
Linux/Unix Programming Manual -25(Termination of processes)
How the process terminates
- Terminating the process by signaling can result in a core dump.
- By calling the
_exit()
Normal termination,main()
functionreturn n
Is equivalent toexit(n)
- If any steps performed during the rollout process require access
main()
Local variable, then frommain()
Returns result in undefined behavior (ex:setbuff()
Calling local variables)
#include<unistd.h>
void _exit(int status);
// The call always succeeds
Copy the code
Library functionexit()
#include<stdlib.h>
void exit(in status);
// Is not an asynchronous signal safety function
Copy the code
- Call the exit handler (through
atexit()
andon_exit()
Registered functions), executed in reverse order of registration- The refresh
stdio
Stream buffer- use
status
perform_exit()
Details of process termination
- Close all open file descriptors, directory streams, information directory descriptors? And the transformation descriptor?
- The details involved in the following chapters need to be supplemented.
Exit handler
#include<stdlib.h>
int atexit(void (*func) (void));
// 0 on success
Copy the code
#define _BSD_SOURCE #include<stdlib.h> int on_exit(void (*func)(int, void *), void *args); // Register in the same list as atExit, same execution and register in reverse orderCopy the code
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(a){
printf("Hello\n");
write("STDOUT_FILENO"."WORLD\n".6);
if(fork() == - 1) {exit(- 1);
}
exit(EXIT_SUCCESS);
}
Copy the code
$ gcc fork_io.c -o fork_io
$ ./fork_io
Hello
WORLD
$ ./fork_io > 1.txt | cat 1.txtWORLD Hello Hello // Terminal for line buffering, redirection to file for block bufferingCopy the code
Avoid methods
fork()
beforefflush()
Flush the buffer, or adjust the buffer selection in STdio- The process calls _exit() in the confirmation case.