在菲做平台网站,定安免费建站公司,百度指数的主要用户是,大学做网站有哪些文章目录 一、信号的保存——信号的三个表——block表#xff0c;pending表#xff0c;handler表sigset_t信号集操作函数——用户层sigprocmask和sigpending——内核层 二、信号的捕捉重谈进程地址空间#xff08;第三次#xff09;用户态和内核态sigaction可重入函数volat… 文章目录 一、信号的保存——信号的三个表——block表pending表handler表sigset_t信号集操作函数——用户层sigprocmask和sigpending——内核层 二、信号的捕捉重谈进程地址空间第三次用户态和内核态sigaction可重入函数volatile 一、信号的保存——信号的三个表——block表pending表handler表
我们知道操作系统是进程的管理者只有操作系统才有资格向进程发信号具体点是给进程的PCB发信号。
更具体点就是将进程的task_struct中的signal整形的某一个比特位由0置1
那么该信号如何被保存下来呢 实际执行信号的处理动作称为信号递达(Delivery) 信号从产生到递达之间的状态,称为信号未决(Pending)。 进程可以选择阻塞 (Block )某个信号。 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作. 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。 也就是说block表记录的是对某一个信号是否阻塞如果对2号信号阻塞那么block表中2号下标的位图就由0置1。
pending表记录的是收到了哪一个信号且还未处理的信号就保存在pending表中。
handler表保存的是处理对应信号的方法handler表的本质就是一个函数指针数组。
函数指针类型是:
typedef void (*handler_t)(int);这些函数方法如果用户不提供就使用默认的如果用户提供就使用用户的。
handler表的定义如下
handler_t handler[31];需要注意的 一个信号如果被阻塞了只是意味着该信号将暂时保存在pending表中没有被递达直到该信号被解除阻塞才会将该信号进行递达处理。 注意阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
总结block表阻塞表pending表保存表handler表方法表。
sigset_t 上图的三张表都是内核的数据结构是操作系统管理的用户层无法直接访问只能由操作系统提供的结构进行修改。
从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。
因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。
阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
总结sigset_t是一个信号集也就是位图。
信号集操作函数——用户层
#include signal.h
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);这些函数都是对用户层的位图进行操作的。
函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。
函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。
注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。
初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。
这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。
sigprocmask和sigpending——内核层
sigprocmask 该函数的意思就是将set位图设置到内核的block表里面 如果how参数选择
1.SIG_BLOCK将原来的block表保存到oset中并将新的set位图的新的屏蔽信号添加到block表中也就是说如果原来的block表只有2号位置的比特位为1即只有2号信号被屏蔽且新的set位图中的1号比特位为1那么调用完该函数后新的内核block表中的1号和2号的比特位都为1也就是增加了1号信号屏蔽字。 mask mask|set 2.SIG_UNBLOCK与第一个的操作相反 mask mask~set 3.SIG_SETMASK直接将set位图覆盖到内核的block表即可简单粗暴。 mask set sigpending(sigset_t * set)
获取内核数据结构中的pending表并保存到set位图中。
以下代码就是对上面两个系统调用的应用
void PrintPending(sigset_t pending)
{for(int signo 31;signo1;signo--){if(sigismember(pending,signo)) //判断pending表中的比特位{cout 1;}else {cout 0;}}cout \n\n;
}
int main()
{sigset_t bset,oset;sigemptyset(bset); //设置一个位图清0sigemptyset(oset); //设置一个位图清0//添加信号屏蔽字sigaddset(bset,2); // for(int signo 1;signo31;signo)// {// sigaddset(bset,signo);// } //到这里其实没有修改内核中的block表只是创建一个位图而已sigprocmask(SIG_SETMASK,bset,oset); //到这里才是修改内核block表//mask set//打印pending表如果收到2号信号就不会被递达而是一直在pending表里存着。sigset_t pending;while(true){sigpending(pending);PrintPending(pending);sleep(1);}return 0;
}首先创建一个位图将位图的某些位置设置成1。 这时候并没有修改内核中的block表。 然后调用了sigprocmask系统调用后才真正地修改内核的block表。 再将pending表打印出来如果设置的某个信号被屏蔽后意味着在block表中的该位置的比特位为1一旦进程收到该信号就不会被递达 就会被pending所以pending表中的比特位就被设置成了1直到未来某个时候解除屏蔽后才递达该信号。 二、信号的捕捉
重谈进程地址空间第三次
在进程的地址空间中有1GB的内存是专门留给操作系统的
在启动电脑时是操作系统的数据和代码先被放到物理内存的较底部的位置先运行起来。 然后有关操作系统的进程也被操作系统跑起来。
无论是哪些进程只要是一个进程该进程的虚拟地址空间中的3~4GB这个空间区域一定是属于操作系统所有的
而对应的由于每个进程的1GB空间都属于操作系统所以任何进程看到的操作系统的数据和代码都是一样的
而当进程调用系统调用时这个过程就显得非常简单。
因为进程的虚拟地址空间中的1GB空间可以直接通过内核级别页表映射到物理空间中的固定位置。
所以进程想要调用系统调用直接去自己的进程地址空间中的内核空间中执行对应的代码即可
这也侧面验证了 内核级页表只有一份而用户级页表有多份的结论。 因为操作系统的代码在进程地址空间中的内核空间是固定的所以只需要一份页表直接映射到固定位置就能访问操作系统的代码和数据。 所以不考虑权限问题的话
从进程视角来看调用系统调用的方法就是直接在我自己的地址空间中进行执行的。从操作系统来看任何时刻都有进程执行只要进程想执行操作系统的代码就可以随时执行。 用户态和内核态
前面在讲到进程要调用系统调用时没有考虑权限。 但实际上要想执行操作系统的代码和数据是要有权限的。
而这个所谓的权限就是内核态。
内核态允许进程访问操作系统的代码和数据用户态只能访问用户自己的代码和数据
在CPU内部其中有两个寄存器一个寄存器叫CR3寄存器保留的是当前进程用户级页表的物理地址。
还有一个寄存器叫做ecs寄存器该寄存器的后两位比特位就是记录当前进程属于用户态还是属于内核态。
00表示用户态11表示内核态。
并且要想修改当前进程从用户态转变成内核态就需要调用系统调用int 8080就是系统调用的编号。 而在用户态到内核态之间的切换如下 上面的图比较繁琐这样非常好理解 并且在从用户态进入内核态时一定不仅只有调用系统调用才会由用户态进入内核态。
当操作系统要对进程进行调度时就要将进程的PCB加载到运行队列等待队列等待这些管理结构中然后将进程的上下文加载到CPU和操作系统中这个加载的过程一定是在内核态完成的在加载完成后操作系统转而就会执行进程自己的代码和数据而执行进程的代码和数据的过程一定是在用户态执行的
这就有了进程可以有无数次机会从用户态进入内核态再由内核态进入用户态的过程
所以这也验证了一个结论
信号不会被进程立即处理而是在合适的时间处理这个合适的时间其实就是在内核态中信号的检测阶段处理。
sigaction
sigaction函数与signal函数有一样的功能。 不同的是sigaction的功能更多一些。 sigaction也是一个结构体该结构体的名字与该函数名相同。 结构体中的主要两个成员是
void (*sa_handler)(int);
sigset_t sa_mask; 一个是捕捉信号是对应的处理方法一个是block表。
具体功能就是signum信号对应的自定义捕捉方法存入act函数指针指向的结构体的sa_handler方法中oact存的就是旧的捕捉方法。
重点不在这里重点在于从发送信号到保存信号捕捉信号的过程中block表pending表handler表是如何协同工作的 在我们向进程发送信号时假如发送二号信号此时进程收到信号后首先将pending表中的2号位置由0置1意味着先将2号信号保存起来进程会在合适的时间处理。当这个2号信号被递达也就是被处理时在调用handler方法中的2号位置对应的处理方法前将pending表中的2号位置就由1置空0且会将block表中的2号位置由0置1这就意味这当进程在处理2号信号时再发送2号信号过来时pending表中的2号位置一定是由0置1的因为一定是等上一个2号信号处理完成后再处理这个2号信号。 在将上面的2号信号处理完成后调用处理方法返回前会将block表中的2号位置再由1置成0此时就完成了整个信号的捕捉处理过程。 具体如下 在进程处理2号信号期间当我再次发送2号信号时就能看到pending表中的2号位置由0置1.
void PrintPending()
{sigset_t set;sigpending(set);for (int signo 1; signo 31; signo){if (sigismember(set, signo))cout 1;elsecout 0;}cout \n;
}void handler(int signo)
{cout catch a signal, signal number : signo endl;while (true){PrintPending();sleep(1);}
}int main()
{struct sigaction act, oact;memset(act, 0, sizeof(act));memset(oact, 0, sizeof(oact));// sigemptyset(act.sa_mask);// sigaddset(act.sa_mask, 1);// sigaddset(act.sa_mask, 3);// sigaddset(act.sa_mask, 4);act.sa_handler handler; // SIG_IGN SIG_DFLsigaction(2, act, oact);while (true){cout I am a process: getpid() endl;sleep(1);}return 0;
}可重入函数 假设main函数内部再调用insert函数进行链表的头插操作。
当执行完newnode-next head后本来要执行下一条代码时此时收到某个信号该信号有一个自定义处理函数该函数的内部又调用了insert函数此时再次进入了insert函数内部再次执行了newnode-next head情况如上图所示然后再执行head newnode完成头插工作。
在处理完该信号后会回到main函数调用insert函数执行完上一条代码的地方将要执行下一条代码执行后最终结果如上图。node2就会找不到了也就意味着内存泄露了。
如果一个函数在被重复进入的情况下出错了或者可能会出错这样的函数叫做不可重入函数。
否则叫做可重入函数
显然上面的insert函数就是不可重入函数。
volatile
volatile 作用保持内存的可见性告知编译器被该关键字修饰的变量不允许被优化对该变量的任何操作都必须在真实的内存中进行操作。
请看下面一段代码
int flag 0;void handler(int signo)
{cout catch a signal: signo endl;flag 1;
}int main()
{signal(2, handler);// 在优化条件下 flag变量可能被直接优化到CPU内的寄存器中while(!flag); // flag 假, !flag 真cout process quit normal endl;return 0;
}在进程接收到2号信号时调用handler函数将flag的值修改成1当进入循环时flag逻辑表达式为假退出循环这就是我们预期的结果。 但事实并非如此因为编译器对该变量进行了优化将flag变量存在了寄存器中flag 1这条语句修改的是内存中的flag对寄存器中的flag并未修改。 并且flag逻辑表达式在判断的时候是对寄存器的值进行判断的。 所以在寄存器中的flag一直为真就不会退出循环。 在flag变量之前加上一个volatile关键字后flag就不会被优化到寄存器中对flag变量的逻辑运算就会在内存执行。 文章转载自: http://www.morning.sfmqm.cn.gov.cn.sfmqm.cn http://www.morning.bwkzn.cn.gov.cn.bwkzn.cn http://www.morning.rqkzh.cn.gov.cn.rqkzh.cn http://www.morning.wdwfm.cn.gov.cn.wdwfm.cn http://www.morning.cpctr.cn.gov.cn.cpctr.cn http://www.morning.hgcz.cn.gov.cn.hgcz.cn http://www.morning.ytmx.cn.gov.cn.ytmx.cn http://www.morning.qwbht.cn.gov.cn.qwbht.cn http://www.morning.xbmwh.cn.gov.cn.xbmwh.cn http://www.morning.przc.cn.gov.cn.przc.cn http://www.morning.fbmjw.cn.gov.cn.fbmjw.cn http://www.morning.hgwsj.cn.gov.cn.hgwsj.cn http://www.morning.nlrp.cn.gov.cn.nlrp.cn http://www.morning.gwdnl.cn.gov.cn.gwdnl.cn http://www.morning.cfybl.cn.gov.cn.cfybl.cn http://www.morning.qrdkk.cn.gov.cn.qrdkk.cn http://www.morning.nyjgm.cn.gov.cn.nyjgm.cn http://www.morning.mhrzd.cn.gov.cn.mhrzd.cn http://www.morning.lsnhs.cn.gov.cn.lsnhs.cn http://www.morning.yydzk.cn.gov.cn.yydzk.cn http://www.morning.zlmbc.cn.gov.cn.zlmbc.cn http://www.morning.rghkg.cn.gov.cn.rghkg.cn http://www.morning.ftcrt.cn.gov.cn.ftcrt.cn http://www.morning.hhfwj.cn.gov.cn.hhfwj.cn http://www.morning.nnwmd.cn.gov.cn.nnwmd.cn http://www.morning.zrdhd.cn.gov.cn.zrdhd.cn http://www.morning.dxzcr.cn.gov.cn.dxzcr.cn http://www.morning.rqqlp.cn.gov.cn.rqqlp.cn http://www.morning.rwfp.cn.gov.cn.rwfp.cn http://www.morning.rngyq.cn.gov.cn.rngyq.cn http://www.morning.jzlfq.cn.gov.cn.jzlfq.cn http://www.morning.tmcmj.cn.gov.cn.tmcmj.cn http://www.morning.rcfwr.cn.gov.cn.rcfwr.cn http://www.morning.lwmzp.cn.gov.cn.lwmzp.cn http://www.morning.rwmqp.cn.gov.cn.rwmqp.cn http://www.morning.yrctp.cn.gov.cn.yrctp.cn http://www.morning.wxccm.cn.gov.cn.wxccm.cn http://www.morning.bysey.com.gov.cn.bysey.com http://www.morning.hlxxl.cn.gov.cn.hlxxl.cn http://www.morning.sbwr.cn.gov.cn.sbwr.cn http://www.morning.fpxsd.cn.gov.cn.fpxsd.cn http://www.morning.ldspj.cn.gov.cn.ldspj.cn http://www.morning.ppdr.cn.gov.cn.ppdr.cn http://www.morning.yhtnr.cn.gov.cn.yhtnr.cn http://www.morning.qbtkg.cn.gov.cn.qbtkg.cn http://www.morning.byjwl.cn.gov.cn.byjwl.cn http://www.morning.ljllt.cn.gov.cn.ljllt.cn http://www.morning.playmi.cn.gov.cn.playmi.cn http://www.morning.mlnzx.cn.gov.cn.mlnzx.cn http://www.morning.jfcbz.cn.gov.cn.jfcbz.cn http://www.morning.dmtld.cn.gov.cn.dmtld.cn http://www.morning.wcgcm.cn.gov.cn.wcgcm.cn http://www.morning.mdtfh.cn.gov.cn.mdtfh.cn http://www.morning.brscd.cn.gov.cn.brscd.cn http://www.morning.scrnt.cn.gov.cn.scrnt.cn http://www.morning.xxwhz.cn.gov.cn.xxwhz.cn http://www.morning.hksxq.cn.gov.cn.hksxq.cn http://www.morning.ygkb.cn.gov.cn.ygkb.cn http://www.morning.zrwlz.cn.gov.cn.zrwlz.cn http://www.morning.xrrbj.cn.gov.cn.xrrbj.cn http://www.morning.lgcqj.cn.gov.cn.lgcqj.cn http://www.morning.hqqpy.cn.gov.cn.hqqpy.cn http://www.morning.bswhr.cn.gov.cn.bswhr.cn http://www.morning.lhsdf.cn.gov.cn.lhsdf.cn http://www.morning.pccqr.cn.gov.cn.pccqr.cn http://www.morning.ryzgp.cn.gov.cn.ryzgp.cn http://www.morning.cfcpb.cn.gov.cn.cfcpb.cn http://www.morning.snjpj.cn.gov.cn.snjpj.cn http://www.morning.wspjn.cn.gov.cn.wspjn.cn http://www.morning.jwgmx.cn.gov.cn.jwgmx.cn http://www.morning.pplxd.cn.gov.cn.pplxd.cn http://www.morning.atoinfo.com.gov.cn.atoinfo.com http://www.morning.syssdz.cn.gov.cn.syssdz.cn http://www.morning.trwkz.cn.gov.cn.trwkz.cn http://www.morning.ghrhb.cn.gov.cn.ghrhb.cn http://www.morning.yrbqy.cn.gov.cn.yrbqy.cn http://www.morning.kdrjd.cn.gov.cn.kdrjd.cn http://www.morning.qpmwb.cn.gov.cn.qpmwb.cn http://www.morning.wphfl.cn.gov.cn.wphfl.cn http://www.morning.gsyns.cn.gov.cn.gsyns.cn