linux信号以及core

2020-12-11  本文已影响0人  R0lan
CoreDumped.gif

何为信号

信号(signal)用于通知进程发生了某种情况。进程有以下3种处理信号的方式:

  1. 忽略信号。有些信号表示硬件异常,例如,除以0或访问进程地址空间以外的存储单元等,因为这些异常产生的后果不确定,所以不推荐使用这种处理>方式。
  2. 按系统默认方式处理
  3. 提供一个函数,信号发生时调用该函数,这被称为捕抓该信号。通过提供的信号处理函数,我们就能知道什么时候产生了信号,并按期望的方式处理它。

以上摘自《APUE》中文版14页

信号类型

[root@test ~]# kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    

每个信号有其默认的处理方式,参考manpage,分为以下几种类型

Term Default action is to terminate the process.

Ign Default action is to ignore the signal.

Core Default action is to terminate the process and dump core (see
core(5)).

Stop Default action is to stop the process.

Cont Default action is to continue the process if it is currently
stopped.

例如我们常用的kill的默认信号SIGTERM(15),以及强制结束信号SIGKILL(9),其对应的处理方式都是Term,我们经常使用SIGABRT(6)对应处理方式是Core,可以用来产生core-dump文件

core文件详解

core文件是ELF-formatted文件,可以使用readelf查看core的elf信息。它包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息等

配置coredump

# 开启coredump
ulimit -c unlimited
# 持久化方式
echo "ulimit -c unlimited" >> /etc/profile
# 在/etc/sysctl.conf文件中加入
# 例如下面这个命令,可以通过在%e前面增加/home/之类的路径使其保存到特定路径
kernel.core_pattern=%e.core.%s_%t
并保存退出,执行下面指令使其生效
sysctl -p


# 参数含义
  %%  output one '%'
  %p  pid
  %u  uid
  %g  gid
  %s  signal number
  %t  UNIX time of dump
  %h  hostname
  %e  executable filename

# 默认是这样的 |/usr/share/apport/apport %p %s %c %P
  If the first character of the pattern is a '|', the kernel will treat
  the rest of the pattern as a command to run.  The core dump will be
  written to the standard input of that program instead of to a file.

控制coredump的mapping

/proc/[pid]/coredump_filter可以指定怎样的进程空间内存可以保存到core文件里,它是由下面的bitmask组成

默认配置是0x33,也就是说bits 0 (anonymous private mappings), 1 (anonymous shared mappings), 4 (ELF headers) and 5 (private huge pages) 都会被dump出。如果想改变bitmask,可以使用如下方法:

echo 0x00000001 > /proc/[pid]/coredump_filter

也可以配置在当前shell生效的coredump_filter

# 如果没有下面的文件,请检查内核参数CONFIG_ELF_CORE是否配置
$ echo 0x7 > /proc/self/coredump_filter
$ ./some_program

注意:

  1. 如果你想设置全局的coredump_filter,可以参考这篇文章
  2. MMIO的页永远不会被dump出来

产生core-dump的方法

  1. 发信号SIGABRT,会终止进程
  2. gcore,gcore会调用gdb产生coredump,不会终止进程,但是产生core的时候进程会处于stopped状态
  3. 设置信号处理函数,信号处理函数fork()abort()
  4. google-coredumper上述的方法1终止进程,方法2会暂停进程,所以谷歌就做了个coredump库,支持在运行时coredump

gcore

gdb --nx --batch -ex set pagination off -ex set height 0 -ex set width 0 -ex attach 40923 -ex gcore core.40923 -ex detach -ex quit

所以,如果你没有gcore命令,也可以使用如下命令产生gcore

gdb --pid=40923 --batch -ex gcore

signal 函数

  1. 信号处理函数signal 规定了在某种信号signo情况下调用func处理
  2. 正在处理的时候,其他信号是不能递交的,必须等当前信号处理完毕,其他信号才能递交
#include <signal.h>
typedef void Sigfunc(int);
Sigfunc *signal(int signo, Sigfunc *func)

SIGCHLD

  1. 子进程终止时,会给父进程发一个SIGCHLD信号(其实这个信号是由内核在进城终止时发给父进程的(《UNIX网络编程》P103)),如果父进程不处理的话,子进程机会变成僵尸进程
  2. 父进程只需要在信号处理函数里面调用wait即可。
  3. 如果不想子进程产生僵尸进程,可以设置SIG_IGN
     signal(SIGCHLD, SIG_IGN);
    
signal(SIGCHLD, sig_chld);

void
sig_chld(int signo)
{
    pid_t   pid;
    int     stat;

    pid = wait(&stat);
    printf("child %d terminated\n", pid);
    return;
}

僵尸进程的危害

所谓僵尸进程,形象来说,进程已死,但其尸体还在,没人收尸啊,冤魂不散,仍然占用一个进程号,如果主进程不妥善处理,当僵尸进程数量巨大之后,就没法再次fork了,所以对于大型并发服务器来说,当建立了进程池,一定要想办法处理掉所有僵尸进程。

SIGPIPE

《UNP》中文版113页

  1. A与B连接, B异常终止了,这是B会终止socket连接并且给A发一个fin,
  2. 这时如果A再给B发一个写, B的套接字会回一个RST
  3. 如果A再再给B发一个写, 会引发SIGPIPE信号, 这个信号试图终止进程A, 如果A不情愿被终止,那么必须signal捕获该信号并处理,但是无论如何,写操作都会fail并且errno=EPIPE
  4. signal函数最简单的方法就是把SIGPIPE置成SIG_IGN,这样进程就会忽略SIGPIPE信号。
signal(SIGPIPE, SIG_IGN);

SIGIO

信号驱动式I/O模型, 利用信号,让内核在描述符就绪时发送SIGIO信号通知进程

其他相关函数

sigation

struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, 0 );

sigation的定义:

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;  // 一般置0, 有一些特殊的标志位会用到这个, 如SA_NOCLDSTOP等
    void     (*sa_restorer)(void);
};

wait和waitpid函数

wait 函数可以用来处理已终止的子进程

#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);  // 成功返回进程ID, 出错返回0或-1
  1. 通过返回,返回已终止子进程的ID
  2. 通过statloc指针返回子进程终止状态

waitpid()

《UNIX网络编程》 P110

通过waitpid设置WNOHANG选项,可以告知waitpid在有尚未终止的子进程在运行时不要阻塞

参考链接

  1. Linux信号(一):信号类型
  2. What's the easiest way to detect what signals are being sent to a process?
  3. signal(7) — Linux manual page信号的不同用处
  4. core(5) — Linux manual page
  5. 线上内存泄漏的正确查找姿势
上一篇 下一篇

猜你喜欢

热点阅读