文章目录
ToDo
待整理信号屏蔽 和 未决信号。
当信号发生时, 信号处理函数在执行时,又有新的信号产生时,分两种情况:
相同信号:排队处理
不同信号:会中断当前信号处理,执行新的信号处理函数,处理完后再执行原来的信号处理函数
什么是信号?
是一种软中断,由内核传递到目标进程 或 通过系统调用(kill、raise)发送信号。
查看系统中所有信号
fzf@fzf:~/work/DailyTest/C$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
其中:
1 ~ 31叫非实时信号,可能会丢失,不支持排队
31往后的信号(到64):实时信号,支持排队,发送的多个实时信号都会被接收
ctrl + c:触发SIGINT
kill、killall:默认触发SIGTERM
ctrl + z:触发SIGTSTP
信号的几种处理方式
- 默认动作,大部分动作是中止进程;
- 忽略:指定后如果有信号发生,则进程忽略处理;
- 捕获:执行信号处理接口;
其中:SIGKILL和SIGSTOP信号不能被捕获、也不能被忽略。
注册信号处理函数
使用signal函数
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal() sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL, or the address of a programmer-defined function (a "signal handler").
* If the signal signum is delivered to the process, then one of the following happens:
* If the disposition is set to SIG_IGN, then the signal is ignored.
* If the disposition is set to SIG_DFL, then the default action associated with the signal (see signal(7)) occurs.
* If the disposition is set to a function, then first either the disposition is reset to SIG_DFL, or the signal is blocked (see Portability below), and then handler is called with argument signum. If invocation of the handler caused the signal to be blocked, then the signal is unblocked upon return from the handler.
The signals SIGKILL and SIGSTOP cannot be caught or ignored.
RETURN VALUE
signal() returns the previous value of the signal handler, or SIG_ERR on error. In the event of an error, errno is set to indicate the cause.
示例代码:
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
//定义信号处理函数, signo表示信号值
void sig_handler(int signo)
{
printf("pid: %d, signal %d occured\n", getpid(), signo);
}
int main(void)
{
/*
向内核登记信号值以及信号处理函数
忽略此信号时, 第二个参数传"SIG_IGN
*/
if (signal(SIGTSTP, sig_handler) == SIG_ERR) { //ctrl + z
perror("signal sigtstp error");
}
if (signal(SIGINT, sig_handler) == SIG_ERR) { //ctrl + c
perror("signal sigint error");
}
/*
SIGKILL, SIGSTOP不能被忽略, 也不能被捕获
*/
if (signal(SIGKILL, sig_handler) == SIG_ERR) {
perror("signal sigkill error");
}
if (signal(SIGSTOP, sig_handler) == SIG_ERR) {
perror("signal sigstop error");
}
if (signal(SIGKILL, SIG_IGN) == SIG_ERR) {
perror("ingore signal sigkill error");
}
if (signal(SIGSTOP, SIG_IGN) == SIG_ERR) {
perror("ignore signal sigstop error");
}
while (1) {
sleep(5);
}
return 0;
}
使用sigaction函数
struct sigaction {
void (*sa_handler)(int); //函数指针, 信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask; //信号集,可以屏蔽信号
int sa_flags; //SA_RESTART
void (*sa_restorer)(void);
};
示例代码:
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
static void sig_handler(int signo)
{
printf("Received signal(%d)\n", signo);
}
int main(int argc, char *argv[])
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_handler;
int ret = sigaction(SIGINT, &sa, NULL);
if (ret != 0) {
perror("sigaction sigint failed.");
}
ret = sigaction(SIGKILL, &sa, NULL);
if (ret != 0) {
perror("sigaction sigkill failed.");
}
ret = sigaction(SIGSTOP, &sa, NULL);
if (ret != 0) {
perror("sigaction sigstop failed.");
}
sa.sa_handler = SIG_IGN;
ret = sigaction(SIGKILL, &sa, NULL);
if (ret != 0) {
perror("sigaction sigkill failed.");
}
ret = sigaction(SIGSTOP, &sa, NULL);
if (ret != 0) {
perror("sigaction sigstop failed.");
}
while (1) {
sleep(5);
}
return 0;
}
SIGCHLD信号(子进程退出产生该信号)
子进程结束时产生该信号,父进程需要使用wait调用来等待子进程结束并回收它
避免出现僵尸进程
示例代码:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
void sig_handler(int signo)
{
printf("child process deaded, signo: %d\n", signo);
//当父进程捕获到SIGCHLD信号后要调用wait函数回收子进程,否则子进程会成为僵尸进程
wait(NULL);
}
void out(int num)
{
int i = 0;
while (i < num) {
printf("pid: %d running %d\n", getpid(), i++);
sleep(2);
}
}
int main(void)
{
if (signal(SIGCHLD, sig_handler) == SIG_ERR) {
perror("signal sigchld error");
}
pid_t pid = fork();
if (pid < 0) {
perror("fork error");
exit(1);
} else if (pid > 0) { //parent process
out(100);
} else { //child process
out(10);
}
return 0;
}
信号发送
除了内核和超级用户,并不是每个进程都可以向其他的进程发送信号
一般进程向相同uid 和 gid 的进程发送信号
kill函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
#返回值:returns 0 on success, and nonzero for failure
参数pid取值:
- pid > 0:将信号发给进程ID为pid的进程
- pid = 0:将信号发给与发送进程同一进程组的所有进程
- pid < 0:将该信号发送给进程组ID等于pid的绝对值
- pid = -1:将该信号发送给发送进程有权限向他们发送信号的系统上的所有进程
当sig为0时,用来判断pid代表的进程是否存在
示例代码:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
//定义信号处理函数,signo表示信号值
void sig_handler(int signo)
{
printf("pid: %d, signal %d occured\n", getpid(), signo);
}
int main(void)
{
//向内核登记信号值以及信号处理函数
if (signal(SIGINT, sig_handler) == SIG_ERR) { //ctrl + c
perror("signal sigint error");
}
int exist = kill(getpid(), 0); //判断该进程是否存在
printf("process exist: %d\n", exist);
int i = 0;
while (i < 20) {
printf("%d running %d\n", getpid(), i++);
if (i == 10) {
//kill(getpid(), SIGKILL); //把自己杀死
kill(getpid(), SIGINT);
}
sleep(1);
}
return 0;
}
raise函数
给正在执行的进程发信号
#include <signal.h>
int raise(int sig);
示例代码:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void sig_handler(int signo)
{
if (signo == SIGTSTP) {
printf("SIGTSTP occured!\n");
}
}
int main(void)
{
if (signal(SIGTSTP, sig_handler) == SIG_ERR) {
perror("signal sigtstp error");
}
printf("begin main running\n");
int i = 0;
while (i < 20) {
printf("%d running %d\n", getpid(), i++);
if (i == 10) {
raise(SIGTSTP);
}
sleep(1);
}
printf("end main running\n");
return 0;
}
alarm函数
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
返回:0 或 以前设置的定时器时间余留秒数
由内核产生SIGALRM信号
参数为0,取消以前设置的定时器
示例代码:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
void sig_handler(int signo)
{
if (signo == SIGALRM) {
system("clear");
time_t t = time(NULL);
struct tm *timeInfo = localtime(&t);
printf("%02d:%02d:%02d\n", timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
alarm(1);
}
}
int main(void)
{
if (signal(SIGALRM, sig_handler) == SIG_ERR) {
perror("signal sigalrm error");
}
int ret = alarm(5);
ret = alarm(1);
printf("ret is %d\n", ret);
while (1) {
pause();
}
return 0;
}
信号集
sigset_t
本质上是128位的集合,每一位代表一个信号
//清空信号集(每一位置为0)
int sigemptyset(sigset_t *);
//添加信号到信号集中(该位置1)
int sigaddset(sigset_t *, int);
//删除信号集中的信号(该位置0)
int sigdelset(sigset_t *, int);
//信号集中的信号全置为1
int sigfillset(sigset_t *);
//判断信号是否在信号集中(该位是否为1)
int sigismember(const sigset_t *, int);
示例代码:
#include <stdio.h>
#include <signal.h>
void Test(const sigset_t *set, int signo)
{
if (sigismember(set, signo) == 1) {
printf("signo(%d)在信号集中\n", signo);
} else {
printf("signo(%d)不在信号集中\n", signo);
}
}
int main(void)
{
sigset_t set;
sigemptyset(&set);
Test(&set, 2);
Test(&set, 3);
sigaddset(&set, 2);
Test(&set, 2);
Test(&set, 3);
sigfillset(&set);
Test(&set, 2);
Test(&set, 3);
sigdelset(&set, 3);
Test(&set, 2);
Test(&set, 3);
return 0;
}
信号屏蔽 与 未决信号
int sigpending(sigset_t *); //得到未决状态的信号
int sigprocmask(int, const sigset_t *restrict, sigset_t *restrict); //设置屏蔽信号