信号:当一个进程产生异常、中断等时,操作系统则给进程发送一个信号,即向进程PCB中写入一个信息(此处即修改一个比特位(位图实现),表示进程是否收到该信号),当进程确认收到该信号则会在恰当的时间去执行其对应的相关处理动作。
三种处理动作:
1.忽略此信号;
2. 执行该信号的默认处理动作(一般都为终止此进程);
3. 提供一个用户自定义信号处理函数,内核处理时切换到用户态执行,即为信号的捕捉。
信号捕捉函数:
#include <signal.h>
typedef void (*sighandler_t)(int); //函数指针,信号处理函数,参数为信号编号
sighandler_t signal(int signum,sighandler_t handler);
参数:
signum:信号编号;
handler:自定义的信号处理函数指针
可用以下命令查看当前系统下的信号:
kill -l
其中1~31是常用信号,其中9号信号不可以被捕捉,34~64则成为实时信号。
产生信号的四种方式:
1.通过终端按键产生信号:
例如:Ctrl-C产生(2号)SIGINT信号,Ctrl-\产生(3号)SIGQUIT信号,Ctrl-Z产生(20号)SIGTSTP信号,SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,SIGTSTP信号使前台进程停止;
2. 硬件异常产生信号:
这些条件由硬件检测并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程;
此处引入Core Dump:
Core Dump:称为核心转储,即当一个进程异常退出之前,将进程的用户空间内存数据全部以文件方式保存到磁盘上,文件名则称作是core,方便gdb的调试。
Post-mortem Debug(事后调试):进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因。
一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存在PCB中),如下:
可用命令:
ulimit -a //查看当前shell下软硬件资源的上限
可知默认下系统分配core文件大小为0,是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全,并且若一个较大程序每次运行都要产生一个core文件,很可能将硬盘空间占用完。
可以用以下命令更改:
ulimit -c 1024 //设置core文件大小上限为1024字节
此处写一个代码(指针非法操作)运用core文件在gdb中调试:
此处第9行指针非法操作,会产生异常,运行此代码:
运用gdb调试如图:最后则直接定位到错误行:第9行,并显示出操作系统发送的信号。
3.调用系统函数向进程发信号(也可使用命令向进程发信号:kill 信号 进程号):
int kill(pid_t id,int signo); //向进程id发送signo信号
int raise(int signo); //自己给自己发送signo信号
返回值:成功返回0,错误返回-1;
#include <stdlib.h>
int abort(void); //使当前进程接收到信号而异常终止
此函数可以捕捉但最后必定使进程退出;
4.由软件条件产生信号:
#include <unisted.h>
unsigned int alarm(unsigned int seconds); //告诉操作系统seconds时间后给自己发送一个SIGALRM信号
返回值:0或以前设定闹钟时间还余下的秒数.
代码模拟:
mykill.c:模拟实现自定义发送信号给某进程
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void usage(char* argv)
{
printf("%s pid sig\n",argv);
exit(0);
}
//自定义实现给一个进程发送信号:./mykill pid sig
int main(int argc,char*argv[])
{
if(argc!=3)
{
usage(argv[0]);
}
int pid=atoi(argv[1]);
int sig=atoi(argv[2]);
kill(pid,sig); //系统接口函数向pid进程发送sig信号
return 0;
}
mysig.c:此代码可以实现:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int value=0;
void myhander1(int sig) //自定义信号处理动作:信号递达
{
printf("pid: %d sig: %d\n",getpid(),sig);
}
void myhander2(int sig)
{
printf("pid:%d sig: %d\n",getpid(),sig);
printf("count==%d\n",value);
}
int main()
{
signal(2,myhander1); //信号捕捉函数:给2号信号定义动作
signal(14,myhander2); //给14号信号定义动作
//统计1s变量可以累加到多大
/*alarm(1); //告诉操作系统1s后给自己发送一个14号信号
while(1)
{
++value;
}*/
int count=0;
while(1)
{
printf("count==%d\n",count);
if(count%5==0)
{
raise(2); //当count%5==0时,自己给自己发送2号信号
}
++count;
sleep(1);
}
return 0;
}
1.count累加无限循环中当%5==0时自己给自己发送2号信号,也可以键盘输入发送2号信号:2.调用alarm(1),告诉操作系统1s后给自己发送一个14号信号,从而调用函数统计1s变量value可以累加到多大:
3.可以调用mykill.c程序结束此无限循环程序: