简单,不能携带大量信息,满足特定条件发生,信号也叫软中断,有可能会有延迟。
信号实际上是由内核发送,内核来处理收到的信号。收到信号的进程,必须对信号做出处理(忽略,捕获,默认动作都行)
阻塞信号集(信号屏蔽字): 将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)
未决信号集:
1
,表信号处于未决状态。当信号被处理对应位翻转回为0
。这一时刻往往非常短暂。2
号信号设置成阻塞(即在阻塞信号集的对应位置设为1
),那么来一个2
号信号,则未信号集的对应值置为1
,什么时候阻塞信号集中的对应位置变成0
了,什么时候未决信号集才能去处理之前被阻塞的那个信号。默认处理动作有五个(linux 输入 man 7 signal 即可查看),如图所示:
注意:9
号,19
号信号,不能捕捉,不能忽略,不能阻塞。
编号 | 名称 | 作用 |
---|---|---|
1 | SIGHUP | 用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程 |
2 | SIGINT | 当用户按下了 |
3 | SIGQUIT | 当用户按下 |
4 | SIGILL | CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件 |
5 | SIGTRAP | 该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。 |
6 | SIGABRT | 调用abort函数时产生该信号。默认动作为终止进程并产生core文件。 |
7 | SIGBUS | 非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。 |
8 | SIGFPE | 在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。 |
9 | SIGKILL | 无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。 |
10 | SIGUSE1 | 用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。 |
11 | SIGSEGV | 指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。 |
12 | SIGUSR2 | 另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。 |
13 | SIGPIPE | Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。 |
14 | SIGALRM | 定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程。 |
15 | SIGTERM | 程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。 |
16 | SIGSTKFLT | Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。 |
17 | SIGCHLD | 子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。 |
18 | SIGCONT | 如果进程已停止,则使其继续运行。默认动作为继续/忽略。 |
19 | SIGSTOP | 停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。 |
20 | SIGTSTP | 停止终端交互进程的运行。按下 |
21 | SIGTTIN | 后台进程读终端控制台。默认动作为暂停进程。 |
22 | SIGTTOU | 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。 |
23 | SIGURG | 套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。 |
24 | SIGXCPU | 进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。 |
25 | SIGXFSZ | 超过文件的最大长度设置。默认动作为终止进程。 |
26 | SIGVTALRM | 虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。 |
27 | SGIPROF | 类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。 |
28 | SIGWINCH | 窗口变化大小时发出。默认动作为忽略该信号。 |
29 | SIGIO | 此信号向进程指示发出了一个异步IO事件。默认动作为忽略。 |
30 | SIGPWR | 关机。默认动作为终止进程。 |
31 | SIGSYS | 无效的系统调用。默认动作为终止进程并产生core文件。 |
34~64 | SIGRTMIN~ SIGRTMAX | LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。 |
Ctrl + c → 2) SIGINT(终止/中断) “INT” ----Interrupt
Ctrl + z → 20) SIGTSTP(暂停/停止) “T” ----Terminal 终端。
Ctrl + \ → 3) SIGQUIT(退出)
除0操作 → 8) SIGFPE (浮点数例外) “F” -----float 浮点数。
非法访问内存 → 11) SIGSEGV (段错误)
总线错误 → 7) SIGBUS
kill
函数/命令产生信号kill命令产生信号:kill -SIGKILL pid
kill函数:给指定进程发送指定信号(不一定杀死)
int kill(pid_t pid, int sig);
pid > 0 要发送进程ID
pid = 0 代表当前调用进程中内所有进程
pid = -1 代表有权限发送的所有进场
pid < 0 代表 -pid 对应的组内所有进程
sig 对应的信号,不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。
练习,让3号子进程杀死父进程
#include
#include
#include
#include
#include
int main(){
int i;
for(i=0; i<5; i++){
pid_t pid=fork();
if(pid==0){
break;
}
}
//我们使3号子进程杀死父进程
if(i==2){
printf("I will kill my father after 5 second!\n");
sleep(5);
kill(getppid(),SIGKILL);
}
if(i==5){
while(1){
printf("I am father!\n");
sleep(1);
}
}
return 0;
}
#include
#include
#include
#include
int main(){
int i=0;
pid_t pid3, pid;
for(i=0; i<5; i++){
pid=fork();
if(pid==0) break;
if(i==2) pid3=pid;
}
if(i<5){
while(1){
printf("I am child, pid=%d, ppid=%d\n", getpid(), getppid());
sleep(3);
}
}
else if(i==5){
printf("I am father,pid=%d, I will kill xiao san pid3=%d\n", getpid(), pid3);
sleep(5);
kill(pid3, SIGKILL);
while(1) sleep(1);
}
return 0;
}
raise
和abort
函数raise
函数:给当前进程发送指定信号(自己给自己发)
raise(signo)
== kill(getpid(), signo);
int raise(int sig);
返回值: 成功:0,失败非0值
#include
#include
#include
#include
int main(){
printf("I will die!\n");
sleep(2);
raise(SIGKILL);//kill(getpid(), sig)
return 0;
}
abort
函数:给自己发送异常终止信号 6) SIGABRT 信号,终止并产生core文件
void abort(void); 该函数无返回
#include
#include
#include
#include
#include
int main(){
printf("I will die!\n");
sleep(2);
//raise(SIGKILL);//kill(getpid(), sig)
abort();
return 0;
}
设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止(Term)。
每个进程都有且只有唯一个定时器。
定时给自己发送SIGALRM
几秒后发送信号
返回值——上次闹钟剩余的秒数
特别的,如果传入参数秒为0,代表取消闹钟
#include
#include
int main(){
alarm(6);
while(1){
printf("hello world \n");
sleep(1);
}
return 0;
}
alarm函数的返回值
#include
#include
int main(){
int ret=0;
ret=alarm(6);
printf("ret=%d\n", ret);
sleep(2);
ret=alarm(5);
printf("ret=%d\n", ret);
while(1){
printf("hello world\n");
sleep(1);
}
return 0;
}
设置定时器(闹钟)。 可代替alarm函数。精度微秒us,可以实现周期定时
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
返回值 成功:0;失败:-1,设置errno
参数:
which:指定定时方式
ITIMER_REAL 自然定时法 SIGALRM
ITIMER_VIRTUAL 计算进程执行时间 SIGVTALRM
ITIMER_PROF 程序执行时间+调度时间 ITIMER_VIRTUAL
new_value 要设置的闹钟时间
old_value 原闹钟时间
#include
#include
#include
int main(){
//setitimer是一个结构体中嵌套了两个结构体
//两个结构体分别代表:周期性的时间设置,下次的闹钟时间
//每个结构体里。,第一个参数是秒,第二个参数是微妙
//这里定义的意思就是:三秒之后发送SIGALRM信号
struct itimerval myit={{0,0},{3,0}};
setitimer(ITIMER_REAL, &myit, NULL);
while(1){
printf("Who can kill me!\n");
sleep(1);
}
return 0;
}
运行结果:
测试周期性发送信号功能
周期性发送SIGALRM信号杀死进程,进程利用catch_sig函数来捕获SIGALRM信号
#include
#include
#include
#include
void catch_sig(int num){
printf("cat %d sig\n", num);
}
int main(){
signal(SIGALRM, catch_sig);
//第一次等待五秒,之后每隔三秒
struct itimerval myit={{3,0},{5,0}};
setitimer(ITIMER_REAL, &myit, NULL);
while(1){
printf("Who can kill me!\n");
sleep(1);
}
return 0;
}
setitimer实现alarm
#include
#include
#include
unsigned int myalarm(unsigned int seconds){
struct itimerval oldit,myit={{0,0},{0,0}};
//second秒之后,给我发送一个SIGALRM信号
myit.it_value.tv_sec=seconds;
//old_value 原闹钟时间
setitimer(ITIMER_REAL, &myit, &oldit);
//打印秒和微秒
printf("tv_sec=%ld, tv_microsec=%ld\n",
oldit.it_value.tv_sec, oldit.it_value.tv_usec);
return oldit.it_value.tv_sec;
}
int main(){
int ret=0;
ret=myalarm(5);
printf("ret=%d\n", ret);
sleep(3);
ret=myalarm(3);
printf("ret=%d\n", ret);
while(1){
printf("hello world\n");
sleep(1);
}
return 0;
}