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

  • volatileAvoid registers that optimize global variables
  • sig_atomic_tEnsure read and write atomicity
  • Global variables shared by the main program and signal handler functions are declared as followsvolatile 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

  • SIGKILLandSIGSTOPThe default behavior of thesignal()andsigaction()Always return an error when changing; It also cannot be blocked
  • SIGCONTIf a process is stopped,SIGCONTAlways make it reply; Process receivedSIGCONTThe stop signal of the waiting state will be discarded. Otherwise, the waiting state will be discarded when the stop signal is receivedSIGCONTdiscarded

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 functionsigsuspendimplementation
  • sleep
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 callsfork(): the child gets a copy of the parent’s stack, data segment, heap, and execution text segment, the same program text segment
  • Library functionexit(status)System call_exit(status)Return all resources occupied by the process to the kernel; Parent process passeswait()Get the state
  • The system callswait(&status)
    • If the child process failsexit()Terminate, suspends the parent process until the child process terminates
    • The child process status passedstatusParameter is
    • Parent-child processes usually have only oneexit()Exit, another use_exit()exit
  • The system callsexecve(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, likefork()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 processesexec()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 callvfork()The child process then gets CPU scheduling before the parent process

fork()And then race conditions

  • /proc/sys/kernel/sched_child_runs_firstTo 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 nIs equivalent toexit(n)
    • If any steps performed during the rollout process require accessmain()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 (throughatexit()andon_exit()Registered functions), executed in reverse order of registration
  • The refreshstdioStream buffer
  • usestatusperform_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.