这里写目录标题

  • kill命令介绍
  • kill命令的原理
    • 信号传递
    • 信号处理(内核)

kill命令介绍

kill 命令会向操作系统内核发送一个信号(多是终止信号)和目标进程的 PID,然后系统内核根据收到的信号类型,对指定进程进行处理。

kill(选项)(参数)
-a:当处理当前进程时,不限制命令名和进程号的对应关系;
-l <信息编号>:若不加<信息编号>选项,则-l参数会列出全部的信息名称;
-p:指定kill 命令只打印相关进程的进程号,而不发送任何信号;
-s <信息名称或编号>:指定要送出的信息;
-u:指定用户。只有第9种信号(SIGKILL)才可以无条件终止进程,其他信号进程都有权利忽略,下面是常用的信号
HUP     1    终端断线(平滑重启进程)
INT     2    中断(同 Ctrl + C)
QUIT    3    退出(同 Ctrl + \)
TERM   15    终止
KILL    9    强制终止
CONT   18    继续(与STOP相反, fg/bg命令)
STOP   19    暂停(同 Ctrl + Z)

示例:

[root@itbkz.com ~]#ps -ef |grep chronyd
chrony   30365     1  0 19:58 ?        00:00:00 /usr/sbin/chronyd
root     30385 29218  0 19:58 pts/1    00:00:00 grep --color=auto chronyd
[root@itbkz.com ~]#kill -9 30483
[root@itbkz.com ~]#ps -ef |grep chronyd
root     30508 29218  0 20:00 pts/1    00:00:00 grep --color=auto chronyd

kill命令的原理

执行kill -9 <PID>,首先要产生信号。执行kill程序需要一个pid,根据这个pid找到这个进程的task_struct(这个是Linux下表示进程/线程的结构),然后在这个结构体的特定的成员变量里记下这个信号。 这时候信号产生了但还没有被特定的进程处理,叫做Pending signal。

等到下一次CPU调度到这个进程的时候,内核会保证先执行do_signal这个函数看看有没有需要被处理的信号,若有,则处理;若没有,那么就直接继续执行该进程。所以我们看到,在Linux下,信号并不像中断那样有异步行为,而是每次调度到这个进程都是检查一下有没有未处理的信号。

信号传递

调用kill命令之后,信号和目标进程的PID被用户发送到内核。

kill本身就是个程序,是有源代码的,它的代码可以在Linux的coreutils里找到。代码很长,我就不全复制过来了,有兴趣的可以去仔细看看。kill命令的核心代码是长这样的:

static int send_signals (int signum, char *const *argv) { … kill (pid, signum); …
}
int main (int argc, char **argv) { … send_signals (signum, argv + optind);…
}

kill命令调用了send_signals (signum, argv + optind)函数,send_signals (signum, argv + optind)又调用了系统调用kill (pid, signum),该系统调用在Linux内核linux-3.16.3/kernel/signal.c中实现,其通过对描述进程的结构体task_struct进行操作,在task_struct中特定的成员变量里记下需要传递的信号。task_struct结构体中与信号相关的部分如下:

struct task_struct { … /* signal handlers */ struct signal_struct *signal; /* 一个进程所有线程共享一个signal */ struct sighand_struct *sighand; sigset_t blocked,real_blocked; /* 哪些信号被阻塞了 */ sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */ struct sigpending pending; /* 进程中的多个线程有各自的pending */…
}

kill (pid, signum)系统调用的核心代码如下,为了方便理解,我给核心逻辑增加了注释:

SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) { … return kill_something_info(sig, &info, pid);
} static int kill_something_info(int sig, struct siginfo * info, pid_t pid) {int ret;// 如果pid大于0,就把信号发送给指定的进程 if (pid > 0) {ret = kill_pid_info(sig, info, find_vpid(pid));return ret;}// 如果pid <=0 并且不等于-1,发送信号给-pid指定的进程组 if (pid != -1) {ret = __kill_pgrp_info(sig, info, pid ? find_vpid( - pid) : task_pgrp(current));} else { //否则发信号给除自己所属进程之外的其它所有进程 int retval = 0,count = 0;struct task_struct * p;for_each_process(p) {if (task_pid_vnr(p) > 1 && !same_thread_group(p, current)) {int err = group_send_sig_info(sig, info, p); ++count;if (err != -EPERM) retval = err;}}ret = count ? retval: -ESRCH;}return ret;
}

kill (pid, signum)会调用kill_something_info(sig, &info, pid)kill_something_info(sig, &info, pid)会根据pid的正负来决定是发给特定的进程还是一个进程组,我们下面主要来看发给一个特定进程的情况,即调用kill_pid_info(sig, info, find_vpid(pid)),该函数的代码如下:

int kill_pid_info(int sig, struct siginfo * info, struct pid * pid) {int error = -ESRCH;struct task_struct * p;p = pid_task(pid, PIDTYPE_PID);if (p) {error = group_send_sig_info(sig, info, p);}return error;
}```

kill_pid_info(sig, info, find_vpid(pid))出现了我们上文提到的task_strcut,这个是Linux下表示每个进程/线程的结构体,根据struct pid找到这个结构后,就调用了group_send_sig_info(sig, info, p),这个函数的代码如下:

int group_send_sig_info(int sig, struct siginfo * info, struct task_struct * p) {int ret;ret = do_send_sig_info(sig, info, p, true);return ret;
}int do_send_sig_info(int sig, struct siginfo * info, struct task_struct * p, bool group) {unsigned long flags;int ret = -ESRCH;if (lock_task_sighand(p, &flags)) {ret = send_signal(sig, info, p, group);unlock_task_sighand(p, &flags);}return ret;
}static int send_signal(int sig, struct siginfo * info, struct task_struct * t, int group) {int from_ancestor_ns = 0;#ifdef CONFIG_PID_NS from_ancestor_ns = si_fromuser(info) && !task_pid_nr_ns(current, task_active_pid_ns(t));#endifreturn __send_signal(sig, info, t, group, from_ancestor_ns);
}static int __send_signal(int sig, struct siginfo * info, struct task_struct * t, int group, int from_ancestor_ns) {struct sigpending * pending;struct sigqueue * q;int override_rlimit;int ret = 0,result; // 发送给进程和线程的区别在这里,如果是进程,则&t->signal->shared_pending,否则&t->pending pending = group ? &t->signal->shared_pending : &t->pending; /* * fast-pathed signals for kernel-internal things like SIGSTOP * or SIGKILL. */ if (info == SEND_SIG_FORCED) goto out_set; … out_set: // 把信号通知listening signalfd. signalfd_notify(t, sig); // 将sig加入目标进程的信号位图中,待下一次CPU调度的时候读取 sigaddset(&pending->signal, sig); // 用于决定由哪个进程/线程处理该信号,然后wake_up这个进程/线程 complete_signal(sig, t, group); ret: trace_signal_generate(sig, info, t, group, result); return ret;
}

可以看到,最终调用到__send_signal,设置信号的数据结构,唤醒需要处理信号的进程,整个信号传递的过程就结束了。这时候信号还没有被进程处理,还是一个pending signal。

信号处理(内核)

内核调度到该进程时,会调用do_notify_resume()来处理信号队列中的信号,之后这个函数又会调用do_signal(),再调用handle_signal(),具体过程就不用代码说明了,最后会找到每一个信号的处理函数,问题是这个信号的处理函数怎么找到?

还记得在上文提到的task_struct吗,里面有一个成员变量sighand_struct就是用来存储每个信号的处理函数的。

struct sighand_struct { atomic_t count; /* 引用计数 */ struct k_sigaction action[_NSIG]; /* 存储处理函数的结构 */ spinlock_t siglock; /* 自旋锁 */ wait_queue_head_t signalfd_wqh; /* 等待队列 */
}; struct k_sigaction { struct sigaction sa;
}
struct sigaction { __sighandler_t sa_handler;
}

其中sa_handler就指向了信号的处理程序。

kill -9 PID杀死进程使用到的系统调用相关推荐

  1. kill -9无法杀死进程

    经常会出现深度学习程序终止,nvidia显卡仍然被占用问题,这时需要用 nvidia-smi 查看占用的进程号,再 kill -9 pid (pid就是进程号) 但有时使用kill -9 无法杀死进程 ...

  2. kill -9 无法杀死进程解决

    kill -9无法杀死一般因为是僵尸进程 利用命令查找僵尸进程 ps -A -ostat,ppid,pid,cmd | grep -e '^[Zz]' 命令注解:-A 参数列出所有进程 -o 自定义输 ...

  3. windows下通过PID杀死进程

    通过pid查看是什么进程: tasklist | findstr pid 通过pid强制杀死进程 taskklill /pid 进程号 -t -f 查找所有运行的端口号 netstat -ano 查看 ...

  4. Ubuntu杀死进程

    在用ubuntu的时候遇到几次程序卡死,但是不知道怎么关闭,心想有没有跟window一样的程序管理器存在?所以就去网上找了下解决方式记录一下: 1.打开系统监视器:gnome-ststem-monit ...

  5. linux强制kill死掉的进程和窗口

    转自ubuntu系统强制关闭程序或窗口 1.最为常用的方法: $ps -aux    查看所有运行的进程 $kill -9  (PID)  杀死进程号为PID的进程 (基本都能处理卡住的程序或窗口,适 ...

  6. Centos杀死进程kill方法大全

    cheersli Centos杀死进程kill方法大全 杀死进程最安全的方法是单纯使用kill命令. 首先使用ps -ef命令确定要杀死进程的PID,然后输入以下命令: # kill -pid 注释: ...

  7. Kill杀死进程方法大全

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 名称:k ...

  8. Python 技术篇-通过进程名称、PID杀死windows进程的两种方法,获取当前运行程序的pid

    方法一: 利用 os 杀死进程. import os# 通过进程名称杀死进程 os.system('taskkill /f /im %s' % 'python.exe')# 通过pid杀死进程 os. ...

  9. linux杀死进程(kill -9的使用)

    linux 的信号机制 信号是Linux中非常重要的部分. 信号机制是进程间相互传递消息的一种方法,全称为软中断信号.信号可以看作进程控制的一部分. 基本概念 signal用来通知进程发生了异步事件, ...

最新文章

  1. 崛起的Python,真的影响了76万人?
  2. 随机森林为何要有放回抽样
  3. 009 数据结构逆向—数组(困难版)
  4. Keras序列模型学习
  5. 【Todo】Zookeeper系列文章
  6. 获得的经验:ActiveMQ,Apache Camel和连接池
  7. mysql sqlstate 08001_关于Toad连接DB2的sqlstate=08001错误
  8. 干掉项目中杂乱的 if-else,试试状态模式,这才是优雅的实现方式!
  9. token与sessionId的区别——学习笔记
  10. 外键查询_详解MySQL数据库删除所有表的外键约束、禁用外键约束相关脚本
  11. 第六章-深入理解类(一)
  12. 固态硬盘故障检测_如何检测固态硬盘是否损坏 - 卡饭网
  13. 两台计算机直连怎么写ip,两台电脑直连(两台电脑用一条网线连接)
  14. python统计元音字母出现的次数,python统计元音字母个数 python输出元音字母
  15. R语言-混合型数据聚类
  16. iVMS-4200 Vs区别_高中和大学的这些区别虽鲜为人知,却字字有据,句句真实
  17. 【GlobalMapper精品教程】027:路径剖面和和视线工具的使用
  18. 矩阵论(2)——线性表示及基与坐标
  19. js实现拼音模糊搜索
  20. 微软修复打印机服务漏洞 所有支持Windows系统都受影响

热门文章

  1. 【漏洞复现】IE 浏览器远程代码执行漏洞(CVE-2018-8174)
  2. finally在python中是什么意思_Python中的finally关键字
  3. Huggingface Accelerate 学习笔记
  4. C#使用 CefSharp 制作一个分屏浏览器
  5. layoutParams的用法
  6. 2022年浙江执业护士资格考试试题及答案
  7. 中间件 middleware
  8. 敏捷测试和瀑布测试的关联
  9. mssql数据库提权中xp_cmdshell使用
  10. conda activate or source activate?