中国网站建设公司排行榜,莱特币做空 网站,wordpress 两边,网站的内部推广的方法信号和信号量半毛钱关系都没有#xff01;
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2
查看信号的机制#xff0c;如默认处理动作man 7 signal
SIGINT的默认处理动作是终止进程#xff0c;SIGQUIT的默认处理…信号和信号量半毛钱关系都没有
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2
查看信号的机制如默认处理动作man 7 signal
SIGINT的默认处理动作是终止进程SIGQUIT的默认处理动作是终止进程并且Core Dump。
进程信号
kill命令可以给进程发送信号。kill -l查看信号列表共61个信号其中信号1-31号普通信号34-64号实时信号。我们只需了解普通信号。信号是给进程发的kill -信号编号 pid。
进程本身是被程序员编写的属性和逻辑的集合—程序员编码完成的当进程收到信号时不一定立即处理。
信号及相关概念
信号是进程之间事件异步通知的一种方式属于软中断。
[yyqVM-8-13-centos 2023_03_01_ProcessSignal]$ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) 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) SIGRTMIN1 36) SIGRTMIN2 37) SIGRTMIN3
38) SIGRTMIN4 39) SIGRTMIN5 40) SIGRTMIN6 41) SIGRTMIN7 42) SIGRTMIN8
43) SIGRTMIN9 44) SIGRTMIN10 45) SIGRTMIN11 46) SIGRTMIN12 47) SIGRTMIN13
48) SIGRTMIN14 49) SIGRTMIN15 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) SIGRTMAXctrlc是一个组合键OS会识别为kill -2 pid是2号信号用于终止前台进程。SIGINT的默认处理动作是终止进程。 实际执行信号的处理动作称为信号递达(Delivery)。 信号从产生到递达之间的状态,称为信号未决(Pending)。信号产生了但是没有被执行 信号可以被进程选择性阻塞。 被阻塞的信号产生时将保持在未决状态直到进程解除对此信号的阻塞才执行递达的动作。 注意阻塞和忽略是不同的只要信号被阻塞就不会递达而忽略是在递达之后可选的一种处理动作。
信号的生命周期
预备
首先进程需要认识信号知道信号的属性和对应要做的动作
信号产生
发送信号的本质就是更改对应进程PCB中的信号位图。PCB的管理者是OS故信号也是由OS发送因为只有OS有权利修改PCB里的内容。
无论有多少种发送信号的方式本质都是通过OS向目标进程发送信号故OS必须提供发送信号/处理信号的相关系统调用。
当信号来临时进程不一定马上处理这个信号。异步各自做着不一样的事情进程继续执行自己的代码而不执行信号。同步进程先执行信号停下自己的代码。由于信号可以随时产生所以进程对信号是异步的。
常见的信号产生方式
键盘输入
ctrlc SIGINT-终止前台进程 ctrl\ SIGQUIT-终止进程
系统调用
kill() 给别人发任意信号、raise() 给自己发任意信号、abort() 给自己发SIGABRT信号
#include sys/types.h
#include signal.h
原型int kill(pid_t pid, int sig);
参数pid:pid0,发送给指定进程; pid0,发送给调用进程的进程组中的每个进程; pid-1,发送给所有调用进程有权限发送信号的进程除了进程1 (init); pid-1, 发送给进程组中ID为-pid的所有进程sig:信号编号
返回值成功返回0失败返回-1#include signal.h
原型int raise(int sig);// 相当于kill(getpid(), sig);
参数sig:信号编号
返回值成功返回0失败返回非0#include signal.h
原型void abort(void);// 相当于kill(getpid(), SIGABRT);大多数信号的默认处理动作都是终止进程信号的意义信号不同代表不同的事件对事件发生之后的处理动作可以一样。
硬件异常
1、如除0异常的8号信号SIGFPE 那操作系统如何得知下面这个程序在执行除0操作 答通过硬件-cpu内的状态寄存器。状态寄存器有个溢出标记位当除0溢出标记位就变成1此次运算结果无意义CPU发生运算异常OS作为软硬件资源的管理者就知道是当前的进程导致的硬件异常于是OS修改该进程的信号标记位。
#include iostream
#include string
#include cstdio
#include unistd.h
#include signal.h
#include sys/types.hvoid handler(int signo)
{std::cout 捕捉到一个信号编号是 signo std::endl;
} int main()
{signal(SIGFPE, handler);// 只是注册了该信号对应的动作int a 10;a / 0;while(true){std::cout 我是一个进程 getpid() std::endl; sleep(1);}return 0;
}在执行这个程序后会一直输出捕捉到一个信号编号是8不应该只输出1次程序就被终止了么 答首先我们自定义了收到8号信号的动作就不会执行默认动作退出。其次1、收到信号不一定会引起进程退出2、进程没有退出就有可能还会被调度3、cpu内部对应的寄存器只有一个但是状态寄存器中的内容属于当前进程的上下文但是用户没有能力去修改寄存器中的内容4、当进程被切换的时候就有无数次状态寄存器被保存和恢复的过程所以每一次恢复的时候就会让OS识别到状态寄存器的溢出标志位所以一直给进程发8号信号。
2、如访问野指针 11号信号SIGSEGV
int* p nullptr;
*p 100;//段错误那操作系统如何得知下面这个程序访问野指针了 答咱们在程序里写的指针本质是就是虚拟地址而虚拟地址空间的0号地址经过页表和MMU(在cou里))的映射发现当前进程不允许访问0号地址MMU这个硬件因为越界/野指针进而发生异常OS识别到了MMU异常就将对应进程的信号位图的11位置为1.
软件条件
1、管道读端关闭写端一直写OS直接终止当前进程13号信号SIGPIPE
2、定时器alarm()函数 14号信号SIGALRM
#include unistd.h
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。可以通过alarm()的使用我们可以明显观察到IO速度很慢
int main()
{alarm(1); // 1s内计算机能够将数据累加到144250int cnt 0;while(true){std::cout cnt std::endl;//这里要进行cnt次IO速度很慢cnt;}return 0;
}
---------------------------------------------
int cnt 0;
void handler(int signo)
{std::cout cnt: cnt std::endl;//只需要进行1次IO
}
int main()
{alarm(1); // 1s内计算机能够将数据累加到435882026signal(SIGALRM, handler);while(true){cnt;}return 0;
}闹钟信号只会通知1次即只有1次输出不会像硬件异常那样疯狂输出
int cnt 0;
void handler(int signo)
{std::cout cnt: cnt std::endl;//只需要进行1次IOalarm(1);//这样就可以实现每隔1s打印一次简单的sleep(1)
}
int main()
{alarm(1); // 1s内计算机能够将数据累加到435882026signal(SIGALRM, handler);while(true){cnt;}return 0;
}alarm(0)表示取消以前设定的闹钟函数的返回值仍然是以前设定的闹钟时间还余下的秒数。
任何一个进程都可以通过alarm系统调用在内核中设置闹钟那么OS就要对其进行管理先描述在组织。
struct alarm { uint64_t when; //未来的时间 即时间戳闹钟设定的时间int type; //闹钟类型一次性或周期性 task_struct *p; //对应的进程 struct alarm* next; //下一个闹钟
};比如用最小堆来管理那么堆顶的闹钟就是将来第一个要超时的闹钟OS就可以周期性的检查堆顶当操作系统的当前时间alarm.when就给对应的进程发送信号。
信号保存
在信号到来到信号被处理的这段时间称为时间窗口必须要保存这个信号。会被保存在进程PCBtask_struct{ unsigned int signal}这个结构体里unsigned int有32个比特位用来表示1-31号信号【比特位的位图结构】。比特位的位置代表信号编号比特位的内容代表是否收到该信号对应的值为0/1收到对应的信号就置为1。
保存和处理中间的情况信号阻塞、信号未决
被阻塞的信号将保持在未决(pending)状态直到进程解除对信号的阻塞(block)才执行递达动作。普通信号在递达之前产生多次只计一次因为对应二进制位非0即1。
task_struct {unsigned int pending 0; //0000 0000 0000 0000 0000 0000 0000 0000 // 从右向左第1个比特位表示1号信号unsigned int block 0; //0000 0000 0000 0000 0000 0000 0000 0000
}if ((1 (signo - 1)) pcb-block)
{//被阻塞,那就不用递达了
}
else
{if((1 (signo - 1)) pcb-pending){//未被阻塞且信号未决那就递达}
}①PCB进程控制块中有信号屏蔽状态字(block)信号未决状态字(pending)还有是否忽略标识 ②信号屏蔽状态字(block)1代表阻塞0代表不阻塞信号未决状态字(pending)1代表未决0代表信号递达 ③向进程发送SIGINT内核首先判断信号屏蔽状态字是否阻塞如果信号屏蔽状态字阻塞信号未决状态字(pengding)相应位置1 若阻塞解除信号未决状态字(pending)相应位置0表示信号可以递达了。 ④block状态字pending状态都是64bit分别代表Linux系统中的64个信号。例如SIGINT是2号信号对应block状态字中的第二位 ⑤block状态字用户可以读写pending状态字用户只能读这是信号的设计机制。
注意即使一个信号没有产生它仍然可以先被设置为阻塞状态。进程能识别信号是因为PCB结构中有相应的数据结构block
位图、pending位图、handler_t表
信号处理
typedef void(*handler_t)(int signo);//函数指针-表示操作方法
handler_thandler[32] 0;//函数指针数组存储32个信号对应的处理方法 数组的下标1表示信号编号数组下标对应的内容表示对应信号的处理方法收到信号后一般会有3个动作。
默认动作
大多数信号的默认处理动作都是终止进程信号的意义信号不同代表不同的事件对事件发生之后的处理动作可以一样。termcoreignstopcont
进程终止时核心转储问题
当进程出现异常的时候我们将进程对应时刻在内存中的有效数据转储到磁盘
First the signals described in the original POSIX.1-1990 standard.
//表中第一列是各信号的宏定义名称第二列是各信号的编号第三列是默认处理动作
Signal Value Action Comment
──────────────────────────────────────────────────────────────────────
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no
readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background processTerm表示终止当前进程OS正常结束该进程 Core表示终止当前进程并且Core DumpCore Dump 用于gdb调试。在命令行输入ulimit -a查看core file size核心转储功能云服务器默认是0关闭该功能命令行输入ulimit -c 1024打开云服务器的核心转储功能并设置大小为1024字节此时再执行会发生段错误的程序就会输出Segmentation fault (core dumped)且当前目录下会生成一个文件core.引起core问题的进程ID给该文件在编译时带上-g选项在进行调试的时候输入core-file core.pid就可以把对应的核心转储文件打开就可以看到详细的报错信息。 Ign表示忽略该信号 Stop表示停止当前进程 Cont表示继续执行先前停止的进程
捕捉自定义动作
用户自定义信号处理函数称为捕捉。9号信号无法捕捉。信号产生不一定被立即处理而是在合适的时候由内核态返回用户态处理。
内核态由用户/进程发起的但实际执行系统调用的身份是内核
用户态平时自己写的代码是运行在用户态的而我们的代码难免访问2种资源【OS资源(getpid, waitpid)硬件资源(printf, write, read)】用户为了访问内核和硬件资源必须通过系统调用来完成对应的访问。
往往系统调用比较费时间要进行角色转换。CPU内有寄存器有个current寄存器指向当前正在运行进程的PCB的起始地址CR3寄存器表征当前进程的运行级别0表示内核态3表示用户态 合适的时候从内核态返回用户态的时候要返回的话曾经肯定进入了内核态系统调用、进程切换等此时要从内核态切换回去OS会先通过PCB看看每个信号的block表是否被阻塞没被阻塞的再看对应的pending表是否未决未决的信号让它递达。【block1不管block0pending0,不管block0pending1,查handler表对应的方法并执行】默认和忽略这两个动作很方便而自定义动作需要切换回用户态才能执行【不可以在内核态直接执行】然后再回到内核态获取当前进程的上下文才能回到用户态跳转的位置继续执行后续代码。
普通信号的自定义捕捉流程用户态–内核态【执行系统调用以及信号检测】–用户态【执行自定义动作】–内核态【获取进程上下文】–用户态。
忽略动作
函数接口
signal()函数 信号捕捉
#include signal.h
功能 信号处理器即可以收到特定信号时执行自定义动作原型typedef void (*sighandler_t)(int); // 函数指针传入的int表示信号的编号sighandler_t signal(int signum, sighandler_t handler);//其实就是拿着signum去操作方法表里修改对应信号的操作方法// 设置了收到2号信号的自定义动作
void handler(int signo)
{std::cout 捕捉到2号信号 signo std::endl;
}// 这里是对signal函数的调用而不是对handler的调用
// handler对应的方法一般不会执行除非收到对应的信号
signal(2, handler);// 只是注册了该信号对应的动作
//要想触发该动作需要按ctrlc组合键 SIGINT的默认处理动作是终止进程
//2号的默认动作是退出进程当我们给这个进程的2号信号设置了自定义动作就会执行自定义动作附1-31号信号产生原因
SIGHUP:当用户退出shell时由该shell启动的所有进程将收到这个信号默认动作为终止进程SIGINT当用户按下了组合键时用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止里程SIGQUIT当用户按下组合键时产生该信号用户终端向正在运行中的由该终端启动的程序发出些信 号。默认动作为终止进程SIGILLCPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件SIGTRAP该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件SIGBUS非法访问内存地址包括内存对齐出错默认动作为终止进程并产生core文件SIGFPE在发生致命的运算错误时发出。不仅包括浮点运算错误还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件SIGKILL无条件终止进程。本信号不能被忽略处理和阻塞。默认动作为终止进程。它向系统管理员提供了可 以杀死任何进程的方法SIGUSE1用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程SIGSEGV指示进程进行了无效内存访问。默认动作为终止进程并产生core文件SIGUSR2这是另外一个用户自定义信号 程序员可以在程序中定义并使用该信号。默认动作为终止进程SIGPIPEBroken pipe向一个没有读端的管道写数据。默认动作为终止进程。 SIGALRM:定时器超时超时的时间 由系统调用alarm设置。默认动作为终止进程 SIGTERM程序结束信号与SIGKILL不同的是该信号可以被阻塞和终止。通常用来要示程序正常退出。执行 shell命令Kill时缺省产生这个信号。默认动作为终止进程 SIGSTKFLTLinux早期版本出现的信号现仍保留向后兼容。默认动作为终止进程 SIGCHLD子进程结束时父进程会收到这个信号。默认动作为忽略这个信号 SIGCONT停止进程的执行。信号不能被忽略处理和阻塞。默认动作为终止进程 SIGTTIN后台进程读终端控制台。默认动作为暂停进程 SIGTSTP停止进程的运行。按下组合键时发出这个信号。默认动作为暂停进程 SIGTTOU该信号类似于SIGTTIN在后台进程要向终端输出数据时发生。默认动作为暂停进程 SIGURG套接字上有紧急数据时向当前正在运行的进程发出些信号报告有紧急数据到达。如网络带外数据到达默认动作为忽略该信号 SIGXFSZ进程执行时间超过了分配给该进程的CPU时间 系统产生该信号并发送给该进程。默认动作为终止进程 SIGXFSZ超过文件的最大长度设置。默认动作为终止进程 SIGVTALRM虚拟时钟超时时产生该信号。类似于SIGALRM但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程 SGIPROF类似于SIGVTALRM它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程 SIGWINCH窗口变化大小时发出。默认动作为忽略该信号 SIGIO此信号向进程指示发出了一个异步IO事件。默认动作为忽略 SIGPWR关机。默认动作为终止进程 SIGSYS无效的系统调用。默认动作为终止进程并产生core文件 31号SIGRTMIN64号SIGRTMAXLINUX的实时信号它们没有固定的含义可以由用户自定义。所有的实时信号的默认动作都为终止进程。