概述
一、什么是信号
1.1、信号的概述
信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。信号是软中断,它是在软件层次上对中断机制的一种模拟。。
信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
1.2、 信号是一种异步通信方式
进程不必等待信号的到达,进程也不知道信号什么时候到达。
信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知用户空间进程发生了哪些系统事件。
1.3、信号特点
每个信号的名字都以字符 SIG 开头。
每个信号和一个数字编码相对应,在头文件 signum.h 中,这些信号都被定义为正整数。
信号名定义路径:
/usr/include/i386-linux-gnu/bits/signum.h
在 Linux 下,要想查看这些信号和编码的对应关系,可使用命令:kill -l
1.4、快捷键产生信号
1、当用户按某些终端键时,将产生信号。
例如:
终端上按“Ctrl+c”组合键通常产生中断信号 SIGINT、终端上按"Ctrl+"键通常产生中断信号 SIGQUIT、终端上按"Ctrl+z"键通常产生中断信号 SIGSTOP。
2、硬件异常将产生信号。
除数为 0,无效的内存访问等。这些情况通常由硬件检测到,并通知内核,然后内核产生适当的信号发送给相应的进程。
3、软件异常将产生信号。
当检测到某种软件条件已发生,并将其通知有关进程时,产生信号。(比如父子进程,当子进程在暂停或退出的时候会自动发出SIGCHLD信号)
二、signal信号函数
头文件:
#include <signal.h>
函数:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
功能:
注册信号处理函数,即确定收到信号后处理函数的入口地址。
参数:
signum:信号编号
handler 的取值:
忽略该信号:SIG_IGN
执行系统默认动作:SIG_DFL
自定义信号处理函数:信号处理函数名
返回值:
成功:返回函数地址,该地址为此信号上一次注册的信号处理函数的地址。
失败:返回 SIG_ERR
注意: SIGKILL、SIGSTOP这两个信号不可忽略,也不可捕捉
2.1、总结函数
sighandler_t signal( int signum, sighandler_t handler);
此函数不难发现,第一个参数,设置为kill -l 下其中一个信号(除了SIGKILL、SIGSTOP)
第二个 参数,有三种选择:
第一种设置为SIG_IGN。 忽略第一个参数 信号
第二种设置为SIG_DFL。 执行第一个参数的动作
第三种设置为信号处理函数名 , 自定义信号处理
三、signal函数使用案例
3.1、signal设置为忽略方式
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
if(signal(SIGINT,SIG_IGN)==SIG_ERR)
{
perror("fail to signal : sigint");
exit(1);
}
if(signal(SIGQUIT,SIG_IGN)==SIG_ERR)
{
perror("fail to signnal : singquit");
exit(1);
}
while(1)
{
printf("Hello worldn");
sleep(1);
}
return 0;
}
此代码设置了 在终端上按"Ctrl+"键或者按“Ctrl+c”发送信号,都会忽略,即不会暂停也不会退出
运行结果
3.2、signal设置为默认方式
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
if(signal(SIGINT,SIG_DFL)==SIG_ERR)
{
perror("fail to signal : sigint");
exit(1);
}
if(signal(SIGQUIT,SIG_DFL)==SIG_ERR)
{
perror("fail to signnal : singquit");
exit(1);
}
while(1)
{
printf("Hello worldn");
sleep(1);
}
return 0;
}
此代码设置了 在终端上按"Ctrl+"键或者按“Ctrl+c”发送信号,都会默认执行结束或者退出
运行结果
3.3、 signal设置为自定义信号处理
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
void hander(int sig);
int main()
{
if(signal(SIGINT,hander)==SIG_ERR)
{
perror("fail to signal : sigint");
exit(1);
}
if(signal(SIGQUIT,hander)==SIG_ERR)
{
perror("fail to signnal : singquit");
exit(1);
}
while(1)
{
printf("Hello worldn");
sleep(1);
}
return 0;
}
void hander(int sig)
{
if(sig == SIGINT)
{
printf("SIGINT正在处理n");
}
if(sig == SIGQUIT)
{
printf("SIGQUIT正在处理n");
}
}
此代码,只是捕捉信号,通过捕捉的信号,执行第二个参数的内容;但不会结束或者退出
运行结果
四、扩展:父子进程,使用signal信号函数回收子进程的资源(重点)
4.1、SIGCHLD概念
无论一个进程是正常终止还是异常终止,都会通过系统内核向其父进程发送 SIGCHLD (17) 信号。父进程完全可以在针对 SIGCHLD (17) 信号的信号处理函数中,异步地回收子进程资源(即僵尸进程),简洁而又高效。
4.2、什么是僵尸进程
子进程自己退出了,父进程没有调用wait或者waidpid函数清理子进程的状态,所以这个状态一直会在进程列表存在。
4.3、解决僵尸进程常用的两种方式
1、父进程需要调用wait/waitpid函数回收子进程资源
2、使用signal信号函数回收子进程的资源
五、案例:使用signal信号函数回收子进程(即僵尸进程)
5.1、僵尸进程的示例(没有使用signal信号函数)
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
pid_t pid;
pid= fork();
if(pid < 0)
{
perror("create fork fail ");
exit(1);
}
if(pid==0)
{
while(1)
{
static int i=0;
i++;
printf("this is son : pid =%dn",getpid());
sleep(1);
if(i==3)
{
exit(1);
}
}
}
else if(pid>0)
{
while(1)
{
printf("Hello this is father: pid =%dn",getpid());
sleep(1);
}
}
return 0;
}
运行结果 (下图红框 Z+ 表示僵尸进程)
5.2、使用signal信号函数回收僵尸进程
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void handler(int sig)
{
wait(NULL);
}
int main()
{
pid_t pid;
pid= fork();
signal(SIGCHLD,handler);
if(pid < 0)
{
perror("create fork fail ");
exit(1);
}
if(pid==0)
{
while(1)
{
static int i=0;
i++;
printf("this is son : pid =%dn",getpid());
sleep(1);
if(i==3)
{
exit(1);
}
}
}
else if(pid>0)
{
while(1)
{
printf("Hello this is father: pid =%dn",getpid());
sleep(1);
}
}
return 0;
}
运行结果(红框变为正常的S+) 即回收成功
最后
以上就是热情灰狼为你收集整理的信号(学习笔记)的全部内容,希望文章能够帮你解决信号(学习笔记)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复