信号----进阶总结:
总结:
信号:
信号的生命周期:信号的产生-》在进程中注册-》在进程中注销-》信号处理
信号的产生:
硬件长产生:Ctrl+c 中断信号 Ctrl+| Ctrl +z进程停止
软件产生:kill -[signum] 就是上面信号的数字(这些数字就是宏) -p pid (向指定进程发送指定信号)
系统调用接口 man 2 kill,----->int kill(pid_t pid, int sig);
信号在进程中的注册:在进程pcb中做标记,标记进程收到了哪些信息
task_struct结构体中,有个sigpending(未决),
我们把信号加入到进程的过程称为
注册
非可靠信号:pcb中pending位图中是否注册,是,就不做任何操作。不是就注册
可靠信号:pcb都进行注册
注销
信号在进程中的注销
非可靠信号:节点只有一个,注销就是删除节点,位图置0
可靠信号:节点有多个,删除一个节点,要判断是否有相同信号节点。如果没有就信号置0.如果还有就不处理位图。
信号的处理
信号的处理并不是立即被处理;而是选择 一个合适的实际进行处理信号。当进程运行从内核态返回用户态的时候,就会去执行这个信号。
问题:进程如何从用户态切换到内核态:发起系统调用;程序异常;中断(硬件中断Ctrl+c);
进程当前运行的代码是用户或者是库函数,就说进程当前运行在用户态
信号的处理方式:
默认的处理方式--既定义好的处理方式
忽略处理方式--处理动作什么都不做
自定义处理方式--用户自己定义的处理方式----修改默认的处理函数(自定义一个处理函数去替换默认的处理函数)
如何修改信号的处理方式:
1 、 sighandler_t signal(int signum ,sighander_t handler);
信号会打断当前的阻塞操作
signum :信号的编码 ---替换signum这个信号的处理函数(也可以是信号名,信号名和数字是宏,可以替换)
handler:函数指针 用户传入的处理函数
SIG_DFL:信号的默认处理动作(default)
SIG_IGN:信号的忽略处理动作(ignore)
typedef void(*sighandler_t)(int);//定义了一个函数指针类型,这个函数指针类型是*sighandler_t,返回值是void,参数是int
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <signal.h>
4#include<stdlib.h>
5
6 int main (){
7 signal(SIGINT, SIG_IGN); // 也可以换成signal(2, 3); 2号信号被替换成3号信号
8 while(1){
9 printf("xiuxiyihui\n");
10 sleep(10);
11 }
12 system("pasue");
13 return 0;
14 }
2、int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); 将信号signum结构被后面的*act结构替换,同时将signum的结构保存到*oldact结构中
struct sigaction {
void (*sa_handler)(int); ---->参入的参数是现有的信号数字,并对现有信号进行处理
void (*sa_sigaction)(int, siginfo_t *, void *); //这两个其实是一个联合体,二选一运用
sigset_t sa_mask; ------>当我们正在处理这个信号时,sa_mask形成一个阻塞,别的信号不能处理
sigset_t sa_mask;这里的sigset是个位图结构,由于这个位图是直接从内存中分配来的,他原来的信息需要清空才能保证我们使用位图时的正确性
int sa_flags; -->这是一个标识,通常标记为0,就是使用上面sa_handler函数;标记为SA_SIGINFO,就是使用sa_sigaction的函数
void (*sa_restorer)(void);
};
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <signal.h>
4 #include <stdlib.h>
5
6 int main (){
7
8 struct sigaction new_act, old_act;
9 new_act.sa_flags = 0;
10 new_act.sa_handler = SIG_IGN;
11
12 // int sigemptyset(sigset_t *set) 清空信号的集合
13 sigemptyset(&new_act.sa_mask);//清空的是结构体中的位图
14 sigaction(2, &new_act, &old_act);
15
16 while(1){
17 printf("xiuxiyihui\n");
18 sleep(10);
19 }
自定义信号在对操作函数的改变上可以传入已经定义好的信号,也可以直接传入函数指针就可以
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <signal.h>
4 #include <stdlib.h>
5
6 void rec(int signo){
7 signal(signo, SIG_DFL);
8 printf("%d已经恢复\n",signo);
9 }
10
11
12
13 int main (){
14 //1 signal(SIGINT, SIG_IGN);
15 struct sigaction new_act, old_act;
16 new_act.sa_flags = 0;
17 new_act.sa_handler = rec;
18
19 sigemptyset(&new_act.sa_mask);//清空的是结构体中的位图
20
21 sigaction(2, &new_act, &old_act);
22
23 while(1){
24 printf("xiuxiyihui\n");
25 sleep(10);
26 }
27 system("pasue");
28 return 0;
29 }
信号的阻塞
阻止信号被抵达--》阻止信号被处理
信号可以注册,但是不被处理(进程信号中位图置1,链表里加入了信号,)
在pcb中还有一个阻塞信号集合【标记哪些信号不被处理】
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how:
SIG_BLOCK 向阻塞集合中加入set集合中的信号block = mask | set
SIG_UNBLOCK 把阻塞的集合中移除set集合中的信号 block = mask &(~set)
SIG_SETMASK 将set集合信号设置为阻塞集合 block= set
oldset:
用于保存修改前的信号
关键函数
sigfillset(sigset_t *set)向set中添加所有信号
sigaddset(sigset_t* set, int signum)向set中添加指定信号
在所有的信号中,9号信号SIGKILL和19号信号SIGSTOP不可以阻塞,他们不能加入到block中进行阻止
调研sleep函数的实现
可重入和不可重入
函数的重入:
可重入:多个执行流程同时执行进入相同的函数
不可重入: 多个执行流程同时执行进入相同的函数,有可能造成数据的二义性以及代码逻辑混乱
当用户设计一个函数或者使用一个函数时,在多个执行流中,这个时候就要考虑函数是否允许重入
函数的可重入和不可重入关键是:
这个函数中是否对临界资源(全局数据)进行来非原子操作
上一篇: 这款软件拯救了设计师 比photoshop更好用!
下一篇: 淘宝店铺装修要注意哪些问题?