目录
一丶信号的概念
● 信号是在软件层次上对中断机制的一种模拟,是一种 异步通信方式
● 信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
● 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
二丶信号的分类
在Linux中,信号被分为不可靠信号和可靠信号,一共64种,可以通过kill -l命令来查看
● 不可靠信号:也称为非实时信号,不支持排队,信号可能会丢失,比如发送多次相同的信号,进程只能收到一次,信号值取值区间为1~31
● 可靠信号:也称为实时信号,支持排队,信号不会丢失,发多少次,就可以收到多少次,信号值取值区间为34~64
三丶信号的处理方式
● 忽略信号:不做任何处理
● 捕捉信号:执行自定义的信号处理函数
● 执行(缺省)默认操作:Linux系统中对每种信号规定了默认操作,即执行信号默认的功
四丶信号产生的方式
● 对于前台进程,用户可以输入特殊字符来发送,比如输入 Ctrl c
● 系统状态变化:比如alarm定时器到期时将引起SIGALRM信号
● 在终端运行kill命令或在程序中调用kill函数
五丶信号
SIGKILL:结束进程,不能被忽略不能被捕捉 9
SIGSTOP:结束进程,不能被忽略不能被捕捉 19 停止信号
SIGCHLD:子进程状态改变时给父进程发的信号,不会结束进程 17
SIGINT:结束进程,对应的快捷方式 ctrl c 2
SIGTSTP:暂停信号,对应的快捷方式 ctrl z 20
SIGQUIT:退出信号,对应的快捷方式 ctrl \ 3
SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程 14
SIGTERM:结束终端进程,kill使用时不加数字默认是此信号 15
六丶信号函数
1.发送信号
#include <signal.h>
int kill(pid_t pid, int sig);
功能:信号发送
参数:pid:指定进程
sig:要发送的信号
返回值:成功 0
失败 -1
父子进程间进行通讯
2.闹钟信号
#include <unistd.h>
unsigned int alarm(unsigned int seconds)
功能:在进程中设置一个定时器
参数:seconds:定时时间,单位为秒
返回值:
如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
注意:一个进程只能有一个闹钟时间。如果在调用alarm时
已设置过闹钟时间,则之前的闹钟时间被新值所代替
int pause(void);
功能:用于将调用进程挂起,直到收到信号为止。
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//kill(getpid(), SIGKILL); //给进程发送信号,此例子是给当前进程发送SIGKILL信号
// raise(SIGKILL); //给当前进程发送SIGKILL信号,等同于kill(getpid(), SIGKILL);
// while (1);
pause(); //将进程挂起,作用类似死循环但是不占用CPU
return 0;
}
3.定时器
unsigned int alarm(unsigned int seconds)
功能:在进程中设置一个定时器。当定时器指定的时间到了时,它就向进程发送SIGALARM信号。
参数:seconds:定时时间,单位为秒
返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
注意:一个进程只能有一个闹钟时间。如果在调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所代替。
常用操作:取消定时器alarm(0),返回旧闹钟余下秒数。
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
printf("%d\n", alarm(10)); //设定闹钟5秒后发送闹钟信号
sleep(1); //睡眠1秒,此时闹钟还剩9秒
printf("%d\n", alarm(3)); //打印剩余的9秒,设新闹钟3秒后发送闹钟信号
pause(); //为了不让进程结束,等待SIGALRM信号产生,产生之后结束当前进程
return 0;
}
4.信号处理函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:信号处理函数
参数:signum:要处理的信号
handler:信号处理方式
SIG_IGN:忽略信号 (忽略 ignore)
SIG_DFL:执行默认操作 (默认 default)
handler:捕捉信号 (handler为函数名,可以自定义)
void handler(int sig){} //函数名可以自定义, 参数为要处理的信号
返回值:成功:设置之前的信号处理方式
失败:-1
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void handler(int sig) //参数sig代表要处理的信号
{
if (sig == SIGINT)
printf("ctrl C: %d\n", sig);
else if (sig == SIGTSTP)
printf("ctrl Z: %d\n", sig);
}
int main(int argc, char const *argv[])
{
signal(SIGINT, SIG_IGN); //对SIGINIT信号设置忽略方式处理
//signal(SIGINT,SIG_DFL); //对SIGINIT信号设置缺省方式处理,也就是默认操作
signal(SIGINT, handler); //对SIGINIT信号设置捕捉方处理,也就是自定义处理方式
signal(SIGTSTP, handler);
//while (1); //为了让进程不要结束,因为等到信号真的来才能验证,不然进程就结束了
pause(); //收到被捕获处理的信号时会结束挂起
return 0;
}
5.信号的处理过程
用信号的知识实现司机和售票员问题。
1)售票员捕捉SIGINT(代表开车)信号,向司机发送SIGUSR1信号,司机打印(let's gogogo)
2)售票员捕捉SIGQUIT(代表停车)信号,向司机发送SIGUSR2信号,司机打印(stop the bus)
3)司机捕捉SIGTSTP(代表到达终点站)信号,向售票员发送SIGUSR1信号,售票员打印(please get off the bus)
4)司机等待售票员下车,之后司机再下车。
分析:司机(父进程)、售票员(子进程)
司机:
忽略的信号:SIGINT、SIGQUIT
捕捉的信号:SIGUSR1、SIGUSR2、SIGTSTP
售票员:
忽略的信号:SIGTSTP
捕捉的信号:SIGINT、SIGQUIT、SIGUSR1
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
pid_t pid;
void saler(int sig)
{
if (sig == SIGINT)
kill(getppid(), SIGUSR1);
if (sig == SIGQUIT)
kill(getppid(), SIGUSR2);
if (sig == SIGUSR1)
{
printf("pls get off the bus!\n");
exit(0);
}
}
void driver(int sig)
{
if (sig == SIGUSR1)
printf("let's gogogo!!!~~\n");
if (sig == SIGUSR2)
printf("stop the bus!\n");
if (sig == SIGTSTP)
{
kill(pid, SIGUSR1);
wait(NULL);
exit(0); //司机等待售票员下车之后再下车
}
}
int main(int argc, char const *argv[])
{
pid = fork();
if (pid < 0)
{
perror("fork err");
return -1;
}
else if (pid == 0)
{
printf("i am saler!\n");
signal(SIGINT, saler);
signal(SIGQUIT, saler);
signal(SIGUSR1, saler);
signal(SIGTSTP, SIG_IGN);
}
else
{
printf("i am driver!\n");
signal(SIGUSR1, driver);
signal(SIGUSR2, driver);
signal(SIGTSTP, driver);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
}
while (1)
pause(); //不能发送一个信号就结束进程,所以可以循环挂起不占用CPU
return 0;
}