信号:
软件层面的异步事件机制;
异步:事件发生的时间顺序不固定;
中断:硬件层面的异步事件机制;
不同的信号用不同的整型标识;
kill -l:查看信号整型对应信号;
信号的默认行为:
Segmentation fault (core dumped)
是段错误(Segmentation Fault)的一个常见表现形式。段错误是程序在尝试访问其内存空间中未分配(或没有权限访问)的内存区域时发生的错误。这种错误通常发生在以下几种情况:
解引用空指针:尝试访问一个值为
NULL
(或未初始化的指针,其值可能是任何随机地址)的指针所指向的内存位置。数组越界:访问数组时超出了其分配的内存范围。
栈溢出:由于递归调用过深或局部变量使用过多导致的栈空间耗尽。
非法内存访问:如尝试修改只读内存区域(如字符串常量)或访问其他进程的内存。
当操作系统检测到程序试图进行上述非法内存访问时,它会终止该程序,并可能生成一个核心转储文件(core dump),该文件包含了程序终止时的内存、寄存器状态等信息,可用于调试程序以确定段错误的原因。
解决段错误通常需要:
- 检查指针是否在使用前被正确初始化。
- 验证数组索引是否在有效范围内。
- 使用调试工具(如 gdb)来运行程序,并在段错误发生时检查程序的状态(如变量的值、堆栈跟踪等)。
- 确保不会修改不应修改的内存区域。
core文件生成:收到信号;
信号产生的时机:
软件+异步: kill -9 pid
软件+同步:调用abort();;自己给自己发信号6,异常终止;
硬件+异步:ctrl +c , ctrl \
硬件+同步:除0;除法计算器;
产生信号(来源)-------时间(可变)---------递送信号(目标进程)
当信号产生时:
信号产生会修改目标进程(认为所有的信号都来自内核)的task_struct;
mask:掩码;
pending:未决;
响应时机:可递送信号的时机;几乎所有状态(除了d状态,处于不可中断的睡眠状态);
更改默认的信号行为
不再执行默认操作,而是调用函数;
signal:注册信号处理行为(等到信号到来时才调用函数)
9号信号不能注册
是一种等到信号递送的被动调用行为;
typedef 给函数指针起了叫sighandler_t的别名;
注册2号信号:sigFunc是回调函数
printf(“”):不加换行,数据被留在缓冲区里;可能会丢失;
#include <43func.h>
void sigFun(int num){
printf("num = %d\n",num);
}
int main(){
void (*ret)(int);//返回值是一个以int为参数的函数指针
ret = signal(SIGINT,sigFun);//(2(信号),函数(void(*fun)(int))
ERROR_CHECK(ret,SIG_ERR,"signal");
while (1)
{
/* code */
}
}
进程上下文和中断上下文;
回调函数:自己写的函数交给其他进程来调用;
注册多个信号:
阻塞和未决实现的原理:
阻塞:
让产生的信号,不能马上递送,处于未决状态;
mask:阻塞信号集(位图);
未决:
已经产生但未递送的信号;
pending:未决信号集(位图);0/1
#include <43func.h>
void sigFun(int num){
printf("before sleep\n");
printf("num = %d\n",num);
sleep(3);
printf("after sleep\n");
}
int main(){
void (*ret)(int);//返回值是一个以int为参数的函数指针
ret = signal(SIGINT,sigFun);//(2(信号),函数(void(*fun)(int))
ERROR_CHECK(ret,SIG_ERR,"signal");
while (1)
{
/* code */
}
}
在一个信号递送的过程中,有多个信号产生,只递送一个;
当进程递送信号A,把信号A本身加入mask,之后有A信号产生;将该信号加入pending集合;
信号递送完成后,A移除mask,递送pending的信号;
pending是位图结构,只能存储0或1(有或无);
当三个信号(非实时信号)要进入pending,第一个信号会被pending存储,pending标识1,当第二第三信号要进入pending,由于pending是位图,只能标识1个信号,所以后面的信号丢失;
信号从产生到递送之前有一段时间处于未决(pending)状态;未决
目标进程的能力可以不马上递送信号 让它处于未决状态;阻塞
不同信号的阻塞(mask):
2号递送时,不把3加入mask;
当2号信号递送一直到递送完成,2号信号一直在mask里面;
低速系统调用:
可能陷入永久等待的系统调用;
#include <43func.h>
void sigFun(int num){
printf("before sleep\n");
printf("num = %d\n",num);
sleep(3);
printf("after sleep\n");
}
int main(){
void (*ret)(int);//返回值是一个以int为参数的函数指针
ret = signal(SIGINT,sigFun);//(2(信号),函数(void(*fun)(int))
ERROR_CHECK(ret,SIG_ERR,"signal");
//ret = signal(SIGQUIT,sigFun);
char buf[512] = {0};
read(STDIN_FILENO,buf,sizeof(buf));
printf("buf = %s\n",buf);
}
在注册信号后,如果信号递送完成之后,会自动重启低速系统调用;案例重启read;
当信号递送时进入回调函数,如果在函数运行过程中,也就是递送没有结束时,标准输入流的数据会被留在输入缓冲区,等到递送完成后重启的系统调用使用该数据;
signal的特点:
(1)一次注册,永久生效;
让注册只生效一次:在sigFun里调用signal函数:SIG_DFL(注册默认行为);
(2)递送A时,会将A加入mask,其他信号不会加入mask;
(3)会自动重启低速系统调用;
sigaction:更灵活的注册,精细控制信号
sigaction是一个用于改变进程接收特定信号后行为的函数,它定义在
<signal.h>
头文件中。以下是对sigaction函数的详细解析,包括其参数、结构体以及使用示例。一、sigaction函数原型
c复制代码
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
二、参数解析
- signo:
- 类型:
int
- 描述:指定要处理的信号的编号。该参数可以是除SIGKILL和SIGSTOP外的任何有效信号。SIGKILL和SIGSTOP不能被捕获、阻塞或忽略,因此不能为它们设置信号处理函数。
- act:
- 类型:
const struct sigaction *
- 描述:指向
sigaction
结构体的指针,该结构体定义了与signo
相关联的新信号处理动作。如果此参数非空,则根据act
修改信号的处理动作。- oact:
- 类型:
struct sigaction *
- 描述:指向
sigaction
结构体的指针,用于保存signo
原来的处理动作。如果此参数非空,则通过oact
传出原来的处理动作。如果不需要保存原处理动作,可以将其设置为NULL。三、sigaction结构体
sigaction
结构体用于指定信号的处理方式,其定义如下:
struct sigaction {
void (*sa_handler)(int); // 信号处理函数指针,与signal函数中的handler相同
void (*sa_sigaction)(int, siginfo_t *, void *); // 另一个信号处理函数指针,用于接收更多信号信息
sigset_t sa_mask; // 信号屏蔽字,在调用处理函数时,将sa_mask中的信号添加到进程的信号屏蔽字中
int sa_flags; // 信号处理标志,用于控制信号处理过程的各种选项
void (*sa_restorer)(void); // 废弃不用
};
四、sa_flags标志位
- SA_SIGINFO:如果设置了此标志,则使用
sa_sigaction
成员作为信号处理函数,该函数可以接收更多关于信号的信息。- SA_NODEFER:默认情况下,信号处理函数执行时,会自动阻塞当前信号。如果设置了此标志,则在信号处理函数执行期间不会阻塞当前信号。
- SA_RESTART:默认情况下,信号处理会中断系统调用并返回EINTR错误。如果设置了此标志,则系统调用在信号处理完成后会自动重启。
- SA_RESETHAND:当信号处理函数返回时,自动将信号处理函数重置为SIG_DFL(默认处理)。
- SA_ONSTACK:如果设置了此标志,并且已经通过sigaltstack()设置了备用信号栈,则信号处理函数会在备用栈上执行。
可以完全取代signal;
sigaction:
SA_SIGINFO:有这个属性,就选sigaction的第二个参数;
默认:
使用1参数的回调函数,
(不自动重启低速系统调用),
递送过程中会把自己加入mask;
使用无参数的sigaction:
#include <43func.h>
void sigFunc(int num){
printf("num = %d\n",num);
}
int main(){
struct sigaction act;
memset(&act,0,sizeof(act));
act.sa_handler = sigFunc;
int ret = sigaction(SIGINT,&act,NULL);
ERROR_CHECK(ret,-1,"sigaction");
while (1)
{
/* code */
}
}
sigaction默认不会重启系统调用直接退出:
#include <43func.h>
void sigFunc(int num){
printf("num = %d\n",num);
}
int main(){
struct sigaction act;
memset(&act,0,sizeof(act));
act.sa_handler = sigFunc;
int ret = sigaction(SIGINT,&act,NULL);
ERROR_CHECK(ret,-1,"sigaction");
char buf[100] = {0};
read(STDIN_FILENO,buf,sizeof(buf));
printf("%s",buf);
}
sigaction默认和signal一样:会把自己的信号加入mask
#include <43func.h>
void sigFunc(int num){
printf("before,num = %d\n",num);
printf("num = %d\n",num);
sleep(3);
printf("after,num = %d\n",num);
}
int main(){
struct sigaction act;
memset(&act,0,sizeof(act));
act.sa_handler = sigFunc;
int ret = sigaction(SIGINT,&act,NULL);
ERROR_CHECK(ret,-1,"sigaction");
//char buf[100] = {0};
// read(STDIN_FILENO,buf,sizeof(buf));
// printf("%s",buf);
while (1)
{
/* code */
}
}
让sigaction重启系统调用:
#include <43func.h>
void sigFunc(int num){
printf("before,num = %d\n",num);
printf("num = %d\n",num);
sleep(3);
printf("after,num = %d\n",num);
}
int main(){
struct sigaction act;//
memset(&act,0,sizeof(act));//
act.sa_handler = sigFunc;//
act.sa_flags = SA_RESTART;//设置sigaction重启系统调用
//(2,struct sigaction *act,struct sigaction *oldact,)
int ret = sigaction(SIGINT,&act,NULL);
ERROR_CHECK(ret,-1,"sigaction");
char buf[100] = {0};
read(STDIN_FILENO,buf,sizeof(buf));
printf("%s",buf);
// while (1)
// {
// /* code */
// }
}
三参数版本回调:
#include <43func.h>
void sigFunc(int num){
printf("before,num = %d\n",num);
printf("num = %d\n",num);
sleep(3);
printf("after,num = %d\n",num);
}
void sigFun3(int num,siginfo_t *siginfo,void *p){
printf("num =%d\n",num);
printf("sender pid = %d\n",siginfo->si_pid);
}
int main(){
struct sigaction act;//
memset(&act,0,sizeof(act));//
act.sa_sigaction = sigFun3;//
act.sa_flags = SA_RESTART|SA_SIGINFO;//设置sigaction重启系统调用
//(2,struct sigaction *act,struct sigaction *oldact,)
int ret = sigaction(SIGINT,&act,NULL);
ERROR_CHECK(ret,-1,"sigaction");
char buf[100] = {0};
read(STDIN_FILENO,buf,sizeof(buf));
printf("%s",buf);
// while (1)
// {
// /* code */
// }
}
已进入函数就把递送行为改为默认递送行为;
SA_RESETHAND:只注册一次
#include <43func.h>
void sigFunc(int num){
printf("before,num = %d\n",num);
printf("num = %d\n",num);
sleep(3);
printf("after,num = %d\n",num);
}
void sigFun3(int num,siginfo_t *siginfo,void *p){
printf("num =%d\n",num);
printf("sender pid = %d\n",siginfo->si_pid);
}
int main(){
struct sigaction act;//
memset(&act,0,sizeof(act));//
act.sa_sigaction = sigFun3;//
act.sa_flags = SA_RESTART|SA_SIGINFO|SA_RESETHAND;//设置sigaction重启系统调用
//(2,struct sigaction *act,struct sigaction *oldact,)
int ret = sigaction(SIGINT,&act,NULL);
ERROR_CHECK(ret,-1,"sigaction");
char buf[100] = {0};
read(STDIN_FILENO,buf,sizeof(buf));
printf("%s",buf);
// while (1)
// {
// /* code */
// }
}
SA_NODEFER:在递送A时不屏蔽A;
#include <43func.h>
void sigFunc(int num)
{
printf("before,num = %d\n", num);
printf("num = %d\n", num);
sleep(3);
printf("after,num = %d\n", num);
}
void sigFun3(int num, siginfo_t *siginfo, void *p)
{
printf("before,num = %d\n", num);
// printf("num =%d\n", num);
//printf("sender pid = %d\n", siginfo->si_pid);
sleep(3);
printf("after,num = %d\n", num);
}
int main()
{
struct sigaction act; //
memset(&act, 0, sizeof(act)); //
act.sa_sigaction = sigFun3; //
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_NODEFER; // 设置sigaction重启系统调用
//(2,struct sigaction *act,struct sigaction *oldact,)
int ret = sigaction(SIGINT, &act, NULL);
ERROR_CHECK(ret, -1, "sigaction");
char buf[100] = {0};
read(STDIN_FILENO, buf, sizeof(buf));
printf("%s", buf);
// while (1)
// {
// /* code */
// }
}
sa_mask:指定递送过程中的额外屏蔽信号;
本质是位图
操作位图的函数:
mask:阻塞集合;//平时没有信号,在递送过程中将自己加入(SA_NODEFER就不加入);
pending:未决集合;
sigaddset:
在SIGINT的递送过程中,把SIGINT加入阻塞;
sa_mask是一种临时的额外阻塞
#include <43func.h>
void sigFun(int num){
printf("before\n");
printf("num = %d\n",num);
sleep(3);
printf("after\n");
}
int main(){
struct sigaction act;//创建结构体,用于sigaction的参数
memset(&act,0,sizeof(act));
act.sa_handler = sigFun;//无参数版本,
act.sa_flags = SA_RESTART;
sigaddset(&act.sa_mask,SIGQUIT);//act结构体中的阻塞集合
int ret = sigaction(SIGINT,&act,NULL);
ERROR_CHECK(ret,-1,"sigaction");
while (1)
{
/* code */
}
}
等到2号信号完成才终止;
sigprocmask:实现全程阻塞:
#include <43func.h>
int main(){
sigset_t set;//创建位图,mask
sigemptyset(&set);//清空set
sigaddset(&set,SIGQUIT);//把SIGQUIT加入set;
sigset_t oldset;//存旧的mask集合
//把现在的mask集合换成set,并把原mask存入oldset
sigprocmask(SIG_SETMASK,&set,&oldset);
printf("sleep\n");
sleep(5);
printf("wakeup\n");
sigprocmask(SIG_SETMASK,&oldset,NULL);
printf("bye\n");
return 0;
}
sigpending:获取pending集合
#include <43func.h>
void sigFun(int num){
printf("num = %d\n",num);
}
int main(){
//signal(SIGQUIT,sigFun);
sigset_t set;
sigemptyset(&set);
sigset_t oldset;
sigaddset(&set,SIGQUIT);
sigprocmask(SIG_SETMASK,&set,&oldset);
printf(" sleep\n");
sleep(5);
printf("wakeup!\n");
sigset_t pending;
sigpending(&pending);//获取pending集合
if(sigismember(&pending,SIGQUIT)){
printf("SIGQUIT is pending!\n");
}else{
printf("SIGQUIT is not pending!\n");
}
signal(SIGQUIT,sigFun);
sigprocmask(SIG_SETMASK,&oldset,NULL);
// signal(SIGQUIT,sigFun);
printf("bye");
return 0;
}
pause:暂停,等待信号;
kill:发送信号给进程
#include <43func.h>
int main(int argc,char *argv[]){
ARGS_CHECK(argc,3);
int sig = atoi(argv[1] + 1);
pid_t pid = atoi(argv[2]);
printf("%d %d\n",sig,pid);
int ret = kill(pid,sig);
ERROR_CHECK(ret,-1,"kill");
}
raise:给自己发信号;(实现有序退出时使用)
1. sigemptyset
功能:初始化信号集,将所有信号从信号集中清除。
原型:
#include <signal.h>
int sigemptyset(sigset_t *set);
参数:
set
:指向信号集的指针。返回值:成功时返回0,失败时返回-1。
用途:在设置
sa_mask
之前,通常使用sigemptyset
来初始化信号集,确保它开始时是空的。2. sigaddset
功能:向信号集中添加一个信号。
原型:
#include <signal.h>
int sigaddset(sigset_t *set, int signum);
参数:
set
:指向信号集的指针。signum
:要添加到信号集中的信号编号。返回值:成功时返回0,失败时返回-1。
用途:将特定的信号添加到
sa_mask
中,以便在信号处理函数执行期间屏蔽这些信号。3. sigdelset
功能:从信号集中删除一个信号。
原型:
#include <signal.h>
int sigdelset(sigset_t *set, int signum);
参数:
set
:指向信号集的指针。signum
:要从信号集中删除的信号编号。返回值:成功时返回0,失败时返回-1。
用途:虽然
sa_mask
的主要用途是在信号处理函数执行期间屏蔽信号,但在某些情况下,可能需要从信号集中删除信号,尽管这在sa_mask
的上下文中可能不太常见。4. sigaction
功能:查询或设置与信号相关联的处理动作。
原型:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oact);
参数:
signum
:要操作的信号编号。act
:指向包含新信号处理的sigaction
结构的指针。如果此参数非空,则使用它来设置新的信号处理。oact
:如果非空,则指向一个sigaction
结构,该结构用于存储之前的信号处理设置。返回值:成功时返回0,失败时返回-1。
用途:虽然
sigaction
本身不是直接操作sa_mask
的函数,但它是设置和查询信号处理动作的主要接口,包括sa_mask
的设置。通过修改sigaction
结构中的sa_mask
成员,可以指定在信号处理函数执行期间哪些信号应该被屏蔽。
alarm:
默认行为:终止进程;所以要注册SIIGALRM;
#include <43func.h>
int main(){
time_t now = time(NULL);
printf("%s\n",ctime(&now));
alarm(5);
pause();
now = time(NULL);
printf("%s\n",ctime(&now));
}
#include <43func.h>
void sigFun(int num){
time_t now = time(NULL);
printf("%s\n",ctime(&now));
}
int main(){
signal(SIGALRM,sigFun);
sigFun(0);
alarm(5);
pause();
}
自己实现sleep():
sleep可能是用alarm实现的,所以不能混用;
时钟:
间隔定时器:
ITIMER_REAL:真实时间,SIGALARM(信号)
ITIMER_VIRTUAL:虚拟时间 占用用户态CPU的时间 SIGVTALARM
ITIMER_PROF:实用时间 占用用户态+内核态CPU的时间 SIGPROF;
用实用时间比较程序性能;
getitimer():获取当前定时器
setitimer():设置定时器:
#include <43func.h>
void sigFunc(int signum){
time_t now = time(NULL);
puts(ctime(&now));
}
int main(){
struct itimerval itimer;
itimer.it_value.tv_sec = 5;//计时器的当前值秒,5秒后第一次触发
itimer.it_value.tv_usec = 0;//微秒
itimer.it_interval.tv_sec = 2;//间隔时间重置值 秒每隔两秒触发一次
itimer.it_interval.tv_usec = 0;//微秒
//真实时间
// signal(SIGALRM,sigFunc);//注册信号
// setitimer(ITIMER_REAL,&itimer,NULL);//用真实时间参数,给出SIGALARM信号;
//用户态
// signal(SIGVTALRM,sigFunc);//注册SIGVTALRM信号(虚拟时间);
// //用虚拟时间做参数返回SIGVTALRM信号;
// setitimer(ITIMER_VIRTUAL,&itimer,NULL);
//实用时间,内核态和用户态;
signal(SIGPROF,sigFunc);
setitimer(ITIMER_PROF,&itimer,NULL);
sigFunc(0);
while(1);
}
真实时间:
虚拟时间
实用时间
四窗口聊天:
代码实现: