目录
1 信号集、信号的阻塞
2 信号集操作函数
2.1 自定义信号集
2.2 清空信号集
2.3 全部置1
2.4 将一个信号添加到集合中
2.5 将一个信号从集合中移除
2.6 判断一个信号是否在集合中
2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞)
2.8 使进程挂起(暂停执行)直到收到一个信号
2.9 更改进程的信号屏蔽字,并等待一个特定的信号到来
掌握:信号集和信号屏蔽
1 信号集、信号的阻塞
应用:
有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延
时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现。
信号的阻塞概念:
信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。
信号的状态:
信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)
信号未决(Pending):从产生到递达之间的状态(挂起)
下图表示了,每一个任务控制块都有一个挂起信号集(可读)和信号屏蔽字(可写可读)。信号屏蔽置1,代表忽略该信号的到来
2 信号集操作函数
2.1 自定义信号集
sigset_t set;
自定义信号集。 是一个 64bit 128bit的数组。
2.2 清空信号集
sigemptyset(sigset_t *set);
代表接收所有信号
2.3 全部置1
sigfillset(sigset_t *set);
接收全部信号
2.4 将一个信号添加到集合中
sigaddset(sigset_t *set, int signum);
接收某一个位的信号
2.5 将一个信号从集合中移除
sigdelset(sigset_t *set, int signum);
屏蔽某一个位的信号
2.6 判断一个信号是否在集合中
sigismember(const sigset_t *set,int signum);
2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞)
#include <signal.h>
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );
参数说明:
how
:表示如何修改信号屏蔽字的标志,可取以下值:SIG_BLOCK
:将set
中指定的信号添加到当前信号屏蔽字中。SIG_UNBLOCK
:将set
中指定的信号从当前信号屏蔽字中移除。SIG_SETMASK
:用set
中指定的信号集替换当前信号屏蔽字。
set
:指向要修改的新的信号屏蔽字的指针。如果为NULL
,则不会修改信号屏蔽字。oset
:用于保存旧的信号屏蔽字的指针。如果为NULL
,则不会保存旧的信号屏蔽字。
函数返回值:
- 成功:返回0,并将旧的信号屏蔽字保存到
oset
中(如果oset
不为NULL
)。 - 失败:返回-1,并设置
errno
来指示错误的原因。
该函数用于在多线程编程、信号处理等场景中控制信号的屏蔽,以保证信号处理的正确性和可靠性。
示例:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void handle(int sig){printf("I get sig=%d\n",sig);}int main(){struct sigaction act;act.sa_handler = handle; //注册回调函数sigemptyset(&act.sa_mask); //清空信号集,代表接收所有信号act.sa_flags = 0; //通过将其设置为0,表示不使用任何额外的标志位,即默认行为。sigaction(SIGINT,&act,NULL); //信号捕获sigset_t set; //定义信号集sigemptyset(&set); //清空信号集sigaddset(&set,SIGINT); //SIGINT,屏蔽SIGINT信号sigprocmask(SIG_BLOCK,&set,NULL); //ctrl+c的信号阻塞,接收不到sleep(5);sigprocmask(SIG_UNBLOCK,&set,NULL); //取消信号阻塞while(1){sleep(1);}}
执行效果 :
2.8 使进程挂起(暂停执行)直到收到一个信号
int pause(void);
进程一直阻塞,直到被信号中断,返回值:-1 并设置errno为EINTR
函数行为:
-1 如果信号的默认处理动作是终止进程,则进程终止,pause函数没有机会返回。
-2 如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause函数不返回
-3 如果信号的处理动作是捕捉,则调用完信号处理函数之后,pause返回-1。
-4 pause收到的信号如果被屏蔽,那么pause就不能被唤醒
示例:pause阻塞效果
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char * argv[])
{pause();printf("after pause\n");
}//运行后结果程序阻塞中,未打印after pause,按ctrl+c或ctrl+\结束
linux@linux:~/Desktop$ ./pause
示例:pause阻塞效果2
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void handle(int sig)
{printf("Handle:I get sig=%d\n",sig);
}int main(int argc, char * argv[])
{struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);pause();printf("after pause\n");
}//运行结果,未恢复至原处理函数的执行效果
linux@linux:~/Desktop$ ./pause
^CHandle:I get sig=2
after pause
linux@linux:~/Desktop$
示例:pause阻塞效果3 ,实现一个等待信号的任务
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void handle(int sig)
{printf("Handle:I get sig=%d\n",sig);
}void mytask()
{printf("mytask:start\n");sleep(3);printf("mytask:end\n");
}int main(int argc, char * argv[])
{struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);pause();while(1){mytask(); pause(); //每次等待信号到来再执行mytask}printf("After pause\n");
} //运行结果
linux@linux:~/Desktop$ ./pause
^CHandle:I get sig=2
mytask:start
mytask:end
^CHandle:I get sig=2
mytask:start
mytask:end
^CHandle:I get sig=2
mytask:start
mytask:end
使用kill 发送SIGHUP信号关闭
示例:pause阻塞效果4 ,实现一个等待多个信号的任务(SIGINT,SIGHUP)
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void handle(int sig)
{printf("Handle:I get sig=%d\n",sig);
}void mytask()
{printf("mytask:start\n");sleep(3);printf("mytask:end\n");
}int main(int argc, char * argv[])
{struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);sigaction(SIGHUP,&act,NULL); pause();while(1){mytask();pause();}printf("After pause\n");
}
按ctrl+c,或者使用kill -1发送关闭信号都可以捕获信号处理任务。
示例:使用 sigaddset,屏蔽多个信号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void handle(int sig)
{printf("Handle:I get sig=%d\n",sig);
}void mytask()
{printf("mytask:start\n");sleep(3);printf("mytask:end\n");
}int main(int argc, char * argv[])
{struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);sigaction(SIGHUP,&act,NULL);sigset_t set;sigaddset(&set,SIGHUP);sigaddset(&set,SIGINT); pause();while(1){sigprocmask(SIG_BLOCK,&set,NULL);mytask();sigprocmask(SIG_UNBLOCK,&set,NULL);pause();}printf("After pause\n");
}
执行效果,在任务过程中设置了信号屏蔽,此时按ctrl+c或者kill -1去发送信号,不会执行handle打印,直到任务结束后再开启接收信号,防止任务被打断
2.9 更改进程的信号屏蔽字,并等待一个特定的信号到来
设定对信号集内的信号屏蔽并恢复。
int sigsuspend(const sigset_t *sigmask);
功能:函数用于临时更改进程的信号屏蔽字,并等待一个特定的信号到来,然后恢复原有的信号屏蔽字。和 pause
函数不同的是,它可以临时更改进程的信号屏蔽字,以便等待某个特定信号。
参数:
sigmask:希望屏蔽的信号
示例:解决任务中的信号丢失问题,sigsuspend相当于sigprocmask+pause
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void handle(int sig)
{printf("Handle:I get sig=%d\n",sig);
}void mytask()
{printf("mytask:start\n");sleep(3);printf("mytask:end\n");
}int main(int argc, char * argv[])
{struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);sigaction(SIGHUP,&act,NULL);sigset_t set;sigaddset(&set,SIGHUP);sigaddset(&set,SIGINT); sigset_t set2; sigemptyset(&set2); //接收所有信号pause();while(1){sigprocmask(SIG_BLOCK,&set,NULL);mytask();//sigprocmask(SIG_UNBLOCK,&set,NULL);//pause();sigsuspend(&set2);}printf("After pause\n");
}
运行结果
linux@linux:~/Desktop$ ./pause
^CHandle:I get sig=2 //1次CTRL+C
mytask:start
mytask:end
^CHandle:I get sig=2 //1次CTRL+C
mytask:start
mytask:end
^CHandle:I get sig=2 //1次CTRL+C
mytask:start
^Cmytask:end //任务中途的1次CTRL+C
Handle:I get sig=2 //也被获取到并执行了
mytask:start
mytask:end
3 练习
实现信号回收子进程程序
提示:SIGCHLD
是一个用于处理子进程状态变化的信号。当一个子进程终止或停止时,它的父进程会收到 SIGCHLD
信号。
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>void handle(int sig)
{wait(NULL);printf("Get sig =%d\n",sig);
}int main(int argc, char* argv[])
{pid_t pid;struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);pid = fork();if(pid > 0){printf("father process\n");sigaction(SIGCHLD,&act,NULL);}else if(pid == 0){printf("childe process\n");sleep(5);exit(0);}else{perror("fork");return 0;}while(1){sleep(1);}return 0;
}
运行结果:5s后子进程退出,没有产生僵尸进程。