一、信号
1.信号的定义
是linux系统
为了响应某些状况而产生的事件
。进程收到信号后应该采取相应的措施
2. 哪些情况会引发信号?
1.键盘事件,如ctrl+ c ,ctrl+/(结束程序)
2.非法内存
3.硬件故障
4.环境切换
3.如何查看信号
通过指令
kill -l
查看
4.信号的默认处理方式
man 7 signal
查看
5.常用信号解释
SIGINT | ctrl+c发出的信号 |
---|---|
SIGOUIT | ctrl+\发出的信号 |
SIGUSER1 | 用户自定义信号,常用于发送与获取 |
SIGALRM | 闹钟信号,用于倒计时 |
SIGPWR | 关机,默认动作为终止进程 |
SIGSTOP | 停止进程的执行,默认作为暂停处理 |
SIGKILL | 无条件终止进程 |
SIGBUS | 非法访问内存地址,默认为终止进程并产生core文件 |
SIGHUP | 用户退出shell,默认终止进程 |
SIGSEGV | 进程进行了无效的内存访问 ,默认动作为终止进程 |
SIGPIPE | 向一个没有读端的管道写数据 |
SIGTERM | 请求终止进程,kill命令缺省发送 |
SIGIO | 此信号向进程指示发出一个异步IO事件 |
SIGWINCH | 窗口大小改变时发出 |
SIGXCPU | CPU时间限制超时 |
SIGXFSZ | 进程超过文件大小限制 |
SIGTRAP | 实现相关硬件异常 |
SIGILL | 非法指令异常 |
SIGFPE | 数学相关的异常,如被0除 |
SIGABRT | 由调用abrot函数产生,进程非正常退出 |
6.进程收到信号的3种处理
1.默认:执行该信号的默认处理动作
2.忽略:信号来了不处理,丢掉信号SIGKILL SIGSTOP不可忽视
3.捕获并处理:信号来了。执行自己写的代码,SIGKILL SIGSTOP不能捕获
7.信号的分类
a.
不可靠信号
( 1-31不可靠):
linux的信号继承自早期的Unix信号,
Unix信号的缺陷:
1.信号处理函数执行完毕,信号恢复成默认处理方式(linux已改进)
2.会出现信号丢失,信号不排队
b.可靠信号
( 34-64信号):
不会出现信号丢失,支持排队,信号处理函数执行完毕,不会恢复成缺省处理方式。
c.实时信号
:就是可靠信号
d.非实时信号
:就是不可靠信号
二、操作信号
1.注册信号
(1)signal函数
函数原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数参数解释
:
signum:指明了所要处理的信号的类型,可取除SIGKILL与SIGSTOP外的任一种信号
sighandler_t handeler:信号执行函数,可取三种方式获得:
(1)自己定义函数
(2)SIG_IGN #define SIG_IGN ((sighandler_t)1) //忽略该信号
(3)SIG_DFL #define SIG_IGN ((sighandler_t)0) //默认处理
返回值
:成功时返回信号处理函数指针
失败时返回SIG_ERR
SIG_ERR #define SIG_IGN((sighandler_t)(-1))
(2)代码实现:
1
2 #include <stdio.h>
3 #include <signal.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6
7 void handler(int s)
8 {
9 printf("game over!\n");
10 exit(0);
11 }
12
13 int main()
14 {
15 //ctrl+c
16 signal(SIGINT,SIG_IGN);
17 //crl+\信号
18 signal(SIGQUIT,handler);
19
20 for(;;)
21 {
22 printf("living!\n");
23 sleep(1);
24 }
25 }
26
2.如何给某一进程发送信号
方式1:
kill -信号值 pid
方式2:
函数:
int kill(int pid,int signum)
返回值:成功返回0,失败返回-1
参数解释:
signum:信号值,及信号编号
pid:进程id,如下值:pid>0 ,发送给pid进程 pid=0;调用者所在的进程组的任何一个进程 pid=-1;有权发送的任何一个进程,除了1;
进程组
:进程组有若干个进程
用管道连接的进程,fork创建出来的父子进程属于同一个进程组
后台进程
:jobs 查看
fg %id 将后台进程调到前台(id是作业号,非进程号)
ctrl+c 只发给前台进程
前台进程
:ctrl+z 前台转后台
直接以后台方式启动进程,(./a.out &)>a
代码实现:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <signal.h>
5
6 void handler(int n)
7 {
8 printf("收到!\n");
9 }
10
11 int main( void )
12 {
13 signal(SIGUSR1, handler);//用户自定义信号
14 pid_t pid = fork();//创建子进程
15 if (pid == 0 ) {
16 sleep(1);
17 kill(getppid(), SIGUSR1);
18 exit(0);
19 } else {
20 while ( 1 ) {
21 printf("^_^\n");
22 sleep(3);
23 }
24 }
25 return 0;
26 }
结果:
3.三个发送信号函数
给自己发信号:
int rasie(int signum)
给进程组发送信号:int killpg(int pgrp,int sig)
暂停进程,直到信号被打断int pause(void)
三、SIGALRM信号
1.alarm函数
int alarm(int sec)
当sec规定的时间到了,触发SIGALRM信号,如果sec是0,表示清除信号
alarm(0)清除闹钟
返回值:若调用此函数前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0,失败返回-1
2.代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void handler(int s) {
printf("超时\n");
exit(1);
}
int main( void )
{
char buf[100] = {};
printf("name:");
signal(SIGALRM, handler);
alarm(3);
scanf("%s", buf);
alarm(0); // 清除闹钟
printf("name=%s\n", buf);
for ( ; ; ) {
printf("OK\n");
fflush(stdout);
sleep(1);
}
}
j结果:
四:简单的考试计时
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <unistd.h>
5
6 int X = 0;//错误数
7 int V = 0;// 正确数
8
9 void handler(int s) {
10 printf("时间到\n");
11 printf("V=%d, X=%d\n", V, X);
12 exit(1);
13 }
14
15 int main( void )
16 {
17 signal(SIGALRM, handler);
18 int i=0;
19 alarm(20);
20 srand(getpid());
21 for ( i=0; i<10; i++) {
22 int left = rand()%10;
23 int right = rand()%10;
24 printf("%d+%d=", left, right);
25 int ret;
26 scanf("%d", &ret);
27 if ( left + right == ret ) {
28 V++;
29 } else {
30 X++;
31 }
32 }
33
34 printf("finish!\n");
35 printf("V=%d, X=%d\n", V, X);
36 }
37
38
结果:
good night!