Linux信号
进程信号
基本认识:信号是进程之间事件异步通知的一种方式, 属于软中断
信号种类
kill -l 命令查看linux信号(需在root用户下才能看到全部信号)
共62种信号, 分为非可靠信号和可靠信号
- 非可靠信号为 1~32
- 可靠信号为 34 ~ 64
信号的生命周期
信号产生 —》信号再进程中注册 —》信号再进程中注销 —》信号处理
信号产生
硬件:ctrl + c ctrl + | ctrl+z
软件: kill -signo pid 命令 kill() raise() abort() alarm() 程序异常
core dumped --核心转储 程序异常退出时保存程序运行信息 方便事后调试
core dumped --默认关闭 因为其占磁盘资源, 安全性考虑
ulimit -a 查看core dump开启状态
ulimit -c 1024 设置核心转储文件大小, 开启核心转储
调试:gdb ./loop —> core-file core.pid —> bt
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
sleep(2);
int kill(pid_t pid, int sig);
//给指定进程发送指定信号
//产生段错误
kill(getpid(), SIGSEGV);
int raise(int sig);
//给调用进程发送指定信号
raise(SIGQUIT);
void abort(void);
//给调用进程发送SIGABRT
//异常终止一个进程
abort();
//unsigned int alarm(unsigned int seconds);
//seconds秒之后给调用进程发送SIGALRM信号
//seconds==0表示取消定时器
//返回值上一个定时器剩余的时间或0
int ret = alarm(3);
sleep(4);
return 0;
}
信号在进程中的注册
-
非可靠信号注册
判断pending信号集合位图相应位是否为1;若为0, 为信号阻止sigqueue节点, 添加到链表中,并且pending位图置1(信号已经注册过还没有被处理), 则什么都不做(等于丢掉) -
可靠信号注册
不管位图是否为1,阻止节点,添加到链表中,并且位图置1(信号不会被丢掉)
信号的注销
-
非可靠信号注销
因为非可靠信号的信号节点只有一个,因此删除节点,位图直接置0
-
可靠信号
因为可靠信号的信号节点有可能会有多个,若还有相同信号节点,则位图依然置1,否则置0
信号的处理
- 默认处理方式 SIG_DFL
- 忽略处理方式 SIG_IGN
- 自定义处理方式 void (*sighandler_t)(int)
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
// 自定义信号
void handler() {
printf("--------\n");
}
int main() {
// ctrl + c 触发信号
signal(SIGINT, handler);
while(1) {
printf("hhhhhh\n");
sleep(5);
}
return 0;
}
- 自定义信号及信号捕捉实例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
// act 为新信号 oldact保存老信号
struct sigaction act, oldact;
void sigcb(int signo){
printf("recv signo:%d\n", signo);
sigaction(signo, &oldact, NULL);
}
int main(){
// 为SIGINT信号自定义处理方式
act.sa_handler = sigcb;
act.sa_flags = 0;
// 清空信号集合set
sigemptyset(&act.sa_mask);
//int sigaction(int signum,struct sigaction *act,struct sigaction *oldact);
//使用act动作替换signum原有的处理动作,并且将原有处理动作拷贝到oldact中
sigaction(SIGINT, &act, &oldact);
while(1){
printf("-------\n");
sleep(1);
}
return 0;
}
// 该实例是通过oldact保存原有SIGINT信号处理方式, 通过一次替换后, 还原原始信号操作
信号的阻塞
暂时阻止信号被递达 信号依然可以注册, 但是只是暂时不处理,解除阻塞之后才会处理
信号的递达:动作 -> 信号的处理
信号的未决:状态 -> 信号从产生到处理之间所处的状态
阻塞原理:在PCB的blocked 信号阻塞集合中标记暂时不处理信号(将blocked位图集合中对应的位 置1, 表示阻塞这个信号)
注意: 有两个信号SIGKILL 9 SIGSTOP 19无法被阻塞, 无法自定义,无法被忽略
API | 功能 |
---|---|
int sigprocmask(int how, const sigset_t *set, sigset_t *oset); | 阻塞解除阻塞信号 |
int sigemptyset(sigset_t *set); | 清空信号集合 |
int sigfillset(sigset_t *set); | 向集合添加所有信号 |
int sigaddset (sigset_t *set, int signo); | 向集合添加指定信号 |
int sigdelset(sigset_t *set, int signo); | 从集合中移除执行信号 |
int sigismember(const sigset_t *set, int signo); | 判断信号是否在集合中 |
int sigpending(sigset_t *set); | 获取未决信号 |
-
信号阻塞实例(实现sleep函数)
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> void sigcb2(int signo) { sleep(10); } void sigcb(int signo) { } int mysleep(int nsec) { signal(SIGALRM, sigcb); sigset_t set, old; sigemptyset(&set); sigprocmask(SIG_BLOCK, &set, &old); alarm(nsec); sigprocmask(SIG_UNBLOCK, &set, &old); //int sigsuspend(const sigset_t *mask); //临时使用mask替换阻塞集合,陷入休眠,被唤醒之后阻塞集合还原 sigset_t mask; sigfillset(&mask); sigdelset(&mask, SIGALRM);//除了SIGALRM所有信号 //这时候除了SIGALRM,所有信号都被阻塞,陷入休眠,只有SIGALRM能够唤醒这个休眠 sigsuspend(&mask); sigprocmask(SIG_SETMASK, &old, NULL); signal(SIGALRM, SIG_DFL); } int main() { signal(SIGQUIT, sigcb2); mysleep(3); printf("------\n"); return 0; }
-
volatile作用
保持内存的可见性,告知编译器,被该关键字修饰的变量, 不允许被优化, 对该变量的任何操作,都必须在真实的内存中进行操作
-
SIGCHLD信号的妙用
在之前我们使用轮询的方式等待子进程结束后清理, 现在由于我们知道进程终止和退出会发出一个SIGCHLD信号, 该信号的处理方式是默认忽略,所以我们使用自定义该信号处理方式来实现,自动对退出后的子进程的清理工作
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <stdlib.h> #include <sys/wait.h> void handler(){ pid_t id; while( (id = waitpid(-1, NULL, WNOHANG)) > 0 ){ printf("wait child success: %d\n", id); } printf("ok, child is quit %d\n", getpid()); } int main(){ signal(SIGCHLD, handler); pid_t pid; pid = fork(); if (pid < 0){ return -1; } else if (pid == 0){ printf("child: %d\n", getpid()); sleep(3); exit(1); } while(1){ printf("parent can do anything!\n"); sleep(1); } return 0; }