你无法用ctrl c 杀死的进程

1、编写一段程序,使用系统调用fork( )创建两个子进程,再用系统调用signal(
)让父进
 程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill(
)向两个子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止:  

终止一个进程或终止一个正在运行的程序,一般是通过 kill
、killall、pkill、xkill
等进行。比如一个程序已经死掉,但又不能退出,这时就应该考虑应用这些工具。

有如下shell 脚本 pingoo.sh

Child process 1 is killed by parent!

另外应用的场合就是在服务器管理中,在不涉及数据库服务器程序的父进程的停止运行,也可以用这些工具来终止。为什么数据库服务器的父进程不能用这些工具杀死呢?原因很简单,这些工具在强行终止数据库服务器时,会让数据库产生更多的文件碎片,当碎片达到一定程度的时候,数据库就有崩溃的危险。比如mysql服务器最好是按其正常的程序关闭,而不是用pkill
mysqld 或killall mysqld
这样危险的动作;当然对于占用资源过多的数据库子进程,我们应该用kill
来杀掉。

#!/bin/bash
while true
do     
    ping 127.0.0.1
done;

$> pingoo.sh

Child process 2 is killed by parent!

1 kill

键盘键入 ctrl +c(^c)
ooooops!
你会发现无论你按多少次都无法终止该脚本的运行.

父进程等待两个子进程终止后,输出以下信息后终止:

kill的应用是和ps 或pgrep 命令结合在一起使用的;

使用ps 命令找到进程树

Parent process is killed!

kill 的用法:

$> ps -j f
 PID  PGID   SID TTY      STAT   TIME COMMAND
21994 21994 21994 pts/29   Ss     0:00 /bin/bash
28833 28833 21994 pts/29   S+     0:00  _ /bin/bash ./pingoo.sh
28834 28833 21994 pts/29   S+     0:00      _ ping 127.0.0.1
 1 #include<stdio.h>
 2 #include<signal.h>
 3 #include<unistd.h>
 4 #include<sys/types.h>
 5 #include<sys/wait.h>
 6 int wait_mark;
 7 void waiting(),stop();
 8 void main()
 9 {int  p1, p2;
10 signal(SIGINT,stop);
11 while((p1=fork())==-1);
12 if(p1>0)                            /*在父进程中*/
13 {①
14 while((p2=fork())==-1);
15            If(p2>0)                    /*在父进程中*/
16             { ②
17                   wait_mark=1;
18                  waiting(0);
19                 kill(p1,10);
20                 kill(p2,12);
21                 wait( );
22                wait( );
23                printf("parent process is killed!n");
24                exit(0);
25             }
26            else                        /*在子进程2中*/
27            {
28 wait_mark=1;
29 signal(12,stop);
30 waiting();
31 lockf(1,1,0);
32 printf("child process 2 is killed by parent!n");
33 lockf(1,0,0);
34 exit(0);
35 }
36 } 
37 else                        /*在子进程1中*/
38 {
39       wait_mark=1;
40       signal(10,stop);
41       waiting();
42       lockf(1,1,0);
43       printf("child process 1 is killed by parent!n");
44       lockf(1,0,0);
45       exit(0);
46 }
47 }
48 void waiting()
49 {
50    while(wait_mark!=0);
51 }
52 void stop()
53 {
54    wait_mark=0;
55 }

kill [信号代码] 进程ID

STAT 进程状态标志:
S: 可中断的sleep,
s: session leader;
+: 前台进程;

首先要明确一个重要假设:当用户按下Ctrl c 时,
他是要终止前台进城组的运行, 出发进程注册了自定义的信号处理函数;

但是如果子进程capture sigint, 则 shell 会假设用户通过发送sigint
使子进程做特定工作(比如ping的统计信息),
也就说用户发送sigint的目的并不是结束前台进程组,
而是触发特定动作;ping 就是这类安装了自定义的信号处理函数的进程;

 

注:信号代码可以省略;我们常用的信号代码是 -9 ,表示强制终止;

当你按下ctrl c后发生了什么?

ctrl +c 被终端驱动程序解释为 sigint 信号, 由kernel 发送给 前台进程组 .
ping 进程 capture 该信号, 调用信号处理函数。 同时,
ping的父进程也收到sigint信号(父进程处理interruptible sleep 状态,
也就出出于wait系统调用中),
父进程被迫退出wait系统调用,检查退出原因(是否是EINTR,也就是中断的系统调用),然后通过WIFSIGNALED宏可以判断子进程是否注册了信号处理函数
。如果 注册了信号处理函数,capture sigint,
父进程继续运行(当前例子是开始下一次循环);
如果是子进程是因收到sigint信号终止的(按照default 方式处理sigint),
父进程会终止运行.

当用户键入ctrl+c后,tty driver 产生SIGINT信号给前台进程,
pingoo.sh和其子进程 都看见sigint信号,(他们是前台进程组成员)。
ping.sh作为父进程处于waitpid blocking状态, 收到sigint 后waitpid
立即返回 -1, 设置errorno为 EINTR。同时 bash要求child
立即死亡(也就是sigint的默认动作 terminate)。最后设置
child_caught_sigint = 0, bash 随后会根据该flag 退出;

如果child capture signal,设置 child_caught_sigint = 1; bash
根据该flag 不退出, 因为子进程capture sigint后, 触发信号处理函数。
其信号处理函数需要做特定处理,如果子进程退出,则 父进程会受到 sigchld
信号,父进程会认为子进程正常退出, 循环继续。

⑴运行程序并分析结果。

举例:

源代码解析:

bash 4.3.3: jobs.c

  1. waitpid
    pid<-1
    等待进程组识别码为
    pid 绝对值的任何子进程。
    pid=-1 等待任何子进程,相当于 wait()。
    pid=0 等待进程组识别码与目前进程相同的任何子进程。
    pid>0 等待任何子进程识别码为 pid 的子进程。

Process-Completion-Status

  1. WIFSIGNALED:
    This macro returns a nonzero value if the child process terminated
    because it received a signal that was not handled.
  2. WTERMSIG:
    If WIFSIGNALED is true of <var style=”background-color:
    inherit;”>status</var>, this macro returns the signal
    number of the signal that terminated the child process
  /* If waitpid returns -1/EINTR and the shell saw a SIGINT, then we
     assume the child has blocked or handled SIGINT.  In that case, we
     require the child to actually die due to SIGINT to act on the
     SIGINT we received; otherwise we assume the child handled it and
     let it go. */
// status = waitpid(pid), pid <0 
      if (pid < 0 && errno == EINTR && wait_sigint_received)
    child_caught_sigint = 1;  //waitpid被 sigint中断,立即 假设此时 子进程也 capture signit 

      if (pid <= 0)
    continue;    /* jumps right to the test */

      /* If the child process did die due to SIGINT, forget our assumption
     that it caught or otherwise handled it. */
      if (WIFSIGNALED (status) && WTERMSIG (status) == SIGINT)
        child_caught_sigint = 0; 

  if (JOBSTATE (job) == JDEAD)
    {
      /* If we're running a shell script and we get a SIGINT with a
     SIGINT trap handler, but the foreground job handles it and
     does not exit due to SIGINT, run the trap handler but do not
     otherwise act as if we got the interrupt. */
// wait_sigint_received :  wait 阻塞中收到sigint,
// child_caught_sigint : 子进程 capture sigint
// IS_FOREGROUND : 前台进程组
// interactive_shell : 交互shell?
// signal_is_trapped : 
      if (wait_sigint_received && interactive_shell == 0 &&
      child_caught_sigint && IS_FOREGROUND (job) &&
      signal_is_trapped (SIGINT))
    {
      int old_frozen;
      wait_sigint_received = 0;
      last_command_exit_value = process_exit_status (child->status);

      old_frozen = jobs_list_frozen;
      jobs_list_frozen = 1;
      tstatus = maybe_call_trap_handler (SIGINT); // 
      jobs_list_frozen = old_frozen;
    }

如何终止该脚本的运行呢:

$> kill -9 28833 28834

一定要先杀死父进程

^C

[[email protected]
~]# ps auxf |grep httpd
root 4939 0.0 0.0 5160 708 pts/3 S+ 13:10 0:00 _ grep httpd
root 4830 0.1 1.3 24232 10272 ? Ss 13:02 0:00 /usr/sbin/httpd
apache 4833 0.0 0.6 24364 4932 ? S 13:02 0:00 _ /usr/sbin/httpd
apache 4834 0.0 0.6 24364 4928 ? S 13:02 0:00 _ /usr/sbin/httpd
apache 4835 0.0 0.6 24364 4928 ? S 13:02 0:00 _ /usr/sbin/httpd
apache 4836 0.0 0.6 24364 4928 ? S 13:02 0:00 _ /usr/sbin/httpd
apache 4837 0.0 0.6 24364 4928 ? S 13:02 0:00 _ /usr/sbin/httpd
apache 4838 0.0 0.6 24364 4928 ? S 13:02 0:00 _ /usr/sbin/httpd
apache 4839 0.0 0.6 24364 4928 ? S 13:02 0:00 _ /usr/sbin/httpd
apache 4840 0.0 0.6 24364 4928 ? S 13:02 0:00 _ /usr/sbin/httpd

child process 2 is killed by parent!

我们查看httpd 服务器的进程;您也可以用pgrep -l httpd 来查看;

child process 1 is killed by parent!

我们看上面例子中的第二列,就是进程PID的列,其中4830是httpd服务器的父进程,从4833-4840的进程都是它4830的子进程;如果我们杀掉父进程4830的话,其下的子进程也会跟着死掉;

parent process is killed!

[[email protected]
~]# kill 4840 注:杀掉4840这个进程;
[[email protected]
~]# ps -auxf |grep httpd
注:查看一下会有什么结果?是不是httpd服务器仍在运行?
[[email protected]
~]# kill 4830 注:杀掉httpd的父进程;
[[email protected]
~]# ps -aux |grep httpd
注:查看httpd的其它子进程是否存在,httpd服务器是否仍在运行?

 

对于僵尸进程,可以用kill -9 来强制终止退出;

⑵如果把signal(SIGINT,stop)放在①号和②号位置,结果会怎样并分析原因。

比如一个程序已经彻底死掉,如果kill
不加信号强度是没有办法退出,最好的办法就是加信号强度 -9
,后面要接杀父进程;比如;

1-

[[email protected]
~]# ps aux |grep gaim
beinan 5031 9.0 2.3 104996 17484 ? S 13:23 0:01 gaim
root 5036 0.0 0.0 5160 724 pts/3 S+ 13:24 0:00 grep gaim

[[email protected]
~]# pgrep -l gaim
5031 gaim
[[email protected]
~]# kill -9 5031

^C

2 killall

child process 2 is killed by parent!

killall 通过程序的名字,直接杀死所有进程,咱们简单说一下就行了。

parent process is killed!

用法:killall 正在运行的程序名

 

killall 也和ps或pgrep 结合使用,比较方便;通过ps或pgrep
来查看哪些程序在运行;

2-

举例:

^C

[[email protected]
beinan]# pgrep -l gaim
2979 gaim
[[email protected]
beinan]# killall gaim

parent process is killed!

3 pkill

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图