Linux进程信号(二)之信号产生1
文章目录
- 产生信号
- 1. 通过终端按键产生信号
- Core Dump
- 2.kill命令
- 3. 调用系统函数向进程发信号
- kill函数
- 实现自定义kill命令
- raise函数
- abort函数
产生信号
1. 通过终端按键产生信号
SIGINT
的默认处理动作是终止进程,
SIGQUIT
的默认处理动作是终止进程并且Core Dump
。
键盘组合键
ctrl+c
2号信号
ctrl+\
3号信号
验证:
mysignal.cc
#include<iostream>
#include<unistd.h>
#include<signal.h>using namespace std;//int signum: 收到了哪个信号
void Handler(int signum)
{cout<<"process get a signal: "<<signum<<endl;
}int main()
{signal(3,Handler);//只需要设置一次,在进程生命周期内都有效。while(1){cout<<"I am a crazy process,pid: "<<getpid()<<endl;sleep(1);}return 0;
}
不是所有的信号都可以被signal
捕捉的。
验证:
mysignal.cc
#include<iostream>
#include<unistd.h>
#include<signal.h>using namespace std;//int signum: 收到了哪个信号
void Handler(int signum)
{cout<<"process get a signal: "<<signum<<endl;
}int main()
{for(int i=1;i<=31;i++){signal(i,Handler);}while(1){cout<<"I am a crazy process,pid: "<<getpid()<<endl;sleep(2);}return 0;
}
验证可知,9号信号和19号信号不可以被捕捉,其他信号都可以。
一个是杀掉进程,一个是暂停进程。
Core Dump
首先解释什么是 Core Dump
。
当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,
文件名通常是core
,这叫做 Core Dump
。
进程异常终止通常是因为有Bug
,比如非法内存访问导致段错误,
事后可以用调试器检查core
文件以查清错误原因,
这叫做Post-mortem Debug
(事后调试)。
一个进程允许产生多大的 core
文件取决于进程的 Resource Limit
(这个信息保存 在PCB中)。
默认是不允许产生core文件的,
因为core
文件中可能包含用户密码等敏感信息,不安全。
(云服务core dump
功能是关闭的,因为很多服务器挂掉之后是会自动重启的,
如果该功能开启了,有些服务器挂了又重启,挂了又重启,每次挂了就core dump
一段时间后,磁盘空间的文件一下子就被转储文件占满了,就不是服务挂掉了而是操作系统挂了)。
在开发调试阶段可以用ulimit
命令改变这个限制,允许产生core
文件。
首先用ulimit
命令改变Shell
进程的Resource Limit
,
允许core
文件最大为1024K: ulimit -c 1024
mysignal.cc
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{pid_t id = fork();if (id == 0){int cnt = 500;while (cnt){cout << "I am a chid process,pid: " << getpid() << " ,cnt: " << cnt << endl;sleep(1);cnt--;}exit(1);}int status=0;pid_t rid=waitpid(id,&status,0);if(id==rid){cout<<"child quit info,rid: "<<rid<<" exit code: "<<((status>>8)&0xff)<<" ,exit sig: "<<((status&0x7f))<<" ,core dump: "<<((status>>7)&1)<<endl;// ? & (0000 0000 ... 0001)}return 0;
}
ulimit
命令改变了Shell
进程的Resource Limit
,
进程的PCB由Shell
进程复制而来,
所以也具 有和Shell进程相同的Resource Limit值,这样就可以产生Core Dump了。
是什么?
打开系统的core dump
功能,
一旦进程出异常,OS会将进程在内存中的运行信息,
dump
(转储)到进程的当前目录(磁盘)
形成core.pid
文件:核心转储(core dump
)。
为什么要进行核心转储呢?
这里的错误可以成为运行时错误。
直接复现问题之后,直接定位到出错行。
先运行后调试core-file
(事后调试).
使用core文件(makefile
要加-g):
mysignal.cc
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{int a=10;int b=0;a/=b;cout<<"a = "<<a<<endl;return 0;
}
2.kill命令
kill -signo pid
3. 调用系统函数向进程发信号
kill函数
首先在后台执行死循环程序,然后用kill
命令给它发SIGSEGV
信号。
指定发送某种信号的kill命令可以有多种写法,
上面的命令还可以写成 kill -SIGSEGV pid 或 kill -11 pid,
11是信号SIGSEGV的编号。
以往遇到的段错误都是由非法内存访问产生的,
而这个程序本身没错,给它发SIGSEGV也能产生段错误。
kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。
实现自定义kill命令
mykill.cc
#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<string>
#include<cstring>using namespace std;void Usage(string proc)
{cout<<"Usage:\n\t"<<proc<<" signum pid\n"<<endl;
}//kill signum pid
int main(int argc,char*argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}int signum=stoi(argv[1]);int pid=stoi(argv[2]);int n=kill(pid,signum);if(n==-1){perror("kill");}return 0;
}
测试:
test.cc
#include <iostream>
#include <unistd.h>
using namespace std; int main()
{ while(1) { cout<<"I am a process,pid: "<<getpid()<<endl;sleep(1);} return 0;
}
raise函数
raise函数可以给当前进程发送指定的信号(自己给自己发信号)。
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1。
mykill.cc
#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<string>
#include<cstring>using namespace std;void Handler(int signum)
{cout<<"process get a signal: "<<signum<<endl;
}int main()
{signal(2,Handler);int cnt=0;while(1){cout<<"I am a process,pid: "<<getpid()<<endl;sleep(1);if(cnt%2==0){raise(2);//= kill(getpid(),2);}cnt++;}return 0;
}
abort函数
abort函数使当前进程接收到信号而异常终止。
#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值。
mykill.cc
#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<string>
#include<cstdlib>
#include<cstring>using namespace std;void Handler(int signum)
{cout<<"process get a signal: "<<signum<<endl;
}int main()
{int cnt=0;while(1){cout<<"I am a process,pid: "<<getpid()<<endl;sleep(1);if(cnt%2==0){abort();//如果不考虑进程退出//abort就等于kill(getpid(),6);}cnt++;}return 0;
}
加上自定义处理方式
signal(6,Handler);
执行了自定义方法,但是最后还是退出了。
while(1)
{cout<<"I am a process,pid: "<<getpid()<<endl;sleep(1);
}
说明:
abort
会捕捉自定义方法,信号捕捉完,还要返回调用行继续运行,让进程退出。
(与直接用 kill -6
还是有区别的)