二、信号的捕获和处理
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:向内核注册一个信号处理函数
signum:信号的编号,可以直接写数字,也可以使用系统提供的宏。
handler:函数指针
SIG_IGN 忽略信号
SIG_DFL 恢复信号默认的处理方式
返回值:是之前信号处理方式
函数指针、SIG_IGN、SIG_DFL、SIG_ERR
#include
#include
void sigsegv(int num)
{
printf("我捕获了一个信号%d\n",num);
exit(0);
}
void sigfpe(int num)
{
printf("我捕获了一个信号%d\n",num);
exit(0);
}
int main()
{
signal(SIGSEGV,sigsegv);
signal(SIGFPE,sigfpe);
int* p = 0x01020304;
//*p = 100;
int num = 0;
int num1 = 100;
int i = num1/num;
for(;;);
return 0;
}
练习:实现一个杀不死的程序。
#include
#include
#include
int main()
{
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
while(1)
{
printf("----\n");
sleep(1);
}
return 0;
}
1、在有些系统中向内核注册的信号处理函数只执行一次(在执行前就被恢复成默认的处理方式),如果想持续处理信号,可以在每次的处理函数结束时再次注册。
2、SIGKILL、SIGSTOP不能被捕获、也不能被忽略。
3、普通用户只能给自己的进程发信号,超级用户可以能任意进程发送信号。
三、子进程的信号处理
1、通过fork创建的子进程会继承父进程的信号处理方式。
2、通过vfork+exec创建的子进程不会继承父进程的信号处理方式,会恢复成默认的。
#include
#include
#include
void sigsegv(int signum)
{
printf("我捕获到一个段错误,信号编号是%d\n",signum);
}
int main()
{
if(SIG_ERR==signal(SIGSEGV,sigsegv))
{
perror("signal");
return -1;
}
if(0 == fork())
{
printf("我是进程%d ,我的父进程是%d\n",getpid(),getppid());
for(;;);
}
printf("我是进程%d ,我的父进程是%d\n",getpid(),getppid());
for(;;);
}
四、信号的发送
1、键盘
Ctrl+c 终端中断信号
Ctrl+z 终端暂停信号,fg命令再次开启
Ctrl+/ 终端退出信号
2、错误产生的信号
除0
非法内存访问
硬件总线
3、命令产生的信号
kill -信号 进程号
killall -信号 程序名(杀死所有同名的进程)。
4、函数产生的信号
int kill(pid_t pid, int sig);
功能:向指定的进程发送信号
pid:与waitpid一样
sig:信号
0表示空信号,不会向进程发送信号,但是会测试是否能向pid发送信号,这样可以检测一个进程是否存在,返回-1表示进程不存在,errno为ESRCH。
返回值:-1,说明进程不存在
int raise(int sig);
功能:向自己发送信号
练习:实现kill命令的功能。
#include
#include
#include
#include
int main(int argc, char *argv[])
{
if(argc <= 2)
{
printf("参数太少了\n");
return -1;
}
int pid = atoi(argv[2]);
int sig = atoi(argv[1]);
kill(pid,sig);
printf("%d进程已经死亡\n",pid);
return 0;
}
五、pause
#include
int pause(void);
功能:休眠
1、进程调用了pause函数后会进程睡眠状态,直到有信号把它叫醒(不被忽略的信号)。
2、当信号来临后,先执行信号处理函数,信号处理函数结束后pause再返回。
3、pause函数要么不返回(一直睡眠),要么返回-1,并且修改errno的值。
4、从功能上来讲它相当于没有时间限制的sleep函数。
#include
#include
#include
void sigsegv(int signum)
{
printf("我收到了信号%d\n",signum);
getchar();
}
int main()
{
signal(SIGSEGV,sigsegv);
printf("我是进程%d,我要睡觉了\n",getpid());
pause();
perror("pause");
printf("我睡醒了,呵呵呵呵...\n");
}
六、sleep
#include
unsigned int sleep(unsigned int seconds);
功能:使用调用的进程睡眠seconds秒
1、调用sleep的进程如果没有睡眠足够的秒数,除非收到信号后才会返回。
2、sleep的返回值是0,或剩余的睡眠秒数。
3、相当于有时间限制的pause
int usleep(useconds_t usec);v
功能:睡眠usec微秒
1秒=1000毫秒=1000000微秒。
它是一种更精确的睡眠函数。
-std=c99 不建议使用
-std=gnu99 在使用系统调用时一定要使用此标准
七、alarm
#include
unsigned int alarm(unsigned int seconds);
功能:定时一个闹钟信号
1、让内核向调用它的进程,在seconds秒后发送一个SIGALRM信号。
2、SIGALRM信号的默认处理方式是直接退出。
练习:实现一个闹钟命令,./alarm sec
八、信号集和信号屏蔽
1、信号集:
多个信号的集合,sigset_t
由128个二进制位组成,每个二进制位表示一个信号
int sigemptyset(sigset_t *set);
功能:清空信号集
int sigfillset(sigset_t *set);
功能:填满信号信
int sigaddset(sigset_t *set, int signum);
功能:向信号集中添加信号
int sigdelset(sigset_t *set, int signum);
功能:从信号集中删除信号
int sigismember(const sigset_t *set, int
signum);
功能:测试一个信号集中是否有某个信号
返回值:有返回1,没有返回0,失败返回-1
2、屏蔽信号集中的信号
每个进程都有一个信号掩码(signal mask),它就是一个信号集,里面包含了进程所屏蔽的信号。
int sigprocmask(int how, const sigset_t
*set, sigset_t *oldset);
功能:设置进程的信号掩码(信号屏蔽码)
how:修改信号掩码的方式
SIG_BLOCK:向信号掩码中添加信号
SIG_UNBLOCK:从信号掩码中删除信号
SIG_SETMASK:用新的信号集替换旧的信号掩码
newset:新添加、删除、替换的信号集,也可以为空
oldset:获取旧的信号掩码
当newset为空时,就是在备份信号掩码
当进程执行一些敏感操作时不希望被打扰(原子操作),此需要向屏蔽信号。
屏蔽信号的目的不是为了不接收信号,而是延时接收,当处理完要做的事情后,应该把屏蔽的信号还原。
当信号屏蔽时发生的信号会记录一次,这个信号设置为末决状态,当信号屏蔽结束后,会再发送一次。
不可靠信号在信号屏蔽期间无论信号发生多少次,信号解除屏蔽后,只发送一次。
可靠信号在信号屏蔽期间发生的信号会排队记录,在信号解除屏蔽后逐个处理。
在执行处理函数时,会默认把当前处理的信号屏蔽掉,执行完成后再恢复
int sigpending(sigset_t *set);
功能:获取末决状态的信号
九、信号处理sigaction signal
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
功能:设置或获取信号处理方式
struct sigaction {
// 信号处理函数指针
void (*sa_handler)(int);
// 信号处理函数指针 需要使用sigqueue发送信号
void (*sa_sigaction)(int, siginfo_t *, void *);
// 信号屏蔽码
sigset_t sa_mask;
int sa_flags;
SA_NOCLDSTOP:忽略SIGCHLD信号
SA_NODEFER/SA_NOMASK:在处理信号时不屏蔽信号
SA_RESETHAND:处理完信号后,恢复系统默认处理方式
SA_RESTART:当信号处理函数中断的系统调用,则重启系统调用。
SA_SIGINFO:用sa_sigaction处理信号
// 保留
void (*sa_restorer)(void);
};
int sigqueue(pid_t pid, int sig, const union sigval value);