Linux信号发送命令之kill命令

kill

作用:向进程发送信号。

注意: kill命令只是用来向进程发送信号的,而不是直接杀死进程。

kill命令的一般使用格式:

kill [参数] 进程号

进程号可用ps命令查看

kill的参数:

-l 列出全部的信号名称
-a 当处理当前进程时,不限制命令名和进程号的对应关系
-p 指定kill 命令只打印相关进程的进程号,而不发送任何信号
-s 指定发送信号
-u 指定用户

kill命令可直接向进程发送特定的信号,使用格式为:

kill [信号编号] 进程号

注意:

如果不指定信号编号,则默认发送15号SIGTERM 信号。该信号是程序结束(terminate)信号,将终止所有不能捕获该信号的进程,通常用来要求程序自己正常退出。如果进程仍然终止不了,可尝试发送9号SIGKILL信号,强制杀死进程。

发送9号信号来强制杀死进程示例:

kill -9 进程号

查看这些信号编号:

kill -l   # l 即 list 的意思
slot@slot-ubt:~$ 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    
slot@slot-ubt:~$ 
    

可以看到,一共有64个信号。其中,前32个信号属于Unix经典信号(即所有类Unix操作系统都提供的信号);后32个信号属于实时信号(用户实时自定义信号),即与特定的硬件驱动相匹配的信号。常用的是前32个信号,而后32个信号做底层驱动开发时能用到。

其中,最常用的信号有:

2号 SIGINT:中断信号
9号 SIGKILL:杀死信号
11号 SIGSEGV:段错误信号
19号 SIGSTOP:暂停信号(如使用control + z操作)
18号 SIGCONT:继续信号(对应19号暂停信号)

实例1: 一个有关段错误(Segmentation fault)的例子

demo:seg_fault.c

#include <stdio.h>

int main()
{
    char *str = "hello";
    *str = 'a'; 
    printf("%s\n", str);
    return 0;
}

编译

slot@slot-ubt:~/test$ gcc seg_fault.c -o app
slot@slot-ubt:~/test$ 
slot@slot-ubt:~/test$ ls
aa  app  bb  cc  haha.ha  hello.txt  seg_fault.c
slot@slot-ubt:~/test$ 

可以看到,编译通过。

运行

slot@slot-ubt:~/test$ 
slot@slot-ubt:~/test$ ./app
Segmentation fault (core dumped)
slot@slot-ubt:~/test$ 

可以看到,运行程序时出现段错误Segmentation fault

调试

slot@slot-ubt:~/test$ gcc seg_fault.c -g -o app
slot@slot-ubt:~/test$
slot@slot-ubt:~/test$ gdb app
GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from app...done.
(gdb) r
Starting program: /home/slot/test/app 

Program received signal SIGSEGV, Segmentation fault.
0x000000000040053a in main () at seg_fault.c:6
6       *str = 'a';
(gdb) 

可以看到,程序退出的原因是接收到了段错误的信息:

Program received signal SIGSEGV, Segmentation fault.

具体出错位置是在源文件的第6行:

6       *str = 'a';

原因分析

我们知道,字符串是不可改变的量,被分配在内存区域的数据段。当尝试向该只读(RO: read-only)数据区域进行写操作时,操作系统内核(kernel)会通过kill命令向当前进程发送编号为11的信号,即SIGSEGV(段错误)信号,导致程序终止。

实例2:向正常程序发送11号段错误信号

demo: loop_forever.c

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

int main()
{
    while (1) {
        printf("hello world\n");
        sleep(2);
    }
    return 0;
}

编译并运行程序

slot@slot-ubt:~/test$ gcc loop_forever.c -o loopapp
slot@slot-ubt:~/test$ 
slot@slot-ubt:~/test$ ./loopapp 
hello world
hello world
...

在另一个终端中向程序loopapp发送11号信号:

slot@slot-ubt:~$ ps -ef | grep loopapp
slot      6242  2123  0 09:54 pts/17   00:00:00 ./loopapp
slot      6247  4844  0 09:54 pts/1    00:00:00 grep --color=auto loopapp
slot@slot-ubt:~$ 
slot@slot-ubt:~$ kill -11 6242
slot@slot-ubt:~$ 

发送11号信号后:

slot@slot-ubt:~/test$ ./loopapp 
hello world
hello world
hello world
hello world
hello world
hello world
hello world
Segmentation fault (core dumped)

可以看到,正常程序在接收到11号信号后同样会出现段错误。

一个小结

一般,当我们遇到Segmentation fault这种错误时,很可能是我们对内存进行了一些非法操作,如 向不存在的内存地址进行写操作、对内核空间(注:在32位Linux操作系统中,地址空间的划分为:0~3G为用户空间,3~4G为内核空间)进行写操作、对只读数据区进行写操作(如实例1所示)等。

ps:gdb的使用可参考如下文章:
GDB: The GNU Project Debugger
陈皓:用GDB调试程序
Linux gdb调试器用法全面解析

扩展:kill函数

作用

kill()函数将信号发送给进程或进程组。

声明

 #include <sys/types.h>
 #include <signal.h>

 int kill(pid_t pid, int sig);

kill函数声明所在位置为

glibc-2.24/signal/signal.h:127:extern int kill (__pid_t __pid, int __sig) __THROW;

关于参数的说明

参数pid 有如下四种情况:

  • pid > 0:将信号发送给进程ID为pid的进程;
  • pid == 0:将信号传发送给与发送进程属于同一进程组的所有进程(这些进程的进程组ID等于发送进程的进程组ID);
  • pid < 0:将该信号发送给其进程组ID等于pid绝对值的所有进程;
  • pid == -1:将该信号发送给系统内所有的进程。
    注意:
    这里所说的“所有进程”不包括实现定义的系统进程集。对于大多数Unix系统而言,系统进程集包括内核进程init进程(pid为1) 。

实现源码

源码位置为glibc-2.24/signal//kill.c:26:__kill (int pid, int sig)

/* Send signal SIG to process number PID.  If PID is zero,
   send SIG to all processes in the current process's process group.
   If PID is < -1, send SIG to all processes in process group - PID.  */
int
__kill (int pid, int sig)
{
      __set_errno (ENOSYS);
      return -1;
}
    原文作者:echoworlding
    原文地址: https://www.jianshu.com/p/6c371828a856
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞