一、信号的初步认识
1、特点
- 简单
- 携带的信息量少
- 使用在某个特定的场景中
2、信号的状态
- 产生
- 键盘:ctrl + c
- 命令:kill
- 系统函数:kill
- 软条件:定时器
- 硬件:段错误,除0错误
- 未决状态 -- 没有被处理
- 递达 - 信号被处理了
- 忽略
- 捕捉
- 执行了默认动作
3、阻塞信号集、未决信号集
阻塞信号集合未决信号集在pcb中,用户不能直接操作,阻塞信号集里存放的是要屏蔽的信号,如果不想让进程处理某个信号就可以将该信号放入阻塞信号集里。
没有被处理的信号的集合叫未决信号集。
二、信号相关函数
1、kill -- 发送信号给指定进程
函数原型:
int kill(pid_t pid, int sig);
pid参数:
pid = 0;发送信号给与调用kill函数进程属于同一进程组的所有进程
pid < -1; 取|pid|发给对应进程组
pid = -1;发送给进程有权限发送的系统中所有进程
示例:
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <signal.h>
int main()
{
pid_t pid = fork();
if(pid > 0)
{
while(1)
{
printf("paren pid = %d",getpid());
sleep(1);
}
}
else if(pid == 0)
{
sleep(3);
kill(getppid(),SIGKILL);
}
return 0;
}
2、raise -- 自己给自己发信号
函数原型:
int raise(int sig);
示例:
# include <stdio.h>
# include <signal.h>
# include <stdlib.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid > 0)
{
int s;
pid_t wpid = wait(&s);
printf("child died pid = %d\n",wpid);
if(WIFSIGNALED(s))
{
printf("died by signal:%d\n",WTERMSIG(s));
}
}
else if(pid == 0)
{
raise(SIGINT);
}
}
3、abort -- 给自己发送异常终止信号
6号信号:SIGABORT--调用abort函数时产生该信号,终止进程并产生core文件。
函数原型:
void abort(void);
没有参数,没有返回值,永远不会调用失败。
示例;
# include <stdio.h>
# include <signal.h>
# include <stdlib.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid > 0)
{
int s;
pid_t wpid = wait(&s);
printf("child died pid = %d\n",wpid);
if(WIFSIGNALED(s))
{
printf("died by signal:%d\n",WTERMSIG(s));
}
}
else if(pid == 0)
{
while(1)
{
abort();
}
}
}
4、闹钟
- alarm--设置定时器(每个进程只有一个定时器),使用自然定时法,不受进程状态影响
- 14号信号:SIGALRM--定时器超时,超时的时间有alarm系统调用设置,信号的操作是终止进程。
函数原型:
unsigned int alarm(unsigned int seconds);
//函数返回上一次闹钟剩余时间,如果是第一次设置闹钟则返回0
示例:
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
int main()
{
int ret = alarm(5);
printf("ret = %d\n",ret); //ret为0,因为是第一次设置闹钟
ret = alarm(2); //ret为5,因为第二次设置的闹钟覆盖了第一次设置的闹钟,第一次闹钟剩余时间还剩5
printf("ret = %d\n",ret);
while(1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
5、setitimer -- 定时器,并实现周期性定时
函数原型:
int setitimer(int which,
const struct itimerval *new_value,
struct itimerval *old_value);
which:定时法则
1、ITIMER_REAL
2、ITIMER_VIRTUAL
3、ITIMER_PROF
struct itimerval
{
struct timeval it_interval; //定时器循环周期
struct timeval it_value; //第一次触发定时器的时间
};
struct timeval
{
time_t tv_sec; //秒
suseconds_t tv_usec; //微秒
};
示例:
# include <stdio.h>
# include <sys/time.h>
# include <unistd.h>
int main()
{
struct itimerval new_value;
new_value.it_value.tv_sec = 2;
new_value.it_value.tv_usec = 0;
new_value.it_interval.tv_sec = 1;
new_value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL,&new_value,NULL);
while(1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
三、信号集
1、概念
- 未决信号集:没有被当前进程处理的信号
- 阻塞信号集:将某个信号放到阻塞信号集,这个信号就不会被处理,阻塞解除之后,信号被处理
2、信号处理流程
- 信号产生,信号处于未决状态,进程收到信号之后,信号被放入未决信号集
- 放入未决信号集中的信号等待处理,在处理之前需要做一件事情:判断阻塞信号集中该信号对应的标志位是否为1,如果为1,不处理,如果为0,处理该信号
3、自定义信号集
- int sigemptyset(sigset_t *set);将set集合置空
- int sigfillset(sigset_t set);将所有信号加入set集合
- int sigaddset(sigset_t *set,int signo);将signo信号加入到set信号集
- int sigdelset(sigset_t *set,int signo);从set集合中移除signo信号
- int sigismember(const sigset_t *set,int signo);判断信号是否存在
4、sigprocmask函数
作用:屏蔽and杰出信号屏蔽,将自定义信号集设置给阻塞信号集
函数原型:int sigproxmask(int how,const sigset_t,sigset_t *oldset);
参数how:
- SIG_BLOCK:当how设置为此值,set表示需要屏蔽的信号。相当于mask = mask | set
- SIG_UNBLOCK:当how设置为此值,set表示需要解除屏蔽的信号。相当于mask = mask & ~set
- SIGSETMASK:当how设置为此值,set表示用于替代原始屏蔽集的新屏蔽集。相当于mask = set。若调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,至少其中一个信号递达。
5、sigpending -- 读取当前进程未决信号集
函数原型:int sigpending(sigset_t set);
参数:set -- 内核将未决信号集写入set
6、示例
- 设置阻塞信号集并读取当前进程的未决信号集
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <signal.h>
int main()
{
//定义自定义信号集变量
sigset_t myset;
//自定义信号集置空
sigemptyset(&myset);
//添加要屏蔽的信号
sigaddset(&myset,SIGINT);
sigaddset(&myset,SIGQUIT);
sigaddset(&myset,SIGKILL);
//修改内核中阻塞信号集
sigprocmask(SIG_BLOCK,&myset,NULL);
//每隔一秒读取阻塞信号集
while(1)
{
sigset_t pengset;
sigpending(&pengset);
int i = 0;
for(i = 1; i < 32; i++)
{
if(sigismember(&pengset,i))
{
printf("1");
}
else
{
printf("0");
}
}
printf("\n");
sleep(1);
}
return 0;
}
四、信号捕捉
1、signal函数
- typedef void (*sighandler_t)(int);
- sighandler_t signal(int signum, sighandler_t handler);
2、sigaction函数
函数原型:
int sigaction(int signum, //捕捉的信号
const struct sigaction *act,
struct sigaction *oldact //一般置NULL);
struct sigaction
{
void (*sa_handler)(int); //捕捉到信号后对应的操作
void (*sa_sigaction)(int,siginfo_t*,void*); //无需做处理
sigset_t sa_mask; //在信号处理函数执行过程中,临时屏蔽指定的信号
int sa_flags; //0
void (*sa_restorer)(void); //以废弃,不用做处理
}
示例:
# include <stdio.h>
# include <stdlib.h>
# include <signal.h>
# include <unistd.h>
int main()
{
//捕捉到信号所做的操作
void myfunc(int no)
{
printf("hello world");
sleep(3);
}
//定义 sigaction函数的第二个参数变量
struct sigaction act;
//设置sigaction函数第二个参数结构体中各个参数
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
//设置在myfunc函数执行过程中要屏蔽的信号
sigaddset(&act.sa_mask,SIGQUIT);
act.sa_handler = myfunc;
//捕捉信号
sigaction(SIGINT,&act,NULL);
while(1)
{
printf("run\n");
sleep(1);
}
return 0;
}