微网站制作,有免费的云服务器吗,网站加外链,wordpress 文章摘要 插件索引 一.初始信号1.什么是信号2.前后台进程3.信号的种类4.信号的管理 二.信号产生前1.验证键盘是可以产生信号的2.通过系统调用接口发送信号3.由软件条件产生信号4.硬件异常产生信号5.总结6.core dump 信号产生中1.信号在内核中的表示2.信号集操作函数 信号产生后1.了解内核态和… 索引 一.初始信号1.什么是信号2.前后台进程3.信号的种类4.信号的管理 二.信号产生前1.验证键盘是可以产生信号的2.通过系统调用接口发送信号3.由软件条件产生信号4.硬件异常产生信号5.总结6.core dump 信号产生中1.信号在内核中的表示2.信号集操作函数 信号产生后1.了解内核态和用户态2.内存如何实现信号的捕捉3.sigaction 一.初始信号
1.什么是信号
生活的角度 红绿灯闹钟下课铃等 1.我们是如何得知这些东西的有人教能够认识这些场景下的信号以及其表示的含义也就是能够识别这些信号 2.我们提前知道这些信号产生时要做什么也就是我们已经提前知道了信号处理的方法
从上述可以看出 即使信号没有产生我们已经具备了处理信号的能力 因此信号是给进程发送的进程要具备处理信号的能力 1.该能力一定是预先已经早就有了的 2.进程能够识别对应的信号 3.进程能够处理对应的信号 这个能力是OS给我们提供的 对于进程来讲即使信号还没有产生我们进程已经具有识别和处理这个信号的能力了
2.前后台进程
while (true){sleep(1);}return 0;当我们直接运行上述程序时该程序会变成一个前台进程此时直接ctrlC可以直接终止是因为ctrlC可以发送一个信号给前台进程使得该进程退出。 但当我们将前台进程变成后台进程时其接不到类似ctrl C的信号也就无法退出了 理解用户按下Ctrl C这个键盘输入产生一个硬件中断被OS获取解释成信号发送给目标前台进程前台进程因为收到信号进而引起进程退出。 注意
1.只有前台进程才能收到CtrlC产生的信号后台进程无法收到一个运行进程的命令后面可以使得前台进程转化成后台进程转化为后台进程之后shell不必等到进程结束才可以接受新的命令可以直接启动新进程2.前台进程在运行过程中用户随时按下ctrlC而产生一个信号也就是说该进程的用户空间代码执行到任何地方都有可能受到SIGINT就是CtrlC而终止所以信号相对于进程的控制流程来说是异步的。
3.信号的种类 kill -l 可以显示信号列表 数字是信号编号右侧是宏二者一个意思 1-31是分时信号产生信号了不用立即处理 34-64是实时信号信号产生了就必须处理。 我们学习的是1-31的普通信号。 4.信号的管理 那么进程又是如何管理信号的呢 是在进程的PCB中 eg: task_struct { uint32_t sig;//位图 0000 0000 … } 位图的内容表示有没有该信号位图的位置表示是哪一个信号由于PCB是在内核数据结构所以只有OS有资格修改位图OS是进程的管理者进程的所有属性的获取和设置只能又操作系统来设置因此无论信号怎么产生最终一定是OS帮我们进行信号的设置
下面我将从三个部分信号产生前信号产生中信号产生后来叙述进程间信号
二.信号产生前
上述可以了解到信号在OS中是由位图表示的所以信号的产生OS发送给进程的时候不如说是写入信号。
1.验证键盘是可以产生信号的
sighandler_t signal(int signum, sighandler_t handler); 对信号设置回调 #include signal.htypedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);验证Ctrl C是2号信号我们先对 -定义捕捉动作当我们在键盘上按CtrlC的时候如果调用了我们的自定义函数验证成功
void handler(int signo)
{cout i am a process ,我获取了一个信号: signo endl;
}
int main()
{signal(SIGINT, handler);sleep(3);cout 自定义信号捕捉函数设置完毕 endl;while (true){cout 我是一个正在运行的进程 endl;sleep(1);}return 0;
}signal(SIGINT, handler); 这里不是在调用handler方法只有信号产生的时候才会调用handler方法. 实验结果如下 因此可以得出结论Ctrl C :本质就是给前台进程发送2号信号给目标进程目标进程默认对2号信号的处理动作就是终止自己然而现在我们设置了用户对信号的自定义处理动作。 Ctrl C 产生2号信号 Ctrl \ 产生3号信号同样也是终止进程 注意9号信号是不能设置自定义的即使设置了kill -9 PID 照样也可以杀死进程,因此9号信号也叫做管理员信号
2.通过系统调用接口发送信号 int kill(pid_t pid, int sig);不仅是一个命令还是一个系统调用接口表示对某个进程发送某个信号 我自己写一个mykill进程该进程是调用了kill这个函数的可以得出结论代码如下
mykill.cc
static void Usage(const string proc)
{cerr Usage :\n\t proc signo pid endl;
}
int main(int argc, char *argv[])
{if (argc ! 3){Usage(argv[0]);exit(1);}if (kill(static_castpid_t(atoi(argv[2])), atoi(argv[1])) -1){cerr kill : strerror(errno) endl;}}myproc.cc
while (true){sleep(1);cout 我的PID是 getpid() endl;}int raise(int sig); 给自身发信号
NAMEabort - cause abnormal process terminationSYNOPSIS#include stdlib.hvoid abort(void);//直接终止进程
abort()直接终止进程
3.由软件条件产生信号
NAMEalarm - set an alarm clock for delivery of a signalSYNOPSIS#include unistd.hunsigned int alarm(unsigned int seconds);调用alarm函数可以设定一个闹钟也就是告诉内核在seconds秒之后给当前进程发送SIGALRM信号该信号的默认处理动作是终止当前进程。函数的返回值是0或者是设定闹钟时间的剩余秒数。
int cnt 0;void handler(int signo)
{cout i am a process ,我获取了一个信号: signo endl;cout cnt: cnt endl;
}
int main()
{signal(SIGALRM, handler);alarm(1);while (1){cnt;}
}一秒钟之后就会捕捉SIGALRM信号
4.硬件异常产生信号
下面先列举两个崩溃的进程 猜想一下上述两个进程崩溃的本质也是收到了某个信号验证一下先将每个信号都设置自定义动作然后再运行。 进程崩溃的本质是该进程收到了异常信号因为硬件异常而导致OS向目标进程发信号进而导致进程终止的现象。 除0:CPU内部状态寄存器当我们除0的时候CPU内的状态寄存器会被设置成为有报错浮点数越界CPU内部寄存器硬件OS就会识别到CPU内有报错-1,然后OS就会向目标进程发信号目标进程在合适的时候处理信号终止进程 越界野指针 我们在语言层面使用的地址指针其实都是虚拟地址-物理地址-物理内存-读取对应的数据和代码如果虚拟地址有问题而地址转化的工作是由MMU硬件页表软件构成转化过程就会引起问题-表现在硬件MMU上-OS就会发现硬件出了问题-OS向目标进程发送信号-目标进程在合适的时候处理信号-终止进程 由此可得出结论我们在C/C 中除0内存越界等异常在系统层面上是被当成信号处理的。
5.总结
上面所说的信号的产生信号的发送最终都是要由OS来执行的因为OS是进程的管理者信号不是被立即执行而是在进程合适的时候信号不是被立即执行的那么信号就会被记录下来记录在进程的PCB中一个进程在没有收到信号的时候能否知道自己应该对合法信号做何处理能处理方式已经由之前的程序员写在内核了
6.core dump
Coredump叫做核心转储是进程在运行时突然崩溃的一个内存快照。
pid_t wait(int *status);pid_t waitpid(pid_t pid, int *status, int options);wait 与waitpid都有一个status参数该参数是一个输出型参数由OS填充其他比特位在我之前的博客有提过当生成Core dump文件的时候该标记位会设置成1. 由上可以看出并不是所有的信号都会生成core文件的只有程序自身内部出了问题才会产生core文件。
int main()
{pid_t id fork();if (id 0) // 子进程{int *p nullptr;*p 10000; // 野指针问题exit(1);}int status 0;waitpid(id, status, 0);printf(exitcode: %d,signo: %d, core dump flag: %d \n, (status 8) 0xff, status * 0x7f, (status 7) 0x1);
}运行结果 可以子进程的退出信号是11符合野指针出错信号但此时的core dump标记位还是0为什么呢 因为我是线上的的云服务器厂商设置的默认core文件个数就是0也就是说禁止生成core文件。 设置之后此时允许生成core文件。 但这不是真正的用途此时我们要生成可调试版本的程序。
为什么线上的云服务器将core dump文件关闭 因为云服务器上的产品一般都是release版本但是我们生成的core文件是可调式版本的并且如果线上的产品挂掉了最重要的不是找bug而是重启并且一旦服务挂掉了会直接重启eg一秒钟重启一万次的话每次都有core文件的话此时磁盘占据大量的文件磁盘被打满了会危急到操作系统很危险 总结一下 信号从产生到递达之间的状态称为信号未决 进程可以选择阻塞某个信号当进程阻塞信号时信号无法被抵达 被阻塞的信号产生时将保持在未决状态直到进程解除对此信号的阻塞才执行递达的动作 阻塞和忽略是不同的只要信号被阻塞那么信号即使产生了也不会被递达忽略是信号处理的一个动作 信号产生中
1.信号在内核中的表示
信号在内核中的表示
信号在内核中task_struct指向三张表三张表都是用 位图表示的。 pending:未决信号集表示该信号是否产生 block阻塞信号集表示该信号是否被阻塞 handler:指向的是每个信号的自定义函数. 以上述的SIGQUIT为例此时该信号未产生一旦产生该信号他的处理动作是用户的自定义函数sighandler但是由于此时该信号被阻塞了此时该信号不会抵达除非接触对该信号的阻塞才会抵达。 同时如果一个信号最初未被阻塞但是在某信号抵达之前也可以说是该信号正在处理的时候如果继续产生该信号该信号也还是只会被记录一次实时信号在抵达之前可以产生多次这里不讨论。
2.信号集操作函数
从上图来看每个信号只有一个bit的未决标志非0即1不记录该信号产生了多少次阻塞标志也一样因此未决和阻塞标志可以用相同的数据类型sigset_t来储存。 sigset_t类型对于每种信号用一个bit表示有效或无效,这个类型内部如何储存这些bit则依赖于系统实现我们不必关心我们只要会使用如下几个函数就可以了。
typedef __sigset_t sigset_t;
......
typedef struct{unsigned long int __val[_SIGSET_NWORDS];} __sigset_t;根据上述源码可以看到sigset_t的实现与系统自身有关所以我们不必关心。 int sigemptyset(sigset_t *set) 初始化set所指向的信号集使其中所有信号的对应bit清零表示该信号集不包含任何有效信号 int sigfillset(sigset_t *set) 使其中所有信号的对应bit置位表示该信号集的有效信号包括系统支持的所有信号 int sigaddset(sigset_t *set, int signo) 该函数允许将一个指定的信号添加到一个自定义信号集中也就是将该信号的标准为设为1表示阻塞该信号。 int sigdelset(sigset_t *set,int signo) 与上述函数相反表示解除该信号的阻塞 int sigismember(const sigset_t *set, int signo) 判断一个信号集的有效信号中是否包含某种信号也就是检查是否屏蔽该信号如果包含则返回1反之0 int sigpending(sigset_t *set); 获取当前进程谁调用获取谁的pending信号集,通过set参数传出调用成功返回0失败返回-1 int sigpromask(int how, const sigset_t *set, sigset_t *oset 成功返回0失败返回-1 若oset非空当前进程的信号屏蔽字通过oset传出 set非空更改进程的信号屏蔽字 how:指示如何更改 如果oset 和 set都非空则将原来的信号屏蔽字备份到oset然后根据set和how参数更改信号屏蔽字。 假设当前的信号屏蔽字为mask下述说明了how参数的可选值
SIG_BLOCKset包含了我们希望添加到当前信号屏蔽字的信号SIG_UNBLOCKset包含了从当前信号屏蔽字接触的·信号SIG_SETMASK设置当前信号屏蔽字为set所指向的值
V1.1版本的代码先指示将2号信号添加到信号屏蔽字中 预期结果初始的pending信号集都是0当我们向进程发送2号信号后pending信号集中表示2号信号的比特位变成1
#include unistd.h
#include iostream
#include signal.h
using namespace std;void showpending(sigset_t *pendings)
{for (int sig 1; sig 31; sig){if (sigismember(pendings, sig)){cout 1;}else{cout 0;}}cout endl;
}
void handler(int signo)
{cout 我是一个进程刚刚获得了一个信号 signo endl;
}
int main()
{// 2.屏蔽掉2号信号sigset_t bsig, obsig;sigemptyset(bsig);sigemptyset(obsig);// 2.1添加2号信号到信号屏蔽字中sigaddset(bsig, 2);// 2.2 设置用户级的信号屏蔽字到内核中让当前进程屏蔽2号信号sigprocmask(SIG_SETMASK, bsig, obsig);cout pid: getpid() endl;signal(SIGINT, handler);// 1.不断获取当前进程的pending信号集sigset_t pendings;int cnt 0;while (true){// 1.1清空信号集sigemptyset(pendings);// 1.2获取当前进程pending信号集(谁调用就获取谁)if (sigpending(pendings) 0){// 1.3打印一下当前进程的pending信号集sleep(1);showpending(pendings);cnt;}cout cnt: cnt endl;}return 0;
}V2.0 先将所有的信号都屏蔽在20秒之后解除2号和3号信号 下面只贴部分更改的代码 sigset_t bsig, obsig;sigemptyset(bsig);sigemptyset(obsig);// 2.1添加1-31号信号到信号屏蔽字中for (int sig 1; sig 32; sig){sigaddset(bsig, sig);}// 2.2 设置用户级的信号屏蔽字到内核中让当前进程屏蔽2号信号sigprocmask(SIG_SETMASK, bsig, obsig);cout pid: getpid() endl;signal(SIGINT, handler);signal(3, handler);// 1.不断获取当前进程的pending信号集sigset_t pendings;int cnt 0;while (true){// 1.1清空信号集sigemptyset(pendings);// 1.2获取当前进程pending信号集(谁调用就获取谁)if (sigpending(pendings) 0){// 1.3打印一下当前进程的pending信号集sleep(1);showpending(pendings);cnt;}if (cnt 20){// sigprocmask(SIG_SETMASK,obsig,nullptr); 直接用该方法可以直接解除所有信号集sigset_t sigs;sigemptyset(sigs);sigaddset(sigs, 2);sigaddset(sigs, 3);sigprocmask(SIG_UNBLOCK, sigs, nullptr);}cout cnt: cnt endl;}根据上述结果可以看出当我们将信号添加到信号集之后我们向进程发送信号时此时代表该信号的比特位由0 -- 1 解除信号屏蔽之后就会重新由1 -- 0 但是根据结果可以看出 9号信号即使被屏蔽了还是可以杀死进程
信号产生后
1.了解内核态和用户态
上述提到信号产生后OS系统是在什么时候处理信号呢 实在合适的时候那合适的时候具体是什么时候呢 当 当前进程从内核态切换回用户态的时候进行信号的检测与处理 每个进程都有自己的task_struct指向其虚拟地址虚拟地址到物理地址的转化是通过页表实现的而每个进程对于自己的用户空间3G是独立的还有一份公共的内核页表如下所示. 那么OS在不在内存中被加载答案是肯定的 无论进程如何切换我们都可以找到内核的代码和数据前提是你要有足够的权利进行访问 那么当前的进程如何具备权利访问内核页表乃至访问内核数据呢 要进行身份切换。我们要让OS知道此时访问数据的是内核还是页表 CPU内有对应的状态寄存器CR3寄存器当比特位是0的时候表示内核态当比特位是3的时候表示用户态: 用户态只能访问用户级页表 内核态既能访问内核级页表也能访问用户级页表 内核态相比于用户态拥有更高的权限 那么一般什么时候会从用户态切换回内核态呢
系统调用的时候时间片到了进行进程间切换等
2.内存如何实现信号的捕捉
我们必须要了解一个知识 我们的程序会无数次直接或者间接的访问系统级软硬件资源管理是OS本质上你并没有去操作这些软硬件资源而是必须通过OS–无数次陷入内核(1.切换身份 2.切换页表) --调用内核的代码–完成访问的动作–结果返回给用户(1.切换身份 2.切换页表)–得到结果 eg: while(1); 仅仅是这一行代码存在从用户态切换成内核态吗 一定是有的因为每个进程都有自己的时间片当时间片到了需要转换成内核态然后更换内核级页表 --为了保护上下文执行调度算法–切换新的进程–恢复新进程的上下文–再切换成用户态–CPU执行的就是新进场的代码
下面一个场景当我们调用完系统调用之后返回内核态时检测出了错误 快速记忆 3.sigaction
该函数可以读取和修改指定信号相关联的处理动作成功返回0失败返回-1. #includesignal.h int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }; 参数解析 signum:指定信号编号 act:非空根据指针act修改该信号的处理动作 oldact:非空通过其传出该信号原来的处理动作 act和oldact都是sigaction结构体 上述有该结构体的具体组成 sa_handler:表示该信号的处理动作 当某个信号正在被处理时内核会自动将当前信号加入进程的信号屏蔽字当信号处理函数返回时自动恢复成原来的信号屏蔽字这样的话当前进程正在被处理时如果这个信号再次产生该信号会被阻塞直到当前信号处理结束。 如果在调用信号处理函数时除了希望自动屏蔽当前信号还希望自动屏蔽其他信号则用sa_mask字段说明这些需要额外屏蔽的信号当信号处理函数返回时恢复成原来的状态 sa_flags设为0.
实验一先不给sa_mask添加信号
void handler(int signo)
{cout 我是一个进程刚刚获得了一个信号 signo endl;sigset_t pending;// 此时会永远在处理某个信号while (true){sigpending(pending);for (int i 1; i 31; i){if (sigismember(pending, i))cout 1;elsecout 0;}cout endl;sleep(1);}
}
int main()
{cout my pid: getpid() endl;struct sigaction act, oldact;act.sa_handler handler;act.sa_flags 0;sigemptyset(act.sa_mask);// sigaddset(act.sa_mask, 3);sigaction(2, act, oldact);while (true){cout main running endl;sleep(1);}return 0;
}实验二给sa_mask添加信号 sigaddset(act.sa_mask, 3);
文章转载自: http://www.morning.prjty.cn.gov.cn.prjty.cn http://www.morning.xhrws.cn.gov.cn.xhrws.cn http://www.morning.sogou66.cn.gov.cn.sogou66.cn http://www.morning.rwwdp.cn.gov.cn.rwwdp.cn http://www.morning.brrxz.cn.gov.cn.brrxz.cn http://www.morning.yslfn.cn.gov.cn.yslfn.cn http://www.morning.rszyf.cn.gov.cn.rszyf.cn http://www.morning.pswqx.cn.gov.cn.pswqx.cn http://www.morning.qynpw.cn.gov.cn.qynpw.cn http://www.morning.pmjhm.cn.gov.cn.pmjhm.cn http://www.morning.zljqb.cn.gov.cn.zljqb.cn http://www.morning.ssjee.cn.gov.cn.ssjee.cn http://www.morning.nmfwm.cn.gov.cn.nmfwm.cn http://www.morning.bpmnz.cn.gov.cn.bpmnz.cn http://www.morning.bfgpn.cn.gov.cn.bfgpn.cn http://www.morning.yxgqr.cn.gov.cn.yxgqr.cn http://www.morning.fhlfp.cn.gov.cn.fhlfp.cn http://www.morning.smqjl.cn.gov.cn.smqjl.cn http://www.morning.swkzk.cn.gov.cn.swkzk.cn http://www.morning.xkhhy.cn.gov.cn.xkhhy.cn http://www.morning.bxqry.cn.gov.cn.bxqry.cn http://www.morning.lmmyl.cn.gov.cn.lmmyl.cn http://www.morning.sgrdp.cn.gov.cn.sgrdp.cn http://www.morning.kfmlf.cn.gov.cn.kfmlf.cn http://www.morning.jwpcj.cn.gov.cn.jwpcj.cn http://www.morning.qyfqx.cn.gov.cn.qyfqx.cn http://www.morning.rbjf.cn.gov.cn.rbjf.cn http://www.morning.zrbpx.cn.gov.cn.zrbpx.cn http://www.morning.hsrpr.cn.gov.cn.hsrpr.cn http://www.morning.mwqbp.cn.gov.cn.mwqbp.cn http://www.morning.ddtdy.cn.gov.cn.ddtdy.cn http://www.morning.lbpqk.cn.gov.cn.lbpqk.cn http://www.morning.lczxm.cn.gov.cn.lczxm.cn http://www.morning.pnmnl.cn.gov.cn.pnmnl.cn http://www.morning.ydnxm.cn.gov.cn.ydnxm.cn http://www.morning.jwpcj.cn.gov.cn.jwpcj.cn http://www.morning.bpmnz.cn.gov.cn.bpmnz.cn http://www.morning.bby45.cn.gov.cn.bby45.cn http://www.morning.bnylg.cn.gov.cn.bnylg.cn http://www.morning.hknk.cn.gov.cn.hknk.cn http://www.morning.mkyxp.cn.gov.cn.mkyxp.cn http://www.morning.sflnx.cn.gov.cn.sflnx.cn http://www.morning.hdtcj.cn.gov.cn.hdtcj.cn http://www.morning.rkbly.cn.gov.cn.rkbly.cn http://www.morning.dpsgq.cn.gov.cn.dpsgq.cn http://www.morning.dwdjj.cn.gov.cn.dwdjj.cn http://www.morning.grfhd.cn.gov.cn.grfhd.cn http://www.morning.nrmyj.cn.gov.cn.nrmyj.cn http://www.morning.qhmgq.cn.gov.cn.qhmgq.cn http://www.morning.rqpgk.cn.gov.cn.rqpgk.cn http://www.morning.jnhhc.cn.gov.cn.jnhhc.cn http://www.morning.ydtdn.cn.gov.cn.ydtdn.cn http://www.morning.c7495.cn.gov.cn.c7495.cn http://www.morning.zlrrj.cn.gov.cn.zlrrj.cn http://www.morning.sfyqs.cn.gov.cn.sfyqs.cn http://www.morning.ftldl.cn.gov.cn.ftldl.cn http://www.morning.xnbd.cn.gov.cn.xnbd.cn http://www.morning.txltb.cn.gov.cn.txltb.cn http://www.morning.wfdlz.cn.gov.cn.wfdlz.cn http://www.morning.807yy.cn.gov.cn.807yy.cn http://www.morning.kpxnz.cn.gov.cn.kpxnz.cn http://www.morning.mwlxk.cn.gov.cn.mwlxk.cn http://www.morning.xbdrc.cn.gov.cn.xbdrc.cn http://www.morning.jpkk.cn.gov.cn.jpkk.cn http://www.morning.gskzy.cn.gov.cn.gskzy.cn http://www.morning.ggcjf.cn.gov.cn.ggcjf.cn http://www.morning.xqjh.cn.gov.cn.xqjh.cn http://www.morning.hmqjj.cn.gov.cn.hmqjj.cn http://www.morning.zsrdp.cn.gov.cn.zsrdp.cn http://www.morning.lsgjf.cn.gov.cn.lsgjf.cn http://www.morning.cybch.cn.gov.cn.cybch.cn http://www.morning.gbfzy.cn.gov.cn.gbfzy.cn http://www.morning.mpmtz.cn.gov.cn.mpmtz.cn http://www.morning.qxdrw.cn.gov.cn.qxdrw.cn http://www.morning.mbpfk.cn.gov.cn.mbpfk.cn http://www.morning.hhkzl.cn.gov.cn.hhkzl.cn http://www.morning.rlkgc.cn.gov.cn.rlkgc.cn http://www.morning.myfwb.cn.gov.cn.myfwb.cn http://www.morning.bxbnf.cn.gov.cn.bxbnf.cn http://www.morning.spghj.cn.gov.cn.spghj.cn